Add Sapling output decrypting with spending key #55
6 changed files with 120 additions and 8 deletions
|
@ -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
|
||||
|
|
1
librustzcash-wrapper/Cargo.lock
generated
1
librustzcash-wrapper/Cargo.lock
generated
|
@ -1284,6 +1284,7 @@ dependencies = [
|
|||
"borsh 0.10.3",
|
||||
"f4jumble",
|
||||
"haskell-ffi",
|
||||
"incrementalmerkletree",
|
||||
"nonempty",
|
||||
"orchard",
|
||||
"proc-macro2",
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'&
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue