Add Orchard commitment tree functionality #65

Merged
pitmutt merged 2 commits from rav001 into dev040 2024-04-17 16:00:08 +00:00
5 changed files with 178 additions and 2 deletions

View File

@ -112,6 +112,7 @@ use orchard::{
note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note_encryption::OrchardDomain,
primitives::redpallas::{VerificationKey, SpendAuth, Signature},
tree::MerkleHashOrchard,
value::ValueCommitment
};
@ -1259,6 +1260,94 @@ 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;
}
}
}
pub extern "C" fn rust_wrapper_decode_sapling_address(
sapling: *const u8,
sapling_len: usize,
@ -1303,4 +1392,3 @@ pub extern "C" fn rust_wrapper_decode_sapling_address(
}
}
}

View File

@ -239,3 +239,23 @@ 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
, rustWrapperOrchardNoteDecode
, rustWrapperOrchardNoteDecodeSK
, rustWrapperReadOrchardCommitmentTree
, rustWrapperReadOrchardPosition
, rustWrapperReadOrchardWitness
, rustWrapperUADecode
, rustWrapperUfvkDecode
)
import qualified Data.ByteString as BS
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.Encoding as E
import Data.Word
@ -184,3 +187,35 @@ decryptOrchardActionSK sk scope oa =
decodedAction =
withPureBorshVarBuffer $
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

@ -617,6 +617,16 @@ instance FromJSON OrchardAction where
a <- obj .: "spendAuthSig"
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
data DecodedNote = DecodedNote
{ a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_.

View File

@ -68,7 +68,9 @@ import ZcashHaskell.Types
, CoinType(..)
, DecodedNote(..)
, OrchardAction(..)
, OrchardCommitmentTree(..)
, OrchardSpendingKey(..)
, OrchardWitness(..)
, Phrase(..)
, RawData(..)
, RawOutPoint(..)
@ -880,6 +882,27 @@ main = do
getSaplingNotePosition <$>
(getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1)
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
let sr =
getSaplingFromUA