diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index d5533f5..ad70b02 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -6,7 +6,8 @@ use std::{ marker::PhantomData, io::{ Write, - Cursor + Cursor, + Error }, }; @@ -28,6 +29,10 @@ use incrementalmerkletree::frontier::CommitmentTree; use zip32; use zcash_primitives::{ + merkle_tree::{ + read_commitment_tree, + write_commitment_tree + }, zip32::{ Scope as SaplingScope, ChildIndex, @@ -57,6 +62,7 @@ use zcash_primitives::{ MerklePath, NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, PaymentAddress, + note::ExtractedNoteCommitment as SaplingNoteCommitment, keys::{ PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, ExpandedSpendingKey, @@ -187,6 +193,14 @@ pub struct Hhex { bytes: Vec } +impl ToHaskell for Hhex { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + + #[derive(BorshSerialize, BorshDeserialize)] pub struct Haction { 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); } + +#[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 = marshall_from_haskell_var(tree, tree_len, RW); + let tree_reader = Cursor::new(tree_in); + let mut ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); + + let node_in: Vec = 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 = 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); + } + } +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index ea2f9ea..f131d40 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -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)'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index ef287d6..8153034 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -19,6 +19,7 @@ module ZcashHaskell.Sapling where import C.Zcash ( rustWrapperIsShielded + , rustWrapperReadSaplingCommitmentTree , rustWrapperSaplingCheck , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingDecodeEsk @@ -30,7 +31,7 @@ import C.Zcash ) import Data.Aeson 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 Foreign.Rust.Marshall.Variable ( withPureBorshVarBuffer @@ -166,3 +167,17 @@ genSaplingInternalAddress sk = where res = 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) diff --git a/test/Spec.hs b/test/Spec.hs index 6db774b..95320b9 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -54,6 +54,7 @@ import ZcashHaskell.Sapling , isValidSaplingViewingKey , isValidShieldedAddress , matchSaplingAddress + , updateSaplingCommitmentTree ) import ZcashHaskell.Transparent import ZcashHaskell.Types @@ -844,6 +845,24 @@ main = do let tb = zt_tBundle t' print tb 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 prop_PhraseLength :: Property