From 3203c7e8ffefcb788caf5801425af344a85922bc Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 18 Oct 2024 08:47:52 -0500 Subject: [PATCH 1/4] feat: implement Frontier for Sapling witnesses --- librustzcash-wrapper/src/lib.rs | 109 ++++++++++++++++++++++++++------ src/C/Zcash.chs | 11 +++- src/ZcashHaskell/Sapling.hs | 31 +++++---- src/ZcashHaskell/Types.hs | 9 +++ 4 files changed, 128 insertions(+), 32 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 10e8df0..a0e98b6 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1369,6 +1369,31 @@ pub extern "C" fn rust_wrapper_bech32_encode( marshall_to_haskell_var(&string, out, out_len, RW); } +#[no_mangle] +pub extern "C" fn rust_wrapper_read_sapling_frontier( + tree: *const u8, + tree_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 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); + }, + None => { + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![vec![0]]}; + marshall_to_haskell_var(&f0, out, out_len, RW); + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_read_sapling_commitment_tree( tree: *const u8, @@ -1687,21 +1712,31 @@ 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_anchor = match sap_iw { - Ok(s_iw) => { - Some(SaplingAnchor::from(s_iw.root())) - }, - Err(_e) => { + //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 - } - }; + } 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_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) => { @@ -1746,7 +1781,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } } - let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1763,13 +1797,32 @@ pub extern "C" fn rust_wrapper_create_transaction( let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let merkle_path = iw.path().unwrap(); if net { - let _mb = main_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap(); + let mb = main_builder.add_sapling_spend::(&sk, note, merkle_path); + 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_spend::(&sk, note, merkle_path).unwrap(); + let tb = test_builder.add_sapling_spend::(&sk, note, merkle_path); + match tb { + Ok(()) => { + continue; + }, + Err(_e) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } }, Err(_e) => { - continue; + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); } } } @@ -1786,9 +1839,27 @@ pub extern "C" fn rust_wrapper_create_transaction( let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let merkle_path = OrchardMerklePath::from(iw.path().unwrap()); if net { - let _mb = main_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap(); + let mb = main_builder.add_orchard_spend::(&sp_key, note, merkle_path); + match mb { + Ok(()) => { + continue; + }, + Err(_e) => { + let x = Hhex {bytes: vec![7]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } else { - let _tb = test_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap(); + let tb = test_builder.add_orchard_spend::(&sp_key, note, merkle_path); + match tb { + Ok(()) => { + continue; + }, + Err(_e) => { + let x = Hhex {bytes: vec![7]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index beb11e2..ab4bd54 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -204,9 +204,9 @@ import ZcashHaskell.Types #} {# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree - { toBorshVar* `BS.ByteString'& + { toBorshVar* `SaplingFrontier'& , toBorshVar* `BS.ByteString'& - , getVarBuffer `Buffer HexString'& + , getVarBuffer `Buffer SaplingFrontier'& } -> `()' #} @@ -232,6 +232,13 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_read_sapling_frontier as rustWrapperReadSaplingFrontier + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer SaplingFrontier'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_decode_sapling_address as rustWrapperDecodeSaplingAddress { toBorshVar* `BS.ByteString'& , getVarBuffer `Buffer (BS.ByteString)'& diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index f145c91..4b7c286 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -21,6 +21,7 @@ import C.Zcash ( rustWrapperDecodeSaplingAddress , rustWrapperIsShielded , rustWrapperReadSaplingCommitmentTree + , rustWrapperReadSaplingFrontier , rustWrapperReadSaplingPosition , rustWrapperReadSaplingWitness , rustWrapperSaplingCheck @@ -184,21 +185,29 @@ genSaplingInternalAddress sk = res = withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk) --- | Update a Sapling commitment tree -updateSaplingCommitmentTree :: - SaplingCommitmentTree -- ^ the base tree - -> HexString -- ^ the new note commitment - -> Maybe SaplingCommitmentTree -updateSaplingCommitmentTree tree cmu = - if BS.length (hexBytes updatedTree) > 1 - then Just $ SaplingCommitmentTree updatedTree +getSaplingFrontier :: SaplingCommitmentTree -> Maybe SaplingFrontier +getSaplingFrontier tree = + if sf_pos updatedTree > 1 + then Just updatedTree else Nothing where updatedTree = withPureBorshVarBuffer $ - rustWrapperReadSaplingCommitmentTree - (hexBytes $ sapTree tree) - (hexBytes cmu) + rustWrapperReadSaplingFrontier $ toBytes $ sapTree tree + +-- | Update a Sapling commitment tree +updateSaplingCommitmentTree :: + SaplingFrontier -- ^ the base tree + -> HexString -- ^ the new note commitment + -> Maybe SaplingFrontier +updateSaplingCommitmentTree tree cmu = + if sf_pos updatedTree > 1 + then Just updatedTree + else Nothing + where + updatedTree = + withPureBorshVarBuffer $ + rustWrapperReadSaplingCommitmentTree tree (hexBytes cmu) -- | Get the Sapling incremental witness from a commitment tree getSaplingWitness :: SaplingCommitmentTree -> Maybe SaplingWitness diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index ebf3dbf..8105d30 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -611,6 +611,15 @@ newtype SaplingCommitmentTree = SaplingCommitmentTree { sapTree :: HexString } deriving (Eq, Prelude.Show, Read) +data SaplingFrontier = SaplingFrontier + { sf_pos :: !Int64 + , sf_leaf :: !HexString + , sf_ommers :: ![BS.ByteString] + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingFrontier + -- | Type for a Sapling incremental witness newtype SaplingWitness = SaplingWitness { sapWit :: HexString -- 2.34.1 From 96cfe3a52a0fc2c6910ea193daa51dc839857f0a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 18 Oct 2024 09:43:02 -0500 Subject: [PATCH 2/4] feat: use `SaplingFrontier` for witnesses --- librustzcash-wrapper/src/lib.rs | 7 ++++--- src/C/Zcash.chs | 2 +- src/ZcashHaskell/Sapling.hs | 6 ++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index a0e98b6..3209737 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1445,9 +1445,10 @@ pub extern "C" fn rust_wrapper_read_sapling_witness( 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 ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); + 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 ct: CommitmentTree = CommitmentTree::from_frontier(&frontier); let inc_wit = IncrementalWitness::from_tree(ct); let mut out_bytes: Vec = Vec::new(); let result = write_incremental_witness(&inc_wit, &mut out_bytes); diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index ab4bd54..71c73e5 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -212,7 +212,7 @@ import ZcashHaskell.Types #} {# fun unsafe rust_wrapper_read_sapling_witness as rustWrapperReadSaplingWitness - { toBorshVar* `BS.ByteString'& + { toBorshVar* `SaplingFrontier'& , getVarBuffer `Buffer HexString'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 4b7c286..e15bf4f 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -210,15 +210,13 @@ updateSaplingCommitmentTree tree cmu = rustWrapperReadSaplingCommitmentTree tree (hexBytes cmu) -- | Get the Sapling incremental witness from a commitment tree -getSaplingWitness :: SaplingCommitmentTree -> Maybe SaplingWitness +getSaplingWitness :: SaplingFrontier -> Maybe SaplingWitness getSaplingWitness tree = if BS.length (hexBytes wit) > 1 then Just $ SaplingWitness wit else Nothing where - wit = - withPureBorshVarBuffer $ - rustWrapperReadSaplingWitness (hexBytes $ sapTree tree) + wit = withPureBorshVarBuffer $ rustWrapperReadSaplingWitness tree -- | Get the Sapling note position from a witness getSaplingNotePosition :: SaplingWitness -> Integer -- 2.34.1 From 5a9ed11c258170855fb17c9c2bdd8944b550f4b0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 18 Oct 2024 12:25:30 -0500 Subject: [PATCH 3/4] fix(rust): SaplingFrontier marshalling --- librustzcash-wrapper/src/lib.rs | 46 ++++++++++----------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 3209737..92e9319 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1403,39 +1403,21 @@ pub extern "C" fn rust_wrapper_read_sapling_commitment_tree( 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 = read_commitment_tree::>, SAPLING_DEPTH>(tree_reader); - match ct { - Ok(mut comm_tree) => { - 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 mut out_bytes: Vec = Vec::new(); - let result = write_commitment_tree(&comm_tree, &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); - } - } - } else { - let h0 = Hhex { bytes: vec![0]}; - marshall_to_haskell_var(&h0, out, out_len, RW); - } - }, - Err(_e) => { - let h0 = Hhex { bytes: vec![0]}; - marshall_to_haskell_var(&h0, out, out_len, RW); - } + 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 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()}; + marshall_to_haskell_var(&f, out, out_len, RW); + } else { + let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![vec![0]]}; + marshall_to_haskell_var(&f0, out, out_len, RW); } - } #[no_mangle] -- 2.34.1 From 4608577c417c1fbfde54bcd502673e413e1f95a5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 18 Oct 2024 14:39:05 -0500 Subject: [PATCH 4/4] docs: version bump --- CHANGELOG.md | 6 ++++++ zcash-haskell.cabal | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1feacf7..3bdb344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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.2.0] + +### Changed + +- Modified Sapling commitment trees to use Frontier + ## [0.7.1.1] ### Added diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index babe1f3..edad0f8 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.1.1 +version: 0.7.2.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain -- 2.34.1