Merge pull request 'Add Orchard commitment tree functionality' (#65) from rav001 into dev040
Reviewed-on: #65
This commit is contained in:
commit
915cd68042
5 changed files with 178 additions and 2 deletions
|
@ -112,6 +112,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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1259,6 +1260,94 @@ pub extern "C" fn rust_wrapper_read_sapling_position(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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(
|
pub extern "C" fn rust_wrapper_decode_sapling_address(
|
||||||
sapling: *const u8,
|
sapling: *const u8,
|
||||||
sapling_len: usize,
|
sapling_len: usize,
|
||||||
|
@ -1303,4 +1392,3 @@ pub extern "C" fn rust_wrapper_decode_sapling_address(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
#}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -617,6 +617,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_.
|
||||||
|
|
23
test/Spec.hs
23
test/Spec.hs
|
@ -68,7 +68,9 @@ import ZcashHaskell.Types
|
||||||
, CoinType(..)
|
, CoinType(..)
|
||||||
, DecodedNote(..)
|
, DecodedNote(..)
|
||||||
, OrchardAction(..)
|
, OrchardAction(..)
|
||||||
|
, OrchardCommitmentTree(..)
|
||||||
, OrchardSpendingKey(..)
|
, OrchardSpendingKey(..)
|
||||||
|
, OrchardWitness(..)
|
||||||
, Phrase(..)
|
, Phrase(..)
|
||||||
, RawData(..)
|
, RawData(..)
|
||||||
, RawOutPoint(..)
|
, RawOutPoint(..)
|
||||||
|
@ -880,6 +882,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
|
||||||
|
|
Loading…
Reference in a new issue