From bb9d336dc326ad9d35eb8c29dbee9e3acbb29341 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 26 Mar 2024 20:39:31 +0000 Subject: [PATCH] Implements low-level transparent components (#45) This PR includes components for the deserialization of low-level part of transparent components. Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/45 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- librustzcash-wrapper/src/lib.rs | 42 ++++++++++++++++++++++++++++----- src/ZcashHaskell/Types.hs | 37 +++++++++++++++++++++++++++-- zcash-haskell.cabal | 1 + 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index d6ee532..e74beb3 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -36,6 +36,7 @@ use zcash_primitives::{ Bundle as TransparentBundle, TxIn, TxOut, + OutPoint, Authorized }, sapling::{ @@ -246,6 +247,7 @@ impl ToHaskell for Htx { #[derive(BorshSerialize, BorshDeserialize)] pub struct HTBundle { + empty: bool, vin: Vec, vout: Vec, coinbase: bool @@ -260,13 +262,13 @@ impl ToHaskell for HTBundle { impl HTBundle { pub fn from_bundle(b: &TransparentBundle) -> HTBundle { - HTBundle { vin: b.vin.iter().map(HTxIn::pack).collect() , vout: b.vout.iter().map(HTxOut::pack).collect(), coinbase: b.is_coinbase()} + HTBundle {empty: false, vin: b.vin.iter().map(HTxIn::pack).collect() , vout: b.vout.iter().map(HTxOut::pack).collect(), coinbase: b.is_coinbase()} } } #[derive(BorshSerialize, BorshDeserialize)] pub struct HTxIn { - outpoint: u32, + outpoint: Houtpoint, script: Vec, sequence: u32 } @@ -280,7 +282,7 @@ impl ToHaskell for HTxIn { impl HTxIn { pub fn pack(t: &TxIn) -> HTxIn { - return HTxIn { outpoint: t.prevout.n(), script: t.script_sig.0.clone(), sequence: t.sequence} + return HTxIn { outpoint: Houtpoint::pack(&t.prevout), script: t.script_sig.0.clone(), sequence: t.sequence} } } @@ -303,6 +305,25 @@ impl HTxOut { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Houtpoint { + hash: Vec, + index: u32 +} + +impl ToHaskell for Houtpoint { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl Houtpoint { + pub fn pack(o: &OutPoint) -> Houtpoint { + return Houtpoint {hash: o.hash().to_vec() , index: o.n() } + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, @@ -651,11 +672,20 @@ pub extern "C" fn rust_wrapper_tx_read( let parsed_tx = Transaction::read(&mut tx_reader, Nu5); match parsed_tx { Ok(t) => { - let h = Htx {txid: t.txid().as_ref().to_vec(), locktime: t.lock_time(), expiry: u32::from(t.expiry_height()), t_bundle: HTBundle::from_bundle(t.transparent_bundle().unwrap()) }; - marshall_to_haskell_var(&h, out, out_len, RW); + let tb = t.transparent_bundle(); + match tb { + Some(my_tb) => { + let h = Htx {txid: t.txid().as_ref().to_vec(), locktime: t.lock_time(), expiry: u32::from(t.expiry_height()), t_bundle: HTBundle::from_bundle(my_tb) }; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + None => { + let h0 = Htx {txid: t.txid().as_ref().to_vec(), locktime: t.lock_time(), expiry: u32::from(t.expiry_height()), t_bundle: HTBundle {empty: true, vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: true} }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } }, Err(_e) => { - let h0 = Htx {txid: vec![0], locktime: 0, expiry: 0, t_bundle: HTBundle {vin: vec![HTxIn {outpoint: 0, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: true} }; + let h0 = Htx {txid: vec![0], locktime: 0, expiry: 0, t_bundle: HTBundle {empty: true, vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: true} }; marshall_to_haskell_var(&h0, out, out_len, RW); } } diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index d22a8a7..91ef4a3 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -30,6 +30,7 @@ import Data.Aeson import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C +import qualified Data.ByteString.Lazy.UTF8 as US import Data.HexString import Data.Int import Data.Maybe (fromMaybe) @@ -135,6 +136,29 @@ data TransparentBundle = TransparentBundle , tb_coinbase :: !Bool } deriving (Eq, Prelude.Show, Read) +-- | Read a raw transparent bundle into the Haskell type +fromRawTBundle :: RawTBundle -> Maybe TransparentBundle +fromRawTBundle rtb = + if ztb_empty rtb + then Nothing + else Just $ + TransparentBundle + (map fromRawTxIn $ ztb_vin rtb) + (map fromRawTxOut $ ztb_vout rtb) + (ztb_coinbase rtb) + +fromRawTxIn :: RawTxIn -> H.TxIn +fromRawTxIn t = + H.TxIn + (H.OutPoint + (read $ US.toString $ C.fromStrict $ rop_hash $ rti_outpoint t) + (rop_n $ rti_outpoint t)) + (rti_script t) + (rti_seq t) + +fromRawTxOut :: RawTxOut -> H.TxOut +fromRawTxOut t = H.TxOut (rto_amt t) (rto_script t) + -- *** Constants for Sapling Human-readable part sapExtSpendingKeyHrp = "secret-extended-key-main" :: String @@ -262,7 +286,8 @@ data RawZebraTx = RawZebraTx -- | Type for a raw deserialized Zebra transparent bundle data RawTBundle = RawTBundle - { ztb_vin :: ![RawTxIn] + { ztb_empty :: !Bool + , ztb_vin :: ![RawTxIn] , ztb_vout :: ![RawTxOut] , ztb_coinbase :: !Bool } deriving stock (Eq, Prelude.Show, GHC.Generic) @@ -323,7 +348,7 @@ data TransparentAddress = TransparentAddress -- | Wrapper types for transparent elements data RawTxIn = RawTxIn - { rti_outpoint :: !Word32 + { rti_outpoint :: !RawOutPoint , rti_script :: !BS.ByteString , rti_seq :: !Word32 } deriving stock (Eq, Prelude.Show, GHC.Generic) @@ -339,6 +364,14 @@ data RawTxOut = RawTxOut deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxOut +data RawOutPoint = RawOutPoint + { rop_hash :: !BS.ByteString + , rop_n :: !Word32 + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawOutPoint + -- * Sapling -- | A spending key for Sapling newtype SaplingSpendingKey = diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 05ac71b..15d27f9 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -58,6 +58,7 @@ library , text , haskoin-core , secp256k1-haskell + , utf8-string build-tool-depends: c2hs:c2hs default-language: Haskell2010