Add Orchard commitment tree functionality

This commit is contained in:
Rene Vergara 2024-04-17 09:21:47 -05:00
parent 914eb6341e
commit a56a4b1e15
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
5 changed files with 180 additions and 1 deletions

View file

@ -111,6 +111,7 @@ use orchard::{
note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note_encryption::OrchardDomain, note_encryption::OrchardDomain,
primitives::redpallas::{VerificationKey, SpendAuth, Signature}, primitives::redpallas::{VerificationKey, SpendAuth, Signature},
tree::MerkleHashOrchard,
value::ValueCommitment value::ValueCommitment
}; };
@ -1256,3 +1257,92 @@ pub extern "C" fn rust_wrapper_read_sapling_position(
} }
} }
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_read_orchard_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 ct = read_commitment_tree::<MerkleHashOrchard, Cursor<Vec<u8>>, 32>(tree_reader);
match ct {
Ok(mut comm_tree) => {
let node_in: Vec<u8> = 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 mut out_bytes: Vec<u8> = 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);
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_read_orchard_witness(
tree: *const u8,
tree_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 ct: CommitmentTree<MerkleHashOrchard, 32> = read_commitment_tree(tree_reader).unwrap();
let inc_wit = IncrementalWitness::from_tree(ct);
let mut out_bytes: Vec<u8> = Vec::new();
let result = write_incremental_witness(&inc_wit, &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);
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_read_orchard_position(
wit: *const u8,
wit_len: usize,
) -> u64 {
let wit_in: Vec<u8> = marshall_from_haskell_var(wit, wit_len, RW);
let wit_reader = Cursor::new(wit_in);
let iw: IncrementalWitness<MerkleHashOrchard, 32> = read_incremental_witness(wit_reader).unwrap();
let path = iw.path();
match path {
Some(p) => {
let pos = p.position();
return u64::from(pos);
},
None => {
return 0;
}
}
}

View file

@ -232,3 +232,24 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_witness as rustWrapperReadOrchardWitness
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun pure unsafe rust_wrapper_read_orchard_position as rustWrapperReadOrchardPosition
{ toBorshVar* `BS.ByteString'&
}
-> `Word64'
#}

View file

@ -23,12 +23,15 @@ import C.Zcash
, rustWrapperOrchardCheck , rustWrapperOrchardCheck
, rustWrapperOrchardNoteDecode , rustWrapperOrchardNoteDecode
, rustWrapperOrchardNoteDecodeSK , rustWrapperOrchardNoteDecodeSK
, rustWrapperReadOrchardCommitmentTree
, rustWrapperReadOrchardPosition
, rustWrapperReadOrchardWitness
, rustWrapperUADecode , rustWrapperUADecode
, rustWrapperUfvkDecode , rustWrapperUfvkDecode
) )
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Char8 as C
import Data.HexString (fromRawBytes, toBytes) import Data.HexString (HexString(..), fromRawBytes, toBytes)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Word import Data.Word
@ -184,3 +187,35 @@ decryptOrchardActionSK sk scope oa =
decodedAction = decodedAction =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External) rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External)
-- | Update a Orchard commitment tree
updateOrchardCommitmentTree ::
OrchardCommitmentTree -- ^ the base tree
-> HexString -- ^ the new note commitment
-> Maybe OrchardCommitmentTree
updateOrchardCommitmentTree tree cmx =
if BS.length (hexBytes updatedTree) > 1
then Just $ OrchardCommitmentTree updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadOrchardCommitmentTree
(hexBytes $ orchTree tree)
(hexBytes cmx)
-- | Get the Orchard incremental witness from a commitment tree
getOrchardWitness :: OrchardCommitmentTree -> Maybe OrchardWitness
getOrchardWitness tree =
if BS.length (hexBytes wit) > 1
then Just $ OrchardWitness wit
else Nothing
where
wit =
withPureBorshVarBuffer $
rustWrapperReadOrchardWitness (hexBytes $ orchTree tree)
-- | Get the Sapling note position from a witness
getOrchardNotePosition :: OrchardWitness -> Integer
getOrchardNotePosition =
fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit

View file

@ -611,6 +611,16 @@ instance FromJSON OrchardAction where
a <- obj .: "spendAuthSig" a <- obj .: "spendAuthSig"
pure $ OrchardAction n r c ephKey encText outText cval a pure $ OrchardAction n r c ephKey encText outText cval a
-- | Type for a Orchard note commitment tree
newtype OrchardCommitmentTree = OrchardCommitmentTree
{ orchTree :: HexString
} deriving (Eq, Prelude.Show, Read)
-- | Type for a Sapling incremental witness
newtype OrchardWitness = OrchardWitness
{ orchWit :: HexString
} deriving (Eq, Prelude.Show, Read)
-- | Type to represent a decoded note -- | Type to represent a decoded note
data DecodedNote = DecodedNote data DecodedNote = DecodedNote
{ a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_. { a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_.

View file

@ -66,7 +66,9 @@ import ZcashHaskell.Types
, CoinType(..) , CoinType(..)
, DecodedNote(..) , DecodedNote(..)
, OrchardAction(..) , OrchardAction(..)
, OrchardCommitmentTree(..)
, OrchardSpendingKey(..) , OrchardSpendingKey(..)
, OrchardWitness(..)
, Phrase(..) , Phrase(..)
, RawData(..) , RawData(..)
, RawOutPoint(..) , RawOutPoint(..)
@ -877,6 +879,27 @@ main = do
getSaplingNotePosition <$> getSaplingNotePosition <$>
(getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1) (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1)
p `shouldBe` Just 129405 p `shouldBe` Just 129405
describe "Orchard commitment trees" $ do
let tree =
OrchardCommitmentTree $
hexString
"01d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d01bb83047c72eb4f71813d00dee37082169546df2d7097bf7fd187ef6a93063b281f015e710ed46b53b48b12733652e150f9dcbc7e7b571cf64f294cf903864c78882f01cac32bc901f501f714a028f7ebe44c1dd8b42661be1c96730066a6fa6ede653600000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
let cmx =
hexString
"d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d"
it "Commitment tree is updated correctly" $ do
let t1 = updateOrchardCommitmentTree tree cmx
t1 `shouldNotBe` Nothing
it "Incremental witness is generated" $ do
let t1 = updateOrchardCommitmentTree tree cmx
case t1 of
Nothing -> assertFailure "Failed to append node to tree"
Just t -> getOrchardWitness t `shouldNotBe` Nothing
it "Position of note is obtained" $ do
let p =
getOrchardNotePosition <$>
(getOrchardWitness =<< updateOrchardCommitmentTree tree cmx)
p `shouldBe` Just 39432
describe "Extract Sapling Address - UA Valid" $ do describe "Extract Sapling Address - UA Valid" $ do
let sr = let sr =
getSaplingFromUA getSaplingFromUA