From 787cf40629575abede6da5998fd69d8a9ba45f02 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 09:12:58 -0500 Subject: [PATCH] Add functionality for transaction creation --- CHANGELOG.md | 6 ++++ librustzcash-wrapper/src/lib.rs | 50 ++++++++++++++++++++++++++++++-- src/ZcashHaskell/Types.hs | 23 +++++++++++++++ src/ZcashHaskell/Utils.hs | 51 +++++++++++++++++++++++++++++++-- 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bee207c..7e315c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.6.1.1] +### Added + +- Type for transaction creation errors +- Types for Sapling circuit parameters +- Function to create transaction + ### Changed - Add `Read` instance for `Rseed` diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6d98974..c4f3316 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1809,9 +1809,53 @@ pub extern "C" fn rust_wrapper_create_transaction( let h = Hhex {bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, - Err(_e) => { - let x = Hhex {bytes: vec![0]}; - marshall_to_haskell_var(&x, out, out_len, RW); + Err(e) => { + match e { + Error::InsufficientFunds(_y) => { + let x = Hhex {bytes: vec![0]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::ChangeRequired(_y1) => { + let x = Hhex {bytes: vec![1]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::Fee(_y2) => { + let x = Hhex {bytes: vec![2]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::Balance(x) => { + let x = Hhex {bytes: vec![3]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::TransparentBuild(x) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuild(x) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuild(x) => { + let x = Hhex {bytes: vec![6]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardSpend(x) => { + let x = Hhex {bytes: vec![7]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardRecipient(x) => { + let x = Hhex {bytes: vec![8]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuilderNotAvailable => { + let x = Hhex {bytes: vec![9]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuilderNotAvailable => { + let x = Hhex {bytes: vec![10]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } } } diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 259c080..ed8cbb9 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -692,6 +692,29 @@ data OutgoingNote = OutGoingNote deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OutgoingNote +newtype SaplingSpendParams = SaplingSpendParams + { sapSParams :: BS.ByteString + } deriving newtype (Eq, Prelude.Show, Read) + +newtype SaplingOutputParams = SaplingOutputParams + { sapOParams :: BS.ByteString + } deriving newtype (Eq, Prelude.Show, Read) + +data TxError + = InsufficientFunds + | ChangeRequired + | Fee + | Balance + | TransparentBuild + | SaplingBuild + | OrchardBuild + | OrchardSpend + | OrchardRecipient + | SaplingBuilderNotAvailable + | OrchardBuilderNotAvailable + | ZHError + deriving (Eq, Prelude.Show, Read) + -- * Classes -- | Class to represent types with a bytestring representation class ToBytes a where diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 3187c62..9c87f39 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -19,8 +19,9 @@ module ZcashHaskell.Utils where import C.Zcash ( rustWrapperBech32Decode - , rustWrapperBech32mEncode , rustWrapperBech32Encode + , rustWrapperBech32mEncode + , rustWrapperCreateTx , rustWrapperF4Jumble , rustWrapperF4UnJumble , rustWrapperTxRead @@ -31,7 +32,7 @@ import Data.Aeson import Data.Binary.Get import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS -import Data.HexString (HexString(..)) +import Data.HexString (HexString(..), toBytes) import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable @@ -121,3 +122,49 @@ readZebraTransaction hex = where rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex +createTransaction :: + Maybe SaplingWitness -- ^ to obtain the Sapling anchor + -> Maybe OrchardWitness -- ^ to obtain the Orchard anchor + -> [TransparentTxSpend] -- ^ the list of transparent notes to spend + -> [SaplingTxSpend] -- ^ the list of Sapling notes to spend + -> [OrchardTxSpend] -- ^ the list of Orchard notes to spend + -> [OutgoingNote] -- ^ the list of outgoing notes, including change notes + -> SaplingSpendParams -- ^ the Sapling circuit spending parameters + -> SaplingOutputParams -- ^ the Sapling circuit output parameters + -> ZcashNet -- ^ the network to be used + -> Int -- ^ target block height + -> Either TxError HexString +createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh = + if BS.length (hexBytes txResult) > 1 + then Right txResult + else case head (BS.unpack $ hexBytes txResult) of + 0 -> Left InsufficientFunds + 1 -> Left ChangeRequired + 2 -> Left Fee + 3 -> Left Balance + 4 -> Left TransparentBuild + 5 -> Left SaplingBuild + 6 -> Left OrchardBuild + 7 -> Left OrchardSpend + 8 -> Left OrchardRecipient + 9 -> Left SaplingBuilderNotAvailable + 10 -> Left OrchardBuilderNotAvailable + _ -> Left ZHError + where + txResult = + withPureBorshVarBuffer $ + rustWrapperCreateTx + (case sapAnchor of + Nothing -> "0" + Just sA -> toBytes $ sapWit sA) + (case orchAnchor of + Nothing -> "0" + Just oA -> toBytes $ orchWit oA) + tSpend + sSpend + oSpend + outgoing + (sapSParams sParams) + (sapOParams oParams) + (znet == MainNet) + (fromIntegral bh)