Add Sapling nullifier calculation #58

Merged
pitmutt merged 4 commits from rav001 into dev040 2024-04-12 18:18:49 +00:00
4 changed files with 88 additions and 2 deletions
Showing only changes of commit 27b291c49a - Show all commits

View file

@ -6,7 +6,8 @@ use std::{
marker::PhantomData, marker::PhantomData,
io::{ io::{
Write, Write,
Cursor Cursor,
Error
}, },
}; };
@ -28,6 +29,10 @@ use incrementalmerkletree::frontier::CommitmentTree;
use zip32; use zip32;
use zcash_primitives::{ use zcash_primitives::{
merkle_tree::{
read_commitment_tree,
write_commitment_tree
},
zip32::{ zip32::{
Scope as SaplingScope, Scope as SaplingScope,
ChildIndex, ChildIndex,
@ -57,6 +62,7 @@ use zcash_primitives::{
MerklePath, MerklePath,
NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH,
PaymentAddress, PaymentAddress,
note::ExtractedNoteCommitment as SaplingNoteCommitment,
keys::{ keys::{
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
ExpandedSpendingKey, ExpandedSpendingKey,
@ -187,6 +193,14 @@ pub struct Hhex {
bytes: Vec<u8> bytes: Vec<u8>
} }
impl<RW> ToHaskell<RW> for Hhex {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
}
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
pub struct Haction { pub struct Haction {
nf: Hhex, nf: Hhex,
@ -1129,3 +1143,33 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver(
marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW);
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_read_commitment_tree(
tree: *const u8,
tree_len: usize,
node: *const u8,
node_len: usize,
out: *mut u8,
out_len: &mut usize
){
let tree_in: Vec<u8> = marshall_from_haskell_var(tree, tree_len, RW);
let tree_reader = Cursor::new(tree_in);
let mut ct: CommitmentTree<Node, SAPLING_DEPTH> = read_commitment_tree(tree_reader).unwrap();
let node_in: Vec<u8> = marshall_from_haskell_var(node, node_len, RW);
let n = Node::from_cmu(&SaplingNoteCommitment::from_bytes(&to_array(node_in)).unwrap());
ct.append(n);
let mut out_bytes: Vec<u8> = Vec::new();
let result = write_commitment_tree(&ct, &mut out_bytes );
match result {
Ok(()) => {
let h = Hhex { bytes: out_bytes};
marshall_to_haskell_var(&h, out, out_len, RW);
},
Err(_e) => {
let h0 = Hhex { bytes: vec![0]};
marshall_to_haskell_var(&h0, out, out_len, RW);
}
}
}

View file

@ -200,3 +200,11 @@ import ZcashHaskell.Types
} }
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_commitment_tree as rustWrapperReadSaplingCommitmentTree
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}

View file

@ -19,6 +19,7 @@ module ZcashHaskell.Sapling where
import C.Zcash import C.Zcash
( rustWrapperIsShielded ( rustWrapperIsShielded
, rustWrapperReadSaplingCommitmentTree
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingDecodeEsk , rustWrapperSaplingDecodeEsk
@ -30,7 +31,7 @@ import C.Zcash
) )
import Data.Aeson import Data.Aeson
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.HexString (HexString(..), fromText, toBytes, toText) import Data.HexString (HexString(..), fromText, hexString, toBytes, toText)
import Data.Word import Data.Word
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
( withPureBorshVarBuffer ( withPureBorshVarBuffer
@ -166,3 +167,17 @@ genSaplingInternalAddress sk =
where where
res = res =
withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk) withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk)
-- | Update a Sapling commitment tree
updateSaplingCommitmentTree ::
HexString -- ^ the base tree
-> HexString -- ^ the new note commitment
-> Maybe HexString
updateSaplingCommitmentTree tree cmu =
if BS.length updatedTree > 1
then Just $ HexString updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadSaplingCommitmentTree (hexBytes tree) (hexBytes cmu)

View file

@ -54,6 +54,7 @@ import ZcashHaskell.Sapling
, isValidSaplingViewingKey , isValidSaplingViewingKey
, isValidShieldedAddress , isValidShieldedAddress
, matchSaplingAddress , matchSaplingAddress
, updateSaplingCommitmentTree
) )
import ZcashHaskell.Transparent import ZcashHaskell.Transparent
import ZcashHaskell.Types import ZcashHaskell.Types
@ -844,6 +845,24 @@ main = do
let tb = zt_tBundle t' let tb = zt_tBundle t'
print tb print tb
show tb `shouldNotBe` "" show tb `shouldNotBe` ""
describe "Sapling commitment trees" $ do
let tree =
hexString
"01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
let cmu1 =
hexString
"45e47c5df6f5c5e48aa3526e977b2d1b57eda57214e36f06128008cb17b0125f"
let cmu2 =
hexString
"426ef44b3b22e0eeda7e4d2b62bac63966572b224e50f97ee56c9490cde4910d"
let tree2 =
hexString
"01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
it "Commitment tree is updated correctly" $ do
let t1 = updateSaplingCommitmentTree tree cmu1
case t1 of
Nothing -> assertFailure "Tree 1 failed"
Just t2 -> updateSaplingCommitmentTree t2 cmu2 `shouldBe` Just tree2
-- | Properties -- | Properties
prop_PhraseLength :: Property prop_PhraseLength :: Property