Merge pull request 'Add Sapling output decrypting with spending key' (#55) from rav001 into dev040

Reviewed-on: #55
This commit is contained in:
pitmutt 2024-04-08 18:01:25 +00:00 committed by Vergara Technologies LLC
commit 817c52dacf
Signed by: Vergara Technologies LLC
GPG key ID: 99DB473BB4715618
6 changed files with 120 additions and 8 deletions

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.5.3.0]
### Added
- Function to decode Sapling outputs with a spending key
### Fixed
- Parsing of `TxIn` for FFI

View file

@ -1284,6 +1284,7 @@ dependencies = [
"borsh 0.10.3",
"f4jumble",
"haskell-ffi",
"incrementalmerkletree",
"nonempty",
"orchard",
"proc-macro2",

View file

@ -18,6 +18,7 @@ zcash_client_backend = "0.10.0"
zip32 = "0.1.0"
proc-macro2 = "1.0.66"
nonempty = "0.7.0"
incrementalmerkletree = "0.5.0"
[features]

View file

@ -23,6 +23,8 @@ use haskell_ffi::{
FromHaskell, HaskellSize, ToHaskell
};
use incrementalmerkletree::frontier::CommitmentTree;
use zip32;
use zcash_primitives::{
@ -51,6 +53,9 @@ use zcash_primitives::{
}
},
sapling::{
Node,
MerklePath,
NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH,
PaymentAddress,
keys::{
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
@ -63,6 +68,7 @@ use zcash_primitives::{
consensus::{
BranchId::Nu5,
MainNetwork,
TestNetwork,
BlockHeight
}
};
@ -602,7 +608,7 @@ pub extern "C" fn rust_wrapper_svk_decode(
let input: Vec<u8> = marshall_from_haskell_var(input, input_len, RW);
let svk = ExtendedFullViewingKey::read(&*input);
match svk {
Ok(k) => {
Ok(_k) => {
true
}
Err(e) => {
@ -698,6 +704,64 @@ pub extern "C" fn rust_wrapper_ufvk_decode(
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
key: *const u8,
key_len: usize,
note: *const u8,
note_len: usize,
external: bool,
net: bool,
out: *mut u8,
out_len: &mut usize
){
let sk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
let note_input: Vec<u8> = marshall_from_haskell_var(note,note_len,RW);
let mut note_reader = Cursor::new(note_input);
let esk = ExtendedSpendingKey::from_bytes(&sk);
let main_domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(419200));
let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(419200));
let scope = if external {
SaplingScope::External
} else {
SaplingScope::Internal
};
match esk {
Ok(k) => {
let action = OutputDescription::read(&mut note_reader);
match action {
Ok(action2) => {
let dfvk = k.to_diversifiable_full_viewing_key();
let ivk = dfvk.to_ivk(scope);
let nk = dfvk.to_nk(scope);
let pivk = SaplingPreparedIncomingViewingKey::new(&ivk);
let result = if net { zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2)}
else {zcash_note_encryption::try_note_decryption(&test_domain, &pivk, &action2)};
match result {
Some((n, r, m)) => {
//let nullifier = n.nf(&nk, MerklePath<Node::from_cmu(&n.cmu()), SAPLING_DEPTH>.position());
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec() };
marshall_to_haskell_var(&hn, out, out_len, RW);
},
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e1) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
key: *const u8,
@ -722,11 +786,11 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3);
match result {
Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec() };
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec()};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
@ -738,7 +802,7 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
}
}
Err(_e) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
@ -772,17 +836,17 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result {
Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec() };
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec()};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}

View file

@ -101,6 +101,16 @@ import ZcashHaskell.Types
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_esk_decrypt as rustWrapperSaplingDecodeEsk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, `Bool'
, `Bool'
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer UnifiedFullViewingKey'&

View file

@ -21,6 +21,7 @@ import C.Zcash
( rustWrapperIsShielded
, rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingDecodeEsk
, rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress
, rustWrapperSaplingSpendingkey
@ -29,7 +30,7 @@ import C.Zcash
)
import Data.Aeson
import qualified Data.ByteString as BS
import Data.HexString (HexString(..), toBytes)
import Data.HexString (HexString(..), fromText, toBytes, toText)
import Data.Word
import Foreign.Rust.Marshall.Variable
( withPureBorshVarBuffer
@ -43,9 +44,11 @@ import ZcashHaskell.Types
, RawTxResponse(..)
, SaplingReceiver(..)
, SaplingSpendingKey(..)
, Scope(..)
, Seed(..)
, ShieldedOutput(..)
, ToBytes(..)
, ZcashNet(..)
, decodeHexText
, getValue
)
@ -58,6 +61,15 @@ isValidShieldedAddress = rustWrapperIsShielded
getShieldedOutputs :: HexString -> [BS.ByteString]
getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t
serializeShieldedOutput :: ShieldedOutput -> BS.ByteString
serializeShieldedOutput so =
hexBytes . fromText $
toText (s_cv so) <>
toText (s_cmu so) <>
toText (s_ephKey so) <>
toText (s_encCipherText so) <>
toText (s_outCipherText so) <> toText (s_proof so)
-- | Check if given bytestring is a valid Sapling viewing key
isValidSaplingViewingKey :: BS.ByteString -> Bool
isValidSaplingViewingKey k =
@ -98,6 +110,26 @@ instance FromJSON RawTxResponse where
a <- o' .: "actions"
pure $ RawTxResponse i h sSpend (getShieldedOutputs h) a ht c b
-- | Attempt to decode the given raw tx with the given Sapling spending key
decodeSaplingOutputEsk ::
SaplingSpendingKey
-> ShieldedOutput
-> ZcashNet
-> Scope
-> Maybe DecodedNote
decodeSaplingOutputEsk key out znet scope =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperSaplingDecodeEsk
(getBytes key)
(serializeShieldedOutput out)
(znet == MainNet)
(scope == External)
-- | Attempts to obtain a sapling SpendingKey using a HDSeed
genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey
genSaplingSpendingKey seed c i = do