Implement Sapling tx decoding

This commit is contained in:
Rene Vergara 2023-08-22 15:05:40 -05:00
parent 4d2540dce1
commit 1d8e3729a8
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
6 changed files with 98 additions and 40 deletions

View file

@ -19,13 +19,17 @@ use haskell_ffi::{
use zcash_primitives::{ use zcash_primitives::{
zip32::Scope as SaplingScope, zip32::Scope as SaplingScope,
transaction::components::sapling::{ transaction::components::sapling::{
read_zkproof,
GrothProofBytes, GrothProofBytes,
OutputDescription, OutputDescription,
CompactOutputDescription CompactOutputDescription
}, },
sapling::{ sapling::{
value::ValueCommitment as SaplingValueCommitment, value::ValueCommitment as SaplingValueCommitment,
keys::FullViewingKey as SaplingViewingKey, keys::{
FullViewingKey as SaplingViewingKey,
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey
},
note_encryption::SaplingDomain, note_encryption::SaplingDomain,
PaymentAddress, PaymentAddress,
note::ExtractedNoteCommitment as SaplingExtractedNoteCommitment note::ExtractedNoteCommitment as SaplingExtractedNoteCommitment
@ -311,35 +315,49 @@ pub extern "C" fn rust_wrapper_ufvk_decode(
} }
} }
//#[no_mangle] #[no_mangle]
//pub extern "C" fn rust_wrapper_sapling_note_decrypt( pub extern "C" fn rust_wrapper_sapling_note_decrypt(
//key: *const u8, key: *const u8,
//key_len: usize, key_len: usize,
//note: *const u8, note: *const u8,
//note_len: usize, note_len: usize,
//out: *mut u8, out: *mut u8,
//out_len: &mut usize out_len: &mut usize
//){ ){
//let evk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW); 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 note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
//let svk = ExtendedFullViewingKey::read(&*evk); let svk = ExtendedFullViewingKey::read(&*evk);
//match svk { match svk {
//Ok(k) => { Ok(k) => {
//let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000)); let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000));
//let action: CompactOutputDescription = CompactOutputDescription { let mut action_bytes = vec![0];
//ephemeral_key: EphemeralKeyBytes(to_array(note_input.eph_key)), action_bytes.extend(&note_input.cv);
//cmu: SaplingExtractedNoteCommitment::from_bytes(&to_array(note_input.cmu)).unwrap(), action_bytes.extend(&note_input.cmu);
//enc_ciphertext: to_array(note_input.enc_txt) action_bytes.extend(&note_input.eph_key);
//}; action_bytes.extend(&note_input.enc_txt);
//let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External); action_bytes.extend(&note_input.out_txt);
//let result = zcash_note_encryption::try_note_decryption(&domain, &ivk, &action); action_bytes.extend(&note_input.proof);
//} let action2 = OutputDescription::read(&mut action_bytes.as_slice()).unwrap();
//Err(_e) => { let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External);
//let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; let pivk = SaplingPreparedIncomingViewingKey::new(&fvk);
//marshall_to_haskell_var(&hn0, out, out_len, RW); 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] #[no_mangle]
pub extern "C" fn rust_wrapper_orchard_note_decrypt( pub extern "C" fn rust_wrapper_orchard_note_decrypt(

View file

@ -72,6 +72,14 @@ import ZcashHaskell.Types
-> `Bool' -> `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 {# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer UnifiedFullViewingKey'& , getVarBuffer `Buffer UnifiedFullViewingKey'&
@ -82,7 +90,7 @@ import ZcashHaskell.Types
{# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode {# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'& , toBorshVar* `OrchardAction'&
, getVarBuffer `Buffer OrchardDecodedAction'& , getVarBuffer `Buffer DecodedNote'&
} }
-> `()' -> `()'
#} #}

View file

@ -35,7 +35,7 @@ decodeUfvk str =
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@. -- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
decryptOrchardAction :: decryptOrchardAction ::
OrchardAction -> UnifiedFullViewingKey -> Maybe OrchardDecodedAction OrchardAction -> UnifiedFullViewingKey -> Maybe DecodedNote
decryptOrchardAction encAction key = decryptOrchardAction encAction key =
case a_value decodedAction of case a_value decodedAction of
0 -> Nothing 0 -> Nothing

View file

@ -3,9 +3,12 @@ module ZcashHaskell.Sapling where
import C.Zcash import C.Zcash
( rustWrapperIsShielded ( rustWrapperIsShielded
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingNoteDecode
, rustWrapperSaplingVkDecode , rustWrapperSaplingVkDecode
) )
import qualified Data.ByteString as BS 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 -- | Check if given bytesting is a valid encoded shielded address
isValidShieldedAddress :: BS.ByteString -> Bool 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 -- | Check if the given bytestring for the Sapling viewing key matches the second bytestring for the address
matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool
matchSaplingAddress = rustWrapperSaplingCheck 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

View file

@ -153,15 +153,15 @@ instance FromJSON OrchardAction where
(decodeHexText cval) (decodeHexText cval)
(decodeHexText a) (decodeHexText a)
-- | Type to represent a decoded Orchard Action -- | Type to represent a decoded note
data OrchardDecodedAction = OrchardDecodedAction data DecodedNote = DecodedNote
{ a_value :: Int64 -- ^ The amount of the transaction in _zatoshis_. { a_value :: Int64 -- ^ The amount of the transaction in _zatoshis_.
, a_recipient :: BS.ByteString -- ^ The recipient Orchard receiver. , a_recipient :: BS.ByteString -- ^ The recipient Orchard receiver.
, a_memo :: BS.ByteString -- ^ The decoded shielded memo field. , a_memo :: BS.ByteString -- ^ The decoded shielded memo field.
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardDecodedAction deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote
-- * Helpers -- * Helpers
-- | Helper function to turn a hex-encoded string to bytestring -- | Helper function to turn a hex-encoded string to bytestring

View file

@ -11,17 +11,18 @@ import Data.Word
import Test.Hspec import Test.Hspec
import ZcashHaskell.Orchard import ZcashHaskell.Orchard
import ZcashHaskell.Sapling import ZcashHaskell.Sapling
( isValidSaplingViewingKey ( decodeSaplingOutput
, isValidSaplingViewingKey
, isValidShieldedAddress , isValidShieldedAddress
, matchSaplingAddress , matchSaplingAddress
) )
import ZcashHaskell.Types import ZcashHaskell.Types
( BlockResponse(..) ( BlockResponse(..)
, DecodedNote(..)
, OrchardAction(..) , OrchardAction(..)
, OrchardDecodedAction(..)
, RawData(..) , RawData(..)
, RawTxResponse(..) , RawTxResponse(..)
, ShieldedOutput(s_cmu) , ShieldedOutput(ShieldedOutput, s_cmu)
, UnifiedFullViewingKey(..) , UnifiedFullViewingKey(..)
, decodeHexText , decodeHexText
) )
@ -313,7 +314,25 @@ main = do
describe "Decode Sapling tx" $ do describe "Decode Sapling tx" $ do
let svk = let svk =
"zxviews1qvapd723qqqqpqq09ldgykvyusthmkky2w062esx5xg3nz4m29qxcvndyx6grrhrdepu4ns88sjr3u6mfp2hhwj5hfd6y24r0f64uwq65vjrmsh9mr568kenk33fcumag6djcjywkm5v295egjuk3qdd47atprs0j33nhaaqep3uqspzp5kg4mthugvug0sc3gc83atkrgmguw9g7gkvh82tugrntf66lnvyeh6ufh4j2xt0xr2r4zujtm3qvrmd3vvnulycuwqtetg2jk384" "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 describe "Decode Orchard tx" $ do
let uvk = let uvk =
"uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm" "uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"