Implement Sapling tx decoding
This commit is contained in:
parent
4d2540dce1
commit
1d8e3729a8
6 changed files with 98 additions and 40 deletions
|
@ -19,13 +19,17 @@ use haskell_ffi::{
|
|||
use zcash_primitives::{
|
||||
zip32::Scope as SaplingScope,
|
||||
transaction::components::sapling::{
|
||||
read_zkproof,
|
||||
GrothProofBytes,
|
||||
OutputDescription,
|
||||
CompactOutputDescription
|
||||
},
|
||||
sapling::{
|
||||
value::ValueCommitment as SaplingValueCommitment,
|
||||
keys::FullViewingKey as SaplingViewingKey,
|
||||
keys::{
|
||||
FullViewingKey as SaplingViewingKey,
|
||||
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey
|
||||
},
|
||||
note_encryption::SaplingDomain,
|
||||
PaymentAddress,
|
||||
note::ExtractedNoteCommitment as SaplingExtractedNoteCommitment
|
||||
|
@ -311,35 +315,49 @@ pub extern "C" fn rust_wrapper_ufvk_decode(
|
|||
}
|
||||
}
|
||||
|
||||
//#[no_mangle]
|
||||
//pub extern "C" fn rust_wrapper_sapling_note_decrypt(
|
||||
//key: *const u8,
|
||||
//key_len: usize,
|
||||
//note: *const u8,
|
||||
//note_len: usize,
|
||||
//out: *mut u8,
|
||||
//out_len: &mut usize
|
||||
//){
|
||||
//let evk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
|
||||
//let note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
|
||||
//let svk = ExtendedFullViewingKey::read(&*evk);
|
||||
//match svk {
|
||||
//Ok(k) => {
|
||||
//let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000));
|
||||
//let action: CompactOutputDescription = CompactOutputDescription {
|
||||
//ephemeral_key: EphemeralKeyBytes(to_array(note_input.eph_key)),
|
||||
//cmu: SaplingExtractedNoteCommitment::from_bytes(&to_array(note_input.cmu)).unwrap(),
|
||||
//enc_ciphertext: to_array(note_input.enc_txt)
|
||||
//};
|
||||
//let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External);
|
||||
//let result = zcash_note_encryption::try_note_decryption(&domain, &ivk, &action);
|
||||
//}
|
||||
//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(
|
||||
key: *const u8,
|
||||
key_len: usize,
|
||||
note: *const u8,
|
||||
note_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: &mut usize
|
||||
){
|
||||
let evk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
|
||||
let note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
|
||||
let svk = ExtendedFullViewingKey::read(&*evk);
|
||||
match svk {
|
||||
Ok(k) => {
|
||||
let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000));
|
||||
let mut action_bytes = vec![0];
|
||||
action_bytes.extend(¬e_input.cv);
|
||||
action_bytes.extend(¬e_input.cmu);
|
||||
action_bytes.extend(¬e_input.eph_key);
|
||||
action_bytes.extend(¬e_input.enc_txt);
|
||||
action_bytes.extend(¬e_input.out_txt);
|
||||
action_bytes.extend(¬e_input.proof);
|
||||
let action2 = OutputDescription::read(&mut action_bytes.as_slice()).unwrap();
|
||||
let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External);
|
||||
let pivk = SaplingPreparedIncomingViewingKey::new(&fvk);
|
||||
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action2);
|
||||
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() };
|
||||
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(_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_orchard_note_decrypt(
|
||||
|
|
|
@ -72,6 +72,14 @@ import ZcashHaskell.Types
|
|||
-> `Bool'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_sapling_note_decrypt as rustWrapperSaplingNoteDecode
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, toBorshVar* `ShieldedOutput'&
|
||||
, getVarBuffer `Buffer DecodedNote'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, getVarBuffer `Buffer UnifiedFullViewingKey'&
|
||||
|
@ -82,7 +90,7 @@ import ZcashHaskell.Types
|
|||
{# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, toBorshVar* `OrchardAction'&
|
||||
, getVarBuffer `Buffer OrchardDecodedAction'&
|
||||
, getVarBuffer `Buffer DecodedNote'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
#}
|
||||
|
|
|
@ -35,7 +35,7 @@ decodeUfvk str =
|
|||
|
||||
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
|
||||
decryptOrchardAction ::
|
||||
OrchardAction -> UnifiedFullViewingKey -> Maybe OrchardDecodedAction
|
||||
OrchardAction -> UnifiedFullViewingKey -> Maybe DecodedNote
|
||||
decryptOrchardAction encAction key =
|
||||
case a_value decodedAction of
|
||||
0 -> Nothing
|
||||
|
|
|
@ -3,9 +3,12 @@ module ZcashHaskell.Sapling where
|
|||
import C.Zcash
|
||||
( rustWrapperIsShielded
|
||||
, rustWrapperSaplingCheck
|
||||
, rustWrapperSaplingNoteDecode
|
||||
, rustWrapperSaplingVkDecode
|
||||
)
|
||||
import qualified Data.ByteString as BS
|
||||
import Foreign.Rust.Marshall.Variable (withPureBorshVarBuffer)
|
||||
import ZcashHaskell.Types (DecodedNote(..), ShieldedOutput)
|
||||
|
||||
-- | Check if given bytesting is a valid encoded shielded address
|
||||
isValidShieldedAddress :: BS.ByteString -> Bool
|
||||
|
@ -18,3 +21,13 @@ isValidSaplingViewingKey = rustWrapperSaplingVkDecode
|
|||
-- | Check if the given bytestring for the Sapling viewing key matches the second bytestring for the address
|
||||
matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool
|
||||
matchSaplingAddress = rustWrapperSaplingCheck
|
||||
|
||||
-- | Attempt to decode the given Sapling raw output with the given Sapling viewing key
|
||||
decodeSaplingOutput :: BS.ByteString -> ShieldedOutput -> Maybe DecodedNote
|
||||
decodeSaplingOutput key out =
|
||||
case a_value decodedAction of
|
||||
0 -> Nothing
|
||||
_ -> Just decodedAction
|
||||
where
|
||||
decodedAction =
|
||||
withPureBorshVarBuffer $ rustWrapperSaplingNoteDecode key out
|
||||
|
|
|
@ -153,15 +153,15 @@ instance FromJSON OrchardAction where
|
|||
(decodeHexText cval)
|
||||
(decodeHexText a)
|
||||
|
||||
-- | Type to represent a decoded Orchard Action
|
||||
data OrchardDecodedAction = OrchardDecodedAction
|
||||
-- | Type to represent a decoded note
|
||||
data DecodedNote = DecodedNote
|
||||
{ a_value :: Int64 -- ^ The amount of the transaction in _zatoshis_.
|
||||
, a_recipient :: BS.ByteString -- ^ The recipient Orchard receiver.
|
||||
, a_memo :: BS.ByteString -- ^ The decoded shielded memo field.
|
||||
} deriving stock (Eq, Prelude.Show, GHC.Generic)
|
||||
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
||||
deriving anyclass (Data.Structured.Show)
|
||||
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardDecodedAction
|
||||
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote
|
||||
|
||||
-- * Helpers
|
||||
-- | Helper function to turn a hex-encoded string to bytestring
|
||||
|
|
27
test/Spec.hs
27
test/Spec.hs
|
@ -11,17 +11,18 @@ import Data.Word
|
|||
import Test.Hspec
|
||||
import ZcashHaskell.Orchard
|
||||
import ZcashHaskell.Sapling
|
||||
( isValidSaplingViewingKey
|
||||
( decodeSaplingOutput
|
||||
, isValidSaplingViewingKey
|
||||
, isValidShieldedAddress
|
||||
, matchSaplingAddress
|
||||
)
|
||||
import ZcashHaskell.Types
|
||||
( BlockResponse(..)
|
||||
, DecodedNote(..)
|
||||
, OrchardAction(..)
|
||||
, OrchardDecodedAction(..)
|
||||
, RawData(..)
|
||||
, RawTxResponse(..)
|
||||
, ShieldedOutput(s_cmu)
|
||||
, ShieldedOutput(ShieldedOutput, s_cmu)
|
||||
, UnifiedFullViewingKey(..)
|
||||
, decodeHexText
|
||||
)
|
||||
|
@ -313,7 +314,25 @@ main = do
|
|||
describe "Decode Sapling tx" $ do
|
||||
let svk =
|
||||
"zxviews1qvapd723qqqqpqq09ldgykvyusthmkky2w062esx5xg3nz4m29qxcvndyx6grrhrdepu4ns88sjr3u6mfp2hhwj5hfd6y24r0f64uwq65vjrmsh9mr568kenk33fcumag6djcjywkm5v295egjuk3qdd47atprs0j33nhaaqep3uqspzp5kg4mthugvug0sc3gc83atkrgmguw9g7gkvh82tugrntf66lnvyeh6ufh4j2xt0xr2r4zujtm3qvrmd3vvnulycuwqtetg2jk384"
|
||||
it "succeeds with correct key" pending
|
||||
it "succeeds with correct key" $ do
|
||||
let rawKey = decodeBech32 svk
|
||||
let rawNote =
|
||||
ShieldedOutput
|
||||
(decodeHexText
|
||||
"a2853a37316dfc32301dfcae2583797a6dab0aaba8b08e9bd052d2d65bc66c14")
|
||||
(decodeHexText
|
||||
"65dc83588d7fa63510391505b2b57455d8740a29bce1ad20f798a647b4163a5a")
|
||||
(decodeHexText
|
||||
"3c9748817d696cf1e540d0ffa759092740e23c2b07415d326f9d007ba1a43bea")
|
||||
(decodeHexText
|
||||
"62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d233")
|
||||
(decodeHexText
|
||||
"01c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668")
|
||||
(decodeHexText
|
||||
"9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900")
|
||||
print rawNote
|
||||
let a = decodeSaplingOutput (bytes rawKey) rawNote
|
||||
maybe 0 a_value a `shouldNotBe` 0
|
||||
describe "Decode Orchard tx" $ do
|
||||
let uvk =
|
||||
"uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"
|
||||
|
|
Loading…
Reference in a new issue