From 004e2c85cd2dbb5f59ba3ee6b06937596bc49c7a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 27 Oct 2024 06:42:04 -0500 Subject: [PATCH 01/17] feat: creation of Orchard commitment nodes --- CHANGELOG.md | 11 ++ librustzcash-wrapper/src/lib.rs | 138 ++++++++++++++++++++---- src/C/Zcash.chs | 30 ++++++ src/ZcashHaskell/Orchard.hs | 36 ++++++- src/ZcashHaskell/Types.hs | 4 +- test/Spec.hs | 181 +++++++++++++++++--------------- zcash-haskell.cabal | 2 +- 7 files changed, 292 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bdb344..9eed57f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.3.0] + +### Added + +- Function to create an Orchard hash from a note commitment +- Function to hash Orchard commitments + +### Changed + +- Modified frontiers to use `HexString` for ommers + ## [0.7.2.0] ### Changed diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 92e9319..e7b5d04 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -30,6 +30,8 @@ use secp256k1::SecretKey; use jubjub::Fr; use incrementalmerkletree::{ + Hashable, + Level, Position, frontier::{ CommitmentTree, @@ -92,7 +94,8 @@ use zcash_primitives::{ write_commitment_tree, read_incremental_witness, write_incremental_witness, - read_frontier_v1 + read_frontier_v1, + read_nonempty_frontier_v1 }, legacy::{ Script, @@ -697,7 +700,7 @@ impl ToHaskell for Hsvk { pub struct Hfrontier { position: u64, leaf: Hhex, - ommers: Vec> + ommers: Vec } impl ToHaskell for Hfrontier { @@ -1384,11 +1387,11 @@ pub extern "C" fn rust_wrapper_read_sapling_frontier( match frontier.value() { Some(f1) => { let (pos, leaf, omm) = f1.clone().into_parts(); - let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| x.to_bytes().to_vec()).collect()}; + let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); }, None => { - let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![vec![0]]}; + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } @@ -1405,17 +1408,17 @@ pub extern "C" fn rust_wrapper_read_sapling_commitment_tree( ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = Node::from_bytes(to_array(tree_in.leaf.bytes)).unwrap(); - let mut comm_tree = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| Node::from_bytes(to_array(x.clone())).unwrap() ).collect()).unwrap(); + let mut comm_tree = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| Node::from_bytes(to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(node_in)); if sap_note_comm.is_some().into() { let n = Node::from_cmu(&sap_note_comm.unwrap()); comm_tree.append(n); let (pos, leaf, omm) = comm_tree.into_parts(); - let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| x.to_bytes().to_vec()).collect()}; + let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); } else { - let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![vec![0]]}; + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } @@ -1429,7 +1432,7 @@ pub extern "C" fn rust_wrapper_read_sapling_witness( ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = Node::from_bytes(to_array(tree_in.leaf.bytes)).unwrap(); - let frontier: Frontier = Frontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| Node::from_bytes(to_array(x.clone())).unwrap() ).collect()).unwrap(); + let frontier: Frontier = Frontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| Node::from_bytes(to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let ct: CommitmentTree = CommitmentTree::from_frontier(&frontier); let inc_wit = IncrementalWitness::from_tree(ct); let mut out_bytes: Vec = Vec::new(); @@ -1501,23 +1504,114 @@ pub extern "C" fn rust_wrapper_read_orchard_frontier( ){ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); let tree_reader = Cursor::new(tree_in); - let comm_tree: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); - //let comm_tree: Frontier = read_frontier_v1(tree_reader).unwrap(); - let frontier: Frontier = comm_tree.to_frontier(); - match frontier.value() { - Some(f1) => { - let (pos, leaf, omm) = f1.clone().into_parts(); - let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| x.to_bytes().to_vec()).collect()}; - marshall_to_haskell_var(&f, out, out_len, RW); + let comm_tree = read_commitment_tree(tree_reader); + //let comm_tree = read_frontier_v1(tree_reader); + //let frontier: Frontier = comm_tree.to_frontier(); + match comm_tree { + Ok::, _>(f1) => { + let frontier = f1.to_frontier(); + match frontier.value() { + Some(f2) => { + let (pos, leaf, omm) = f2.clone().into_parts(); + let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; + marshall_to_haskell_var(&f, out, out_len, RW); + }, + None => { + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; + marshall_to_haskell_var(&f0, out, out_len, RW); + } + } }, - None => { - let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![vec![0]]}; + Err(_e) => { + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } } +#[no_mangle] +pub extern "C" fn rust_wrapper_read_orchard_tree_anchor( + tree: *const u8, + tree_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); + let leaf = MerkleHashOrchard::from_bytes(&to_array(tree_in.leaf.bytes)).unwrap(); + let comm_tree: NonEmptyFrontier = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); + let root = comm_tree.root(None); + let h = Hhex { bytes: root.to_bytes().to_vec() }; + marshall_to_haskell_var(&h, out, out_len, RW); +} +#[no_mangle] +pub extern "C" fn rust_wrapper_read_orchard_witness_anchor( + wit: *const u8, + wit_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let root = iw.root(); + let h = Hhex { bytes: root.to_bytes().to_vec() }; + marshall_to_haskell_var(&h, out, out_len, RW); +} + +#[no_mangle] +pub extern "C" fn rust_wrapper_read_orchard_node( + cmx: *const u8, + cmx_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let node_in: Vec = marshall_from_haskell_var(cmx, cmx_len, RW); + let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(node_in)); + if orchard_note_comm.is_some().into() { + let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); + let h = Hhex { bytes: n.to_bytes().to_vec()}; + marshall_to_haskell_var(&h, out, out_len, RW); + } else { + let h0 = Hhex { bytes: vec![0] }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } +} + +#[no_mangle] +pub extern "C" fn rust_wrapper_combine_orchard_nodes( + level: u8, + left: *const u8, + left_len: usize, + right: *const u8, + right_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let left_in: Vec = marshall_from_haskell_var(left, left_len, RW); + let right_in: Vec = marshall_from_haskell_var(right, right_len, RW); + let left_node = MerkleHashOrchard::from_bytes(&to_array(left_in)); + if left_node.is_some().into() { + if right_in.len() > 1 { + let right_node = MerkleHashOrchard::from_bytes(&to_array(right_in)); + if right_node.is_some().into() { + let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &right_node.unwrap()); + let h = Hhex { bytes: n.to_bytes().to_vec() }; + marshall_to_haskell_var(&h, out, out_len, RW); + } else { + let h0 = Hhex { bytes: vec![0] }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } else { + let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &MerkleHashOrchard::empty_leaf()); + let h = Hhex { bytes: n.to_bytes().to_vec() }; + marshall_to_haskell_var(&h, out, out_len, RW); + } + } else { + let h0 = Hhex { bytes: vec![0] }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } +} #[no_mangle] @@ -1534,17 +1628,17 @@ pub extern "C" fn rust_wrapper_read_orchard_commitment_tree( //let mut comm_tree: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); //let mut comm_tree: Frontier = read_frontier_v1(tree_reader).unwrap(); let leaf = MerkleHashOrchard::from_bytes(&to_array(tree_in.leaf.bytes)).unwrap(); - let mut comm_tree: NonEmptyFrontier = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.clone())).unwrap() ).collect()).unwrap(); + let mut comm_tree: NonEmptyFrontier = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(node_in)); if orchard_note_comm.is_some().into() { let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); comm_tree.append(n); let (pos, leaf, omm) = comm_tree.into_parts(); - let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| x.to_bytes().to_vec()).collect()}; + let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); } else { - let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![vec![0]]}; + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } @@ -1558,7 +1652,7 @@ pub extern "C" fn rust_wrapper_read_orchard_witness( ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = MerkleHashOrchard::from_bytes(&to_array(tree_in.leaf.bytes)).unwrap(); - let frontier: Frontier = Frontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.clone())).unwrap() ).collect()).unwrap(); + let frontier: Frontier = Frontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let ct: CommitmentTree = CommitmentTree::from_frontier(&frontier); let inc_wit = IncrementalWitness::from_tree(ct); let mut out_bytes: Vec = Vec::new(); diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 71c73e5..da2f86b 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -246,6 +246,36 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_read_orchard_node as rustWrapperReadOrchardNode + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_combine_orchard_nodes as rustWrapperCombineOrchardNodes + { `Int8' + , toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_read_orchard_tree_anchor as rustWrapperReadOrchardTreeAnchor + { toBorshVar* `OrchardFrontier'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_read_orchard_witness_anchor as rustWrapperReadOrchardWitnessAnchor + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree { toBorshVar* `OrchardFrontier'& , toBorshVar* `BS.ByteString'& diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 1bb7167..79043ea 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -18,15 +18,19 @@ module ZcashHaskell.Orchard where import C.Zcash - ( rustWrapperGenOrchardReceiver + ( rustWrapperCombineOrchardNodes + , rustWrapperGenOrchardReceiver , rustWrapperGenOrchardSpendKey , rustWrapperOrchardCheck , rustWrapperOrchardNoteDecode , rustWrapperOrchardNoteDecodeSK , rustWrapperReadOrchardCommitmentTree , rustWrapperReadOrchardFrontier + , rustWrapperReadOrchardNode , rustWrapperReadOrchardPosition + , rustWrapperReadOrchardTreeAnchor , rustWrapperReadOrchardWitness + , rustWrapperReadOrchardWitnessAnchor , rustWrapperUADecode , rustWrapperUfvkDecode , rustWrapperUpdateOrchardWitness @@ -205,6 +209,15 @@ getOrchardFrontier tree = withPureBorshVarBuffer $ rustWrapperReadOrchardFrontier $ toBytes $ orchTree tree +getOrchardTreeAnchor :: OrchardFrontier -> HexString +getOrchardTreeAnchor tree = + withPureBorshVarBuffer $ rustWrapperReadOrchardTreeAnchor tree + +getOrchardWitnessAnchor :: OrchardWitness -> HexString +getOrchardWitnessAnchor wit = + withPureBorshVarBuffer $ + rustWrapperReadOrchardWitnessAnchor $ toBytes $ orchWit wit + -- | Update a Orchard commitment tree updateOrchardCommitmentTree :: OrchardFrontier -- ^ the base tree @@ -244,6 +257,27 @@ updateOrchardWitness wit cmus = (map toBytes cmus) else wit +getOrchardNodeValue :: BS.ByteString -> Maybe HexString +getOrchardNodeValue cmx = + if BS.length (hexBytes n) > 1 + then Just n + else Nothing + where + n = withPureBorshVarBuffer $ rustWrapperReadOrchardNode cmx + +combineOrchardNodes :: Integer -> HexString -> HexString -> Maybe HexString +combineOrchardNodes level n1 n2 = + if BS.length (hexBytes r) > 1 + then Just r + else Nothing + where + r = + withPureBorshVarBuffer $ + rustWrapperCombineOrchardNodes + (fromIntegral level) + (toBytes n1) + (toBytes n2) + -- | Parse a potential Zcash address parseAddress :: BS.ByteString -> Maybe ValidAddress parseAddress t = diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 8105d30..324216f 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -614,7 +614,7 @@ newtype SaplingCommitmentTree = SaplingCommitmentTree data SaplingFrontier = SaplingFrontier { sf_pos :: !Int64 , sf_leaf :: !HexString - , sf_ommers :: ![BS.ByteString] + , sf_ommers :: ![HexString] } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -730,7 +730,7 @@ newtype OrchardCommitmentTree = OrchardCommitmentTree data OrchardFrontier = OrchardFrontier { of_pos :: !Int64 , of_leaf :: !HexString - , of_ommers :: ![BS.ByteString] + , of_ommers :: ![HexString] } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) diff --git a/test/Spec.hs b/test/Spec.hs index ac744cb..0547e16 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -55,6 +55,7 @@ import ZcashHaskell.Sapling , genSaplingInternalAddress , genSaplingPaymentAddress , genSaplingSpendingKey + , getSaplingFrontier , getSaplingNotePosition , getSaplingWitness , getShieldedOutputs @@ -73,6 +74,7 @@ import ZcashHaskell.Types , OrchardAction(..) , OrchardBundle(..) , OrchardCommitmentTree(..) + , OrchardFrontier(..) , OrchardSpendingKey(..) , OrchardWitness(..) , Phrase(..) @@ -88,6 +90,7 @@ import ZcashHaskell.Types , SaplingAddress(..) , SaplingBundle(..) , SaplingCommitmentTree(..) + , SaplingFrontier(..) , SaplingReceiver(..) , SaplingSpendingKey(..) , SaplingWitness(..) @@ -895,33 +898,35 @@ main = do Just t' -> do let tb = zt_tBundle t' show tb `shouldNotBe` "" - describe "Sapling commitment trees" $ do - let tree = - SaplingCommitmentTree $ - 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 - t1 `shouldNotBe` Nothing - it "Incremental witness is generated" $ do - let t1 = updateSaplingCommitmentTree tree cmu1 - case t1 of - Nothing -> assertFailure "Failed to append node to tree" - Just t -> getSaplingWitness t `shouldNotBe` Nothing - it "Position of note is obtained" $ do - let p = - getSaplingNotePosition <$> - (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1) - p `shouldBe` Just 129405 + {- + -describe "Sapling commitment trees" $ do + - let tree = + - SaplingCommitmentTree $ + - 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 + - t1 `shouldNotBe` Nothing + - it "Incremental witness is generated" $ do + - let t1 = updateSaplingCommitmentTree tree cmu1 + - case t1 of + - Nothing -> assertFailure "Failed to append node to tree" + - Just t -> getSaplingWitness t `shouldNotBe` Nothing + - it "Position of note is obtained" $ do + - let p = + - getSaplingNotePosition <$> + - (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1) + - p `shouldBe` Just 129405 + -} {- describe "Orchard commitment trees" $ do let tree = OrchardCommitmentTree $ @@ -1061,18 +1066,22 @@ main = do (hexString "97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e8892") it "Sap output 1" $ do - let pos = - getSaplingNotePosition <$> - (getSaplingWitness =<< - updateSaplingCommitmentTree - tree - (fromText - "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")) - case pos of - Nothing -> assertFailure "couldn't get note position" - Just p -> do - let dn = decodeSaplingOutputEsk sk so1 TestNet External p - dn `shouldBe` Nothing + case getSaplingFrontier tree of + Nothing -> assertFailure "failed to read comm tree" + Just tree' -> do + let pos = + sf_pos <$> + updateSaplingCommitmentTree + tree' + (fromText + "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858") + case pos of + Nothing -> assertFailure "couldn't get note position" + Just p -> do + let dn = + decodeSaplingOutputEsk sk so1 TestNet External $ + fromIntegral p + dn `shouldBe` Nothing it "Sap output 2" $ do case readZebraTransaction txHex2 of Nothing -> assertFailure "Failed to read Tx" @@ -1082,24 +1091,27 @@ main = do Nothing -> assertFailure "Failed to get sapling bundle" Just sB -> do let sOuts = sbOutputs sB - let pos = - getSaplingNotePosition <$> - (getSaplingWitness =<< - updateSaplingCommitmentTree - tree - (fromText - "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) - case pos of - Nothing -> assertFailure "couldn't get note position" - Just p -> do - let dn = - decodeSaplingOutputEsk - sk - (head . tail $ sOuts) - TestNet - External - p - dn `shouldBe` Nothing + case getSaplingFrontier tree of + Nothing -> assertFailure "Failed to read tree" + Just tree' -> do + let pos = + getSaplingNotePosition <$> + (getSaplingWitness =<< + updateSaplingCommitmentTree + tree' + (fromText + "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) + case pos of + Nothing -> assertFailure "couldn't get note position" + Just p -> do + let dn = + decodeSaplingOutputEsk + sk + (head . tail $ sOuts) + TestNet + External + p + dn `shouldBe` Nothing it "Decode Sapling Output from Zingo" $ do case readZebraTransaction txHex of Nothing -> assertFailure "Failed to read Tx" @@ -1112,24 +1124,26 @@ main = do Nothing -> assertFailure "Failed to get sapling bundle" Just sB -> do let sOuts = sbOutputs sB - let pos = - getSaplingNotePosition <$> - (getSaplingWitness =<< - updateSaplingCommitmentTree - tree - (fromText - "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) - case pos of - Nothing -> assertFailure "couldn't get note position" - Just p -> do - let dn = - decodeSaplingOutputEsk - sK' - (head . tail $ sOuts) - MainNet - External - p - dn `shouldNotBe` Nothing + case getSaplingFrontier tree of + Nothing -> assertFailure "failed to read comm tree" + Just tree' -> do + let pos = + sf_pos <$> + updateSaplingCommitmentTree + tree' + (fromText + "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843") + case pos of + Nothing -> assertFailure "couldn't get note position" + Just p -> do + let dn = + decodeSaplingOutputEsk + sK' + (head . tail $ sOuts) + MainNet + External + (fromIntegral p) + dn `shouldNotBe` Nothing describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" it "Try to generate valid ExchangeAddress from Transparent Address" $ do @@ -1176,15 +1190,14 @@ main = do Just t1 -> case updateOrchardCommitmentTree t1 cmx1 of Nothing -> assertFailure "Failed to update frontier with cmx" - Just t2 -> - case updateOrchardCommitmentTree t2 cmx2 of - Nothing -> assertFailure "Failed to update frontier with cmx" - Just t3 -> - case updateOrchardCommitmentTree t3 cmx3 of - Nothing -> - assertFailure "Failed to update frontier with cmx" - Just t4 -> - updateOrchardCommitmentTree t4 cmx4 `shouldBe` finalTree + Just t2 -> do + case getOrchardWitness t2 of + Nothing -> assertFailure "Failed to get witness" + Just wit -> do + let uWit = updateOrchardWitness wit [cmx2, cmx3, cmx4] + Just (getOrchardWitnessAnchor uWit) `shouldBe` + getOrchardTreeAnchor <$> + finalTree describe "Witness updates" $ do it "Sapling" $ do let wit = diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index edad0f8..36ce2ae 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.7.2.0 +version: 0.7.3.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain -- 2.34.1 From 2e9e6d8831cda2507d5fd893aea2b040ac11a813 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 28 Oct 2024 11:59:07 -0500 Subject: [PATCH 02/17] feat: allow combining empty Orchard leaves --- librustzcash-wrapper/src/lib.rs | 40 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index e7b5d04..df054c6 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1590,26 +1590,32 @@ pub extern "C" fn rust_wrapper_combine_orchard_nodes( ){ let left_in: Vec = marshall_from_haskell_var(left, left_len, RW); let right_in: Vec = marshall_from_haskell_var(right, right_len, RW); - let left_node = MerkleHashOrchard::from_bytes(&to_array(left_in)); - if left_node.is_some().into() { - if right_in.len() > 1 { - let right_node = MerkleHashOrchard::from_bytes(&to_array(right_in)); - if right_node.is_some().into() { - let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &right_node.unwrap()); - let h = Hhex { bytes: n.to_bytes().to_vec() }; - marshall_to_haskell_var(&h, out, out_len, RW); - } else { - let h0 = Hhex { bytes: vec![0] }; - marshall_to_haskell_var(&h0, out, out_len, RW); - } - } else { - let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &MerkleHashOrchard::empty_leaf()); + if left_in.len() > 1 { + let n = MerkleHashOrchard::combine(Level::new(level), &MerkleHashOrchard::empty_leaf(), &MerkleHashOrchard::empty_leaf()); let h = Hhex { bytes: n.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); - } } else { - let h0 = Hhex { bytes: vec![0] }; - marshall_to_haskell_var(&h0, out, out_len, RW); + let left_node = MerkleHashOrchard::from_bytes(&to_array(left_in)); + if left_node.is_some().into() { + if right_in.len() > 1 { + let right_node = MerkleHashOrchard::from_bytes(&to_array(right_in)); + if right_node.is_some().into() { + let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &right_node.unwrap()); + let h = Hhex { bytes: n.to_bytes().to_vec() }; + marshall_to_haskell_var(&h, out, out_len, RW); + } else { + let h0 = Hhex { bytes: vec![0] }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } else { + let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &MerkleHashOrchard::empty_leaf()); + let h = Hhex { bytes: n.to_bytes().to_vec() }; + marshall_to_haskell_var(&h, out, out_len, RW); + } + } else { + let h0 = Hhex { bytes: vec![0] }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } } } -- 2.34.1 From b6d490d05300a9db9cdf9929baa9b984bee9f3f6 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 28 Oct 2024 12:25:34 -0500 Subject: [PATCH 03/17] fix(rust): orchard hash length check --- librustzcash-wrapper/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index df054c6..1673061 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1590,7 +1590,7 @@ pub extern "C" fn rust_wrapper_combine_orchard_nodes( ){ let left_in: Vec = marshall_from_haskell_var(left, left_len, RW); let right_in: Vec = marshall_from_haskell_var(right, right_len, RW); - if left_in.len() > 1 { + if left_in.len() == 1 { let n = MerkleHashOrchard::combine(Level::new(level), &MerkleHashOrchard::empty_leaf(), &MerkleHashOrchard::empty_leaf()); let h = Hhex { bytes: n.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); -- 2.34.1 From 1492f3b6ad44f8aba2daf3e153dc792c6bab3fbf Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 09:46:24 -0500 Subject: [PATCH 04/17] docs: add debugging to create_transaction --- librustzcash-wrapper/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 1673061..73b52cd 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1844,6 +1844,7 @@ pub extern "C" fn rust_wrapper_create_transaction( let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); for t_in in trans_input { if t_in.sk.len() > 1 { + println!("t inp: {:?}", t_in); let k = SecretKey::from_slice(&t_in.sk).unwrap(); if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { @@ -1866,6 +1867,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } for s_in in sap_input { if s_in.sk.len() > 1 { + println!("s inp: {:?}", s_in); let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); match sp_key { Ok(sk) => { @@ -1912,6 +1914,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } for o_in in orch_input { if o_in.sk.len() > 1 { + println!("o inp: {:?}", o_in); let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap(); let rho = Rho::from_bytes(&to_array(o_in.note.rho)).unwrap(); @@ -1952,6 +1955,7 @@ pub extern "C" fn rust_wrapper_create_transaction( 1 => { let recipient = TransparentAddress::PublicKeyHash(to_array(output.to)); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + println!("t out: {:?} {:?}", val, output.chg); if net { let _mb = main_builder.add_transparent_output(&recipient, val); } else { @@ -1961,6 +1965,7 @@ pub extern "C" fn rust_wrapper_create_transaction( 2 => { let recipient = TransparentAddress::ScriptHash(to_array(output.to)); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + println!("t out: {:?} {:?}", val, output.chg); if net { let _mb = main_builder.add_transparent_output(&recipient, val); } else { @@ -1972,6 +1977,7 @@ pub extern "C" fn rust_wrapper_create_transaction( let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap(); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); let memo = MemoBytes::from_bytes(&output.memo).unwrap(); + println!("s out: {:?} {:?}", val, output.chg); if net { let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); } else { @@ -1987,6 +1993,7 @@ pub extern "C" fn rust_wrapper_create_transaction( }; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; + println!("o out: {:?} {:?}", val, output.chg); let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); -- 2.34.1 From 42da2d3a0fb876e070892fa9fc16a91a7106991f Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 10:12:41 -0500 Subject: [PATCH 05/17] docs: add debug for fee --- librustzcash-wrapper/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 73b52cd..6abf905 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -2007,6 +2007,12 @@ pub extern "C" fn rust_wrapper_create_transaction( } } if build { + let fee_result = if net { + main_builder.get_fee(&FeeRule::standard()) + } else { + test_builder.get_fee(&FeeRule::standard()) + }; + println!("fee: {:?}", fee_result); let (spend_params_in, output_params_in) = load_sapling_parameters(); //let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); let spend_params_reader = Cursor::new(spend_params_in); -- 2.34.1 From 0ad6b9ebab1ab43e2d275883ab3742ce1e40f9a4 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 10:31:25 -0500 Subject: [PATCH 06/17] docs: add error debug output --- librustzcash-wrapper/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6abf905..d15036e 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -2039,6 +2039,7 @@ pub extern "C" fn rust_wrapper_create_transaction( marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { + println!("change req: {:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, -- 2.34.1 From 1e350c289c5b9332df35599a43671103d7e6f65b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 11:06:25 -0500 Subject: [PATCH 07/17] docs: add debug for sapling outputs --- librustzcash-wrapper/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index d15036e..a67eba0 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1914,12 +1914,12 @@ pub extern "C" fn rust_wrapper_create_transaction( } for o_in in orch_input { if o_in.sk.len() > 1 { - println!("o inp: {:?}", o_in); let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap(); let rho = Rho::from_bytes(&to_array(o_in.note.rho)).unwrap(); let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap(); let val = NoteValue::from_raw(o_in.note.note); + println!("o inp: {:?}", val); let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap(); let wit_reader = Cursor::new(o_in.iw); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); -- 2.34.1 From a21642baeeeef51c2cbe31d64ea282ea61e26c77 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 11:19:03 -0500 Subject: [PATCH 08/17] docs: add more Rust debug output --- librustzcash-wrapper/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index a67eba0..c243aa2 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1981,7 +1981,8 @@ pub extern "C" fn rust_wrapper_create_transaction( if net { let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); } else { - let _tb = test_builder.add_sapling_output::(ovk, recipient, val, memo); + let tb = test_builder.add_sapling_output::(ovk, recipient, val, memo); + println!("add sap: {:?}", tb); } }, 4 => { -- 2.34.1 From 5139f9dd7bacae07aa013f7610e3ed37cb5999ab Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 12:15:04 -0500 Subject: [PATCH 09/17] fix(rust): correct tx builder startup --- librustzcash-wrapper/src/lib.rs | 46 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index c243aa2..ade568c 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1795,43 +1795,41 @@ pub extern "C" fn rust_wrapper_create_transaction( build: bool, out: *mut u8, out_len: &mut usize){ - //let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); - //let sap_wit_reader = Cursor::new(sap_wit_in); - //let sap_iw = read_commitment_tree::>, SAPLING_DEPTH>(sap_wit_reader); let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); let sap_anchor = if sap_input.is_empty() { - None + let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); + let sap_wit_reader = Cursor::new(sap_wit_in); + let sap_iw = read_commitment_tree::>, SAPLING_DEPTH>(sap_wit_reader); + match sap_iw { + Ok(s_iw) => { + Some(SaplingAnchor::from(s_iw.root())) + }, + Err(_e) => { + None + } + } } else { let si = &sap_input[0]; let swit_reader = Cursor::new(&si.iw); let iw: IncrementalWitness = read_incremental_witness(swit_reader).unwrap(); Some(SaplingAnchor::from(iw.root())) }; - //let sap_anchor = match sap_iw { - //Ok(s_iw) => { - //Some(SaplingAnchor::from(s_iw.root())) - //}, - //Err(_e) => { - //None - //} - //}; //println!("{:?}", sap_anchor); - //let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); - //let orch_wit_reader = Cursor::new(orch_wit_in); - //let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); - //let orch_anchor = match orch_iw { - //Ok(o_iw) => { - //Some(OrchardAnchor::from(o_iw.root())) - //}, - //Err(_e) => { - //None - //} - //}; let orch_anchor = if orch_input.is_empty() { - None + let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); + let orch_wit_reader = Cursor::new(orch_wit_in); + let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); + match orch_iw { + Ok(o_iw) => { + Some(OrchardAnchor::from(o_iw.root())) + }, + Err(_e) => { + None + } + } } else { let oi = &orch_input[0]; let wit_reader = Cursor::new(&oi.iw); -- 2.34.1 From 1694898117d3d0b211f9d3de224ca206bc30b166 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 14:14:39 -0500 Subject: [PATCH 10/17] fix: correct transaction creation --- src/ZcashHaskell/Utils.hs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index e7e1aae..e060e18 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -137,22 +137,22 @@ createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh bui processResult $! txResult where processResult :: HexString -> Either TxError HexString - processResult input = - if BS.length (hexBytes input) > 1 - then Right input - else case head (BS.unpack $ hexBytes input) of - 0 -> Left InsufficientFunds - 1 -> Left ChangeRequired - 2 -> Left Fee - 3 -> Left Balance - 4 -> Left TransparentBuild - 5 -> Left SaplingBuild - 6 -> Left OrchardBuild - 7 -> Left OrchardSpend - 8 -> Left OrchardRecipient - 9 -> Left SaplingBuilderNotAvailable - 10 -> Left OrchardBuilderNotAvailable - _ -> Left ZHError + processResult input + | BS.length (hexBytes input) > 1 = Right input + | otherwise = + case head (BS.unpack $ hexBytes input) of + 0 -> Left InsufficientFunds + 1 -> Left ChangeRequired + 2 -> Left Fee + 3 -> Left Balance + 4 -> Left TransparentBuild + 5 -> Left SaplingBuild + 6 -> Left OrchardBuild + 7 -> Left OrchardSpend + 8 -> Left OrchardRecipient + 9 -> Left SaplingBuilderNotAvailable + 10 -> Left OrchardBuilderNotAvailable + _ -> Left ZHError txResult = withPureBorshVarBuffer $ rustWrapperCreateTx -- 2.34.1 From 24210b1e0be8ba7319a6ec2fbbe645ae03f9143b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 29 Oct 2024 14:55:43 -0500 Subject: [PATCH 11/17] fix: optimize create transaction --- src/ZcashHaskell/Utils.hs | 52 ++++++++++++--------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index e060e18..bc3566b 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -132,40 +132,20 @@ createTransaction :: -> ZcashNet -- ^ the network to be used -> Int -- ^ target block height -> Bool -- ^ True to build, False to estimate fee - -> Either TxError HexString + -> HexString createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = - processResult $! txResult - where - processResult :: HexString -> Either TxError HexString - processResult input - | BS.length (hexBytes input) > 1 = Right input - | otherwise = - case head (BS.unpack $ hexBytes input) of - 0 -> Left InsufficientFunds - 1 -> Left ChangeRequired - 2 -> Left Fee - 3 -> Left Balance - 4 -> Left TransparentBuild - 5 -> Left SaplingBuild - 6 -> Left OrchardBuild - 7 -> Left OrchardSpend - 8 -> Left OrchardRecipient - 9 -> Left SaplingBuilderNotAvailable - 10 -> Left OrchardBuilderNotAvailable - _ -> Left ZHError - txResult = - withPureBorshVarBuffer $ - rustWrapperCreateTx - (case sapAnchor of - Nothing -> "0" - Just sA -> toBytes $ sapTree sA) - (case orchAnchor of - Nothing -> "0" - Just oA -> toBytes $ orchTree oA) - tSpend - sSpend - oSpend - outgoing - (znet == MainNet) - (fromIntegral bh) - build + withPureBorshVarBuffer $ + rustWrapperCreateTx + (case sapAnchor of + Nothing -> "0" + Just sA -> toBytes $ sapTree sA) + (case orchAnchor of + Nothing -> "0" + Just oA -> toBytes $ orchTree oA) + tSpend + sSpend + oSpend + outgoing + (znet == MainNet) + (fromIntegral bh) + build -- 2.34.1 From fa2b34e26f32b93b2adf81c1465bc6b9eba416b8 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 30 Oct 2024 09:09:11 -0500 Subject: [PATCH 12/17] debuggin FFI --- librustzcash-wrapper/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index ade568c..a09f3e6 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -728,6 +728,7 @@ pub extern "C" fn rust_wrapper_f4jumble( out: *mut u8, out_len: &mut usize) { let input: Vec = marshall_from_haskell_var(input, input_len, RW); + println!("testy mc testface"); let result = f4jumble::f4jumble(&input).unwrap(); marshall_to_haskell_var(&result, out, out_len, RW); } -- 2.34.1 From f4ecfe9d11c67b8517ccabc7b6bb18b55c99dab4 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 30 Oct 2024 10:23:23 -0500 Subject: [PATCH 13/17] fix: optimize FFI for large results --- src/ZcashHaskell/Utils.hs | 50 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index bc3566b..62fb89c 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -132,20 +132,36 @@ createTransaction :: -> ZcashNet -- ^ the network to be used -> Int -- ^ target block height -> Bool -- ^ True to build, False to estimate fee - -> HexString -createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = - withPureBorshVarBuffer $ - rustWrapperCreateTx - (case sapAnchor of - Nothing -> "0" - Just sA -> toBytes $ sapTree sA) - (case orchAnchor of - Nothing -> "0" - Just oA -> toBytes $ orchTree oA) - tSpend - sSpend - oSpend - outgoing - (znet == MainNet) - (fromIntegral bh) - build + -> IO (Either TxError HexString) +createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do + txResult <- + withBorshBufferOfInitSize 5120 $ + rustWrapperCreateTx + (case sapAnchor of + Nothing -> "0" + Just sA -> toBytes $ sapTree sA) + (case orchAnchor of + Nothing -> "0" + Just oA -> toBytes $ orchTree oA) + tSpend + sSpend + oSpend + outgoing + (znet == MainNet) + (fromIntegral bh) + build + if BS.length (hexBytes txResult) > 1 + then pure $ Right txResult + else case head (BS.unpack $ hexBytes txResult) of + 0 -> pure $ Left InsufficientFunds + 1 -> pure $ Left ChangeRequired + 2 -> pure $ Left Fee + 3 -> pure $ Left Balance + 4 -> pure $ Left TransparentBuild + 5 -> pure $ Left SaplingBuild + 6 -> pure $ Left OrchardBuild + 7 -> pure $ Left OrchardSpend + 8 -> pure $ Left OrchardRecipient + 9 -> pure $ Left SaplingBuilderNotAvailable + 10 -> pure $ Left OrchardBuilderNotAvailable + _ -> pure $ Left ZHError -- 2.34.1 From a8d4a96b8ac09cd112c260ac76234dc4801c5d18 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 30 Oct 2024 10:59:20 -0500 Subject: [PATCH 14/17] fix: increase buffer size for createTransaction --- src/ZcashHaskell/Utils.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 62fb89c..4b783ac 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -135,7 +135,7 @@ createTransaction :: -> IO (Either TxError HexString) createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do txResult <- - withBorshBufferOfInitSize 5120 $ + withBorshBufferOfInitSize 10240 $ rustWrapperCreateTx (case sapAnchor of Nothing -> "0" -- 2.34.1 From b3ec3aecbd0d5555538ed8e431b430df40bb4855 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 30 Oct 2024 11:15:34 -0500 Subject: [PATCH 15/17] fix: adjust FFI buffer --- src/ZcashHaskell/Utils.hs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 4b783ac..6956e3f 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -135,21 +135,22 @@ createTransaction :: -> IO (Either TxError HexString) createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do txResult <- - withBorshBufferOfInitSize 10240 $ - rustWrapperCreateTx - (case sapAnchor of - Nothing -> "0" - Just sA -> toBytes $ sapTree sA) - (case orchAnchor of - Nothing -> "0" - Just oA -> toBytes $ orchTree oA) - tSpend - sSpend - oSpend - outgoing - (znet == MainNet) - (fromIntegral bh) - build + do print "calling FFI" + withBorshBufferOfInitSize 51200 $ + rustWrapperCreateTx + (case sapAnchor of + Nothing -> "0" + Just sA -> toBytes $ sapTree sA) + (case orchAnchor of + Nothing -> "0" + Just oA -> toBytes $ orchTree oA) + tSpend + sSpend + oSpend + outgoing + (znet == MainNet) + (fromIntegral bh) + build if BS.length (hexBytes txResult) > 1 then pure $ Right txResult else case head (BS.unpack $ hexBytes txResult) of -- 2.34.1 From c793e0c4ff9547be5b1a7663c6d21277df8876e7 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 30 Oct 2024 11:42:36 -0500 Subject: [PATCH 16/17] fix(rust): remove debug messages --- librustzcash-wrapper/src/lib.rs | 110 ++++++++++++++++++++++++-------- src/ZcashHaskell/Utils.hs | 31 +++++---- 2 files changed, 97 insertions(+), 44 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index a09f3e6..a469cb8 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -20,9 +20,9 @@ use borsh::{BorshDeserialize, BorshSerialize}; use haskell_ffi::{ error::Result, - from_haskell::{marshall_from_haskell_var, marshall_from_haskell_fixed}, - to_haskell::{marshall_to_haskell_var, marshall_to_haskell_fixed}, - FromHaskell, HaskellSize, ToHaskell + from_haskell::marshall_from_haskell_var, + to_haskell::marshall_to_haskell_var, + FromHaskell, ToHaskell }; use secp256k1::SecretKey; @@ -42,8 +42,7 @@ use incrementalmerkletree::{ }; use zip32::{ - Scope as SaplingScope, - ChildIndex + Scope as SaplingScope }; @@ -728,7 +727,6 @@ pub extern "C" fn rust_wrapper_f4jumble( out: *mut u8, out_len: &mut usize) { let input: Vec = marshall_from_haskell_var(input, input_len, RW); - println!("testy mc testface"); let result = f4jumble::f4jumble(&input).unwrap(); marshall_to_haskell_var(&result, out, out_len, RW); } @@ -1843,7 +1841,7 @@ pub extern "C" fn rust_wrapper_create_transaction( let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); for t_in in trans_input { if t_in.sk.len() > 1 { - println!("t inp: {:?}", t_in); + //println!("t inp: {:?}", t_in); let k = SecretKey::from_slice(&t_in.sk).unwrap(); if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { @@ -1866,7 +1864,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } for s_in in sap_input { if s_in.sk.len() > 1 { - println!("s inp: {:?}", s_in); + //println!("s inp: {:?}", s_in); let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); match sp_key { Ok(sk) => { @@ -1918,7 +1916,7 @@ pub extern "C" fn rust_wrapper_create_transaction( let rho = Rho::from_bytes(&to_array(o_in.note.rho)).unwrap(); let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap(); let val = NoteValue::from_raw(o_in.note.note); - println!("o inp: {:?}", val); + //println!("o inp: {:?}", val); let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap(); let wit_reader = Cursor::new(o_in.iw); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); @@ -1954,21 +1952,49 @@ pub extern "C" fn rust_wrapper_create_transaction( 1 => { let recipient = TransparentAddress::PublicKeyHash(to_array(output.to)); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); - println!("t out: {:?} {:?}", val, output.chg); + //println!("t out: {:?} {:?}", val, output.chg); if net { - let _mb = main_builder.add_transparent_output(&recipient, val); + let mb = main_builder.add_transparent_output(&recipient, val); + match mb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } else { - let _tb = test_builder.add_transparent_output(&recipient, val); + let tb = test_builder.add_transparent_output(&recipient, val); + match tb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } }, 2 => { let recipient = TransparentAddress::ScriptHash(to_array(output.to)); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); - println!("t out: {:?} {:?}", val, output.chg); + //println!("t out: {:?} {:?}", val, output.chg); if net { - let _mb = main_builder.add_transparent_output(&recipient, val); + let mb = main_builder.add_transparent_output(&recipient, val); + match mb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } else { - let _tb = test_builder.add_transparent_output(&recipient, val); + let tb = test_builder.add_transparent_output(&recipient, val); + match tb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } }, 3 => { @@ -1976,12 +2002,26 @@ pub extern "C" fn rust_wrapper_create_transaction( let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap(); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); let memo = MemoBytes::from_bytes(&output.memo).unwrap(); - println!("s out: {:?} {:?}", val, output.chg); + //println!("s out: {:?} {:?}", val, output.chg); if net { - let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); + let mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); + match mb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } else { let tb = test_builder.add_sapling_output::(ovk, recipient, val, memo); - println!("add sap: {:?}", tb); + match tb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } + //println!("add sap: {:?}", tb); } }, 4 => { @@ -1993,12 +2033,26 @@ pub extern "C" fn rust_wrapper_create_transaction( }; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; - println!("o out: {:?} {:?}", val, output.chg); + //println!("o out: {:?} {:?}", val, output.chg); let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { - let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); + let mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); + match mb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![6]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } else { - let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); + let tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); + match tb { + Ok(()) => { continue; }, + Err(_e) => { + let x = Hhex {bytes: vec![6]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } }, _ => { @@ -2007,12 +2061,12 @@ pub extern "C" fn rust_wrapper_create_transaction( } } if build { - let fee_result = if net { - main_builder.get_fee(&FeeRule::standard()) - } else { - test_builder.get_fee(&FeeRule::standard()) - }; - println!("fee: {:?}", fee_result); + //let fee_result = if net { + //main_builder.get_fee(&FeeRule::standard()) + //} else { + //test_builder.get_fee(&FeeRule::standard()) + //}; + //println!("fee: {:?}", fee_result); let (spend_params_in, output_params_in) = load_sapling_parameters(); //let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); let spend_params_reader = Cursor::new(spend_params_in); @@ -2039,7 +2093,7 @@ pub extern "C" fn rust_wrapper_create_transaction( marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("change req: {:?}", y1); + //println!("change req: {:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 6956e3f..ee0a0d9 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -135,22 +135,21 @@ createTransaction :: -> IO (Either TxError HexString) createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do txResult <- - do print "calling FFI" - withBorshBufferOfInitSize 51200 $ - rustWrapperCreateTx - (case sapAnchor of - Nothing -> "0" - Just sA -> toBytes $ sapTree sA) - (case orchAnchor of - Nothing -> "0" - Just oA -> toBytes $ orchTree oA) - tSpend - sSpend - oSpend - outgoing - (znet == MainNet) - (fromIntegral bh) - build + withBorshBufferOfInitSize 51200 $ + rustWrapperCreateTx + (case sapAnchor of + Nothing -> "0" + Just sA -> toBytes $ sapTree sA) + (case orchAnchor of + Nothing -> "0" + Just oA -> toBytes $ orchTree oA) + tSpend + sSpend + oSpend + outgoing + (znet == MainNet) + (fromIntegral bh) + build if BS.length (hexBytes txResult) > 1 then pure $ Right txResult else case head (BS.unpack $ hexBytes txResult) of -- 2.34.1 From 33085a46bff80d63a2fb81a0fa2db705765c9ee0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 30 Oct 2024 11:44:56 -0500 Subject: [PATCH 17/17] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eed57f..3f3a7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Modified frontiers to use `HexString` for ommers +- Optimized `createTransaction` ## [0.7.2.0] -- 2.34.1