From ca8010145ba10769ab3ce57d0c6f84ad9cb61374 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 16 Jan 2024 16:15:05 -0600 Subject: [PATCH 001/149] Add functions to generate seed phrases and seeds --- CHANGELOG.md | 7 +++++ librustzcash-wrapper/src/lib.rs | 31 ++++++++++++++++++++ package.yaml | 3 +- src/C/Zcash.chs | 26 ++++++++++++----- src/ZcashHaskell/Keys.hs | 52 +++++++++++++++++++++++++++++++++ src/ZcashHaskell/Transparent.hs | 32 ++++++++++++++++++++ src/ZcashHaskell/Types.hs | 47 ++++++++++------------------- stack.yaml | 2 +- test/Spec.hs | 21 +++++++++++-- zcash-haskell.cabal | 7 +++-- 10 files changed, 183 insertions(+), 45 deletions(-) create mode 100644 src/ZcashHaskell/Keys.hs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b07525..820998f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.0] + +### Added + +- Function to encode a human-readable transparent address +- Function to generate a seed phrase + ## [0.3.0] ### Added diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 8973af8..5a6f046 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -37,6 +37,7 @@ use haskell_ffi::{ use zcash_primitives::{ zip32::Scope as SaplingScope, + zip339::{Count, Mnemonic}, transaction::components::sapling::{ read_zkproof, GrothProofBytes, @@ -582,3 +583,33 @@ pub extern "C" fn rust_wrapper_tx_parse( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_gen_seed_phrase( + out: *mut u8, + out_len: &mut usize + ){ + let mnemonic = Mnemonic::generate(Count::Words24); + let seed = mnemonic.phrase().as_bytes().to_vec(); + marshall_to_haskell_var(&seed, out, out_len, RW); +} + +#[no_mangle] +pub extern "C" fn rust_wrapper_recover_seed( + input: *const u8, + input_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let phrase: String = marshall_from_haskell_var(input, input_len, RW); + let mnemonic = Mnemonic::from_phrase(phrase); + match mnemonic { + Ok(m) => { + let s = m.to_seed("").to_vec(); + marshall_to_haskell_var(&s, out, out_len, RW); + }, + Err(_e) => { + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } + } +} diff --git a/package.yaml b/package.yaml index 4856888..35f34e7 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: zcash-haskell -version: 0.3.0 +version: 0.4.0 git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell" license: LGPL-3 author: "Rene Vergara" @@ -53,3 +53,4 @@ tests: - bytestring - text - aeson + - haskoin-core diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index ebda582..3d83a26 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -29,7 +29,7 @@ module C.Zcash where import qualified Data.ByteString as BS import Codec.Borsh -import Data.Text (Text) +import qualified Data.Text as T import Data.Word import Data.Int import Data.Structured @@ -43,6 +43,12 @@ import qualified Generics.SOP as SOP import qualified GHC.Generics as GHC import ZcashHaskell.Types +{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer RawData'& + } + -> `()' +#} {# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble { toBorshVar* `BS.ByteString'& @@ -71,13 +77,6 @@ import ZcashHaskell.Types -> `Bool' #} -{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode - { toBorshVar* `BS.ByteString'& - , getVarBuffer `Buffer RawData'& - } - -> `()' -#} - {# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode { toBorshVar* `BS.ByteString'& } @@ -127,3 +126,14 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase + { getVarBuffer `Buffer Phrase'& } -> `()' +#} + +{# fun unsafe rust_wrapper_recover_seed as rustWrapperGetSeed + { toBorshVar* `Phrase'& + , getVarBuffer `Buffer Seed'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Keys.hs b/src/ZcashHaskell/Keys.hs new file mode 100644 index 0000000..7bedf5f --- /dev/null +++ b/src/ZcashHaskell/Keys.hs @@ -0,0 +1,52 @@ +{- Copyright 2022-2024 Vergara Technologies LLC + + This file is part of Zcash-Haskell. + + Zcash-Haskell is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License along with + Zcash-Haskell. If not, see . +-} +-- | +-- Module : ZcashHaskell.Keys +-- Copyright : 2022-2024 Vergara Technologies +-- License : LGPL-3 +-- +-- Maintainer : pitmutt@vergara.tech +-- Stability : experimental +-- Portability : unknown +-- +-- Functions to generate keys for the Zcash blockchain +-- +module ZcashHaskell.Keys where + +import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed) +import qualified Data.ByteString as BS +import qualified Data.Text as T +import Foreign.Rust.Marshall.Variable + ( withBorshVarBuffer + , withPureBorshVarBuffer + ) +import ZcashHaskell.Types (Phrase, Seed) + +-- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses. +generateWalletSeedPhrase :: IO Phrase +generateWalletSeedPhrase = withBorshVarBuffer rustWrapperGenSeedPhrase + +-- | Get +getWalletSeed :: Phrase -> Maybe Seed +getWalletSeed p = + if BS.length result > 0 + then Just result + else Nothing + where + result :: Seed + result = (withPureBorshVarBuffer . rustWrapperGetSeed) p diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index bbc10d6..0d605e0 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -31,4 +31,36 @@ -- module ZcashHaskell.Transparent where +import Crypto.Hash +import qualified Data.ByteArray as BA import qualified Data.ByteString as BS +import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58) +import qualified Data.Text as T +import qualified Data.Text.Encoding as E +import Data.Word +import ZcashHaskell.Types + ( TransparentAddress(..) + , TransparentType(..) + , ZcashNet(..) + ) + +encodeTransparent :: TransparentAddress -> T.Text +encodeTransparent t = + case ta_type t of + P2SH -> + case ta_net t of + MainNet -> encodeTransparent' (0x1c, 0xbd) $ ta_bytes t + _ -> encodeTransparent' (0x1c, 0xba) $ ta_bytes t + P2PKH -> + case ta_net t of + MainNet -> encodeTransparent' (0x1c, 0xb8) $ ta_bytes t + _ -> encodeTransparent' (0x1d, 0x25) $ ta_bytes t + where + encodeTransparent' :: (Word8, Word8) -> BS.ByteString -> T.Text + encodeTransparent' (a, b) h = + E.decodeUtf8 $ encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum + where + sha256 :: BS.ByteString -> BS.ByteString + sha256 bs = BA.convert (hash bs :: Digest SHA256) + digest = BS.pack [a, b] <> h + checksum = sha256 $ sha256 digest diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 47df822..759e148 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -25,10 +25,10 @@ -- | -- Module : ZcashHaskell.Types --- Copyright : Vergara Technologies 2023 --- License : BOSL +-- Copyright : 2022-2024 Vergara Technologies +-- License : LGPL-3 -- --- Maintainer : rene@vergara.network +-- Maintainer : pitmut@vergara.tech -- Stability : experimental -- Portability : unknown -- @@ -37,12 +37,10 @@ module ZcashHaskell.Types where import Codec.Borsh -import Control.Exception (MaskingState(Unmasked)) import Crypto.Hash import Data.Aeson import qualified Data.ByteArray as BA import qualified Data.ByteString as BS -import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58) import qualified Data.ByteString.Char8 as C import Data.Int import Data.Structured @@ -53,6 +51,14 @@ import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP -- * General +-- +-- | A seed for generating private keys +type Seed = C.ByteString + +-- | A mnemonic phrase used to derive seeds +type Phrase = BS.ByteString + +-- -- | Type to represent data after Bech32 decoding data RawData = RawData { hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding @@ -144,35 +150,14 @@ data ZcashNet data TransparentType = P2SH | P2PKH - deriving (Eq) + deriving (Eq, Prelude.Show) -- | Type to represent a transparent Zcash addresses data TransparentAddress = TransparentAddress - { ta_type :: TransparentType - , ta_net :: ZcashNet - , ta_bytes :: BS.ByteString - } deriving (Eq) - -instance Prelude.Show TransparentAddress where - show t = - case ta_type t of - P2SH -> - case ta_net t of - MainNet -> Prelude.show $ encodeTransparent (0x1c, 0xbd) $ ta_bytes t - _ -> Prelude.show $ encodeTransparent (0x1c, 0xba) $ ta_bytes t - P2PKH -> - case ta_net t of - MainNet -> Prelude.show $ encodeTransparent (0x1c, 0xb8) $ ta_bytes t - _ -> Prelude.show $ encodeTransparent (0x1d, 0x25) $ ta_bytes t - where - encodeTransparent :: (Word8, Word8) -> BS.ByteString -> BS.ByteString - encodeTransparent (a, b) h = - encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum - where - sha256 :: BS.ByteString -> BS.ByteString - sha256 bs = BA.convert (hash bs :: Digest SHA256) - digest = BS.pack [a, b] <> h - checksum = sha256 $ sha256 digest + { ta_type :: !TransparentType + , ta_net :: !ZcashNet + , ta_bytes :: !BS.ByteString + } deriving (Eq, Prelude.Show) -- * Sapling -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. diff --git a/stack.yaml b/stack.yaml index 33603d4..45d827d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,7 +17,7 @@ # # resolver: ./custom-snapshot.yaml # resolver: https://example.com/snapshots/2018-01-01.yaml -resolver: lts-21.21 +resolver: lts-21.22 # User packages to be built. # Various formats can be used as shown in the example below. diff --git a/test/Spec.hs b/test/Spec.hs index 8007b19..a360e77 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -22,6 +22,8 @@ import C.Zcash (rustWrapperUADecode) import Data.Aeson import Data.Bool (Bool(True)) import qualified Data.ByteString as BS +import Data.Either (isRight) +import Data.Foldable (sequenceA_) import Data.Maybe import qualified Data.Text as T import qualified Data.Text.Encoding as E @@ -29,7 +31,9 @@ import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO import Data.Word import GHC.Float.RealFracMethods (properFractionDoubleInteger) +import Haskoin.Keys.Mnemonic import Test.Hspec +import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) import ZcashHaskell.Orchard import ZcashHaskell.Sapling ( decodeSaplingOutput @@ -38,6 +42,7 @@ import ZcashHaskell.Sapling , isValidShieldedAddress , matchSaplingAddress ) +import ZcashHaskell.Transparent (encodeTransparent) import ZcashHaskell.Types ( BlockResponse(..) , DecodedNote(..) @@ -280,6 +285,18 @@ main = do Right x -> rt_id x `shouldBe` "5242b51f22a7d6fe9dee237137271cde704d306a5fff6a862bffaebb6f0e7e56" + describe "Seeds" $ do + it "generate seed phrase" $ do + s <- generateWalletSeedPhrase + BS.length s `shouldNotBe` 0 + it "get seed from phrase" $ do + s <- generateWalletSeedPhrase + let x = getWalletSeed s + let result = + case x of + Nothing -> False + Just s' -> True + result `shouldBe` True describe "Sapling address" $ do it "succeeds with valid address" $ do let sa = @@ -427,5 +444,5 @@ main = do let msg = case isValidUnifiedAddress ua of Nothing -> "Bad UA" - Just u -> maybe "No transparent" show $ t_rec u - msg `shouldBe` "Got it" + Just u -> maybe "No transparent" encodeTransparent $ t_rec u + msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 30803ce..8bb6a08 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2. +-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.3.0 +version: 0.4.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -26,6 +26,8 @@ source-repository head library exposed-modules: C.Zcash + ZcashHaskell.DB + ZcashHaskell.Keys ZcashHaskell.Orchard ZcashHaskell.Sapling ZcashHaskell.Transparent @@ -63,6 +65,7 @@ test-suite zcash-haskell-test aeson , base >=4.7 && <5 , bytestring + , haskoin-core , hspec , text , zcash-haskell From 0858b805d066d0ce91dcc05594d929e63a99484e Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 17 Jan 2024 07:44:02 -0600 Subject: [PATCH 002/149] Update version --- package.yaml | 2 +- zcash-haskell.cabal | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.yaml b/package.yaml index 35f34e7..122cf17 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: zcash-haskell -version: 0.4.0 +version: 0.4.1 git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell" license: LGPL-3 author: "Rene Vergara" diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 8bb6a08..1eea1ba 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.0 +version: 0.4.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -26,7 +26,6 @@ source-repository head library exposed-modules: C.Zcash - ZcashHaskell.DB ZcashHaskell.Keys ZcashHaskell.Orchard ZcashHaskell.Sapling From beebabcf498410453bd6aaa23b04becbc2b2e839 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 18 Jan 2024 12:55:23 -0600 Subject: [PATCH 003/149] Update license to MIT --- COPYING.LESSER.md | 157 -------- COPYING.md | 675 -------------------------------- LICENSE | 22 ++ librustzcash-wrapper/src/lib.rs | 12 - package.yaml | 2 +- src/C/Zcash.chs | 12 - src/ZcashHaskell/Keys.hs | 22 +- src/ZcashHaskell/Orchard.hs | 23 +- src/ZcashHaskell/Sapling.hs | 30 +- src/ZcashHaskell/Transparent.hs | 26 +- src/ZcashHaskell/Types.hs | 38 +- src/ZcashHaskell/Utils.hs | 28 +- zcash-haskell.cabal | 1 + 13 files changed, 65 insertions(+), 983 deletions(-) delete mode 100644 COPYING.LESSER.md delete mode 100644 COPYING.md create mode 100644 LICENSE diff --git a/COPYING.LESSER.md b/COPYING.LESSER.md deleted file mode 100644 index 6fb6a01..0000000 --- a/COPYING.LESSER.md +++ /dev/null @@ -1,157 +0,0 @@ -# GNU LESSER GENERAL PUBLIC LICENSE - -Version 3, 29 June 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. - - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -This version of the GNU Lesser General Public License incorporates the -terms and conditions of version 3 of the GNU General Public License, -supplemented by the additional permissions listed below. - -## 0. Additional Definitions. - -As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the -GNU General Public License. - -"The Library" refers to a covered work governed by this License, other -than an Application or a Combined Work as defined below. - -An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - -A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - -The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - -The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - -## 1. Exception to Section 3 of the GNU GPL. - -You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - -## 2. Conveying Modified Versions. - -If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - -- a) under this License, provided that you make a good faith effort - to ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or -- b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - -## 3. Object Code Incorporating Material from Library Header Files. - -The object code form of an Application may incorporate material from a -header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - -- a) Give prominent notice with each copy of the object code that - the Library is used in it and that the Library and its use are - covered by this License. -- b) Accompany the object code with a copy of the GNU GPL and this - license document. - -## 4. Combined Works. - -You may convey a Combined Work under terms of your choice that, taken -together, effectively do not restrict modification of the portions of -the Library contained in the Combined Work and reverse engineering for -debugging such modifications, if you also do each of the following: - -- a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. -- b) Accompany the Combined Work with a copy of the GNU GPL and this - license document. -- c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. -- d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of - this License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with - the Library. A suitable mechanism is one that (a) uses at run - time a copy of the Library already present on the user's - computer system, and (b) will operate properly with a modified - version of the Library that is interface-compatible with the - Linked Version. -- e) Provide Installation Information, but only if you would - otherwise be required to provide such information under section 6 - of the GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the Application - with a modified version of the Linked Version. (If you use option - 4d0, the Installation Information must accompany the Minimal - Corresponding Source and Corresponding Application Code. If you - use option 4d1, you must provide the Installation Information in - the manner specified by section 6 of the GNU GPL for conveying - Corresponding Source.) - -## 5. Combined Libraries. - -You may place library facilities that are a work based on the Library -side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - -- a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities, conveyed under the terms of this License. -- b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - -## 6. Revised Versions of the GNU Lesser General Public License. - -The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -as you received it specifies that a certain numbered version of the -GNU Lesser General Public License "or any later version" applies to -it, you have the option of following the terms and conditions either -of that published version or of any later version published by the -Free Software Foundation. If the Library as you received it does not -specify a version number of the GNU Lesser General Public License, you -may choose any version of the GNU Lesser General Public License ever -published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/COPYING.md b/COPYING.md deleted file mode 100644 index 496acdb..0000000 --- a/COPYING.md +++ /dev/null @@ -1,675 +0,0 @@ -# GNU GENERAL PUBLIC LICENSE - -Version 3, 29 June 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. - - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -## Preamble - -The GNU General Public License is a free, copyleft license for -software and other kinds of works. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom -to share and change all versions of a program--to make sure it remains -free software for all its users. We, the Free Software Foundation, use -the GNU General Public License for most of our software; it applies -also to any other work released this way by its authors. You can apply -it to your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you -have certain responsibilities if you distribute copies of the -software, or if you modify it: responsibilities to respect the freedom -of others. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - -Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - -Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the -manufacturer can do so. This is fundamentally incompatible with the -aim of protecting users' freedom to change the software. The -systematic pattern of such abuse occurs in the area of products for -individuals to use, which is precisely where it is most unacceptable. -Therefore, we have designed this version of the GPL to prohibit the -practice for those products. If such problems arise substantially in -other domains, we stand ready to extend this provision to those -domains in future versions of the GPL, as needed to protect the -freedom of users. - -Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish -to avoid the special danger that patents applied to a free program -could make it effectively proprietary. To prevent this, the GPL -assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and -modification follow. - -## TERMS AND CONDITIONS - -### 0. Definitions. - -"This License" refers to version 3 of the GNU General Public License. - -"Copyright" also means copyright-like laws that apply to other kinds -of works, such as semiconductor masks. - -"The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - -To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of -an exact copy. The resulting work is called a "modified version" of -the earlier work or a work "based on" the earlier work. - -A "covered work" means either the unmodified Program or a work based -on the Program. - -To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - -To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user -through a computer network, with no transfer of a copy, is not -conveying. - -An interactive user interface displays "Appropriate Legal Notices" to -the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - -### 1. Source Code. - -The "source code" for a work means the preferred form of the work for -making modifications to it. "Object code" means any non-source form of -a work. - -A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - -The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - -The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can -regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same -work. - -### 2. Basic Permissions. - -All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, -without conditions so long as your license otherwise remains in force. -You may convey covered works to others for the sole purpose of having -them make modifications exclusively for you, or provide you with -facilities for running those works, provided that you comply with the -terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for -you must do so exclusively on your behalf, under your direction and -control, on terms that prohibit them from making any copies of your -copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the -conditions stated below. Sublicensing is not allowed; section 10 makes -it unnecessary. - -### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - -No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - -When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such -circumvention is effected by exercising rights under this License with -respect to the covered work, and you disclaim any intention to limit -operation or modification of the work as a means of enforcing, against -the work's users, your or third parties' legal rights to forbid -circumvention of technological measures. - -### 4. Conveying Verbatim Copies. - -You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - -### 5. Conveying Modified Source Versions. - -You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these -conditions: - -- a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. -- b) The work must carry prominent notices stating that it is - released under this License and any conditions added under - section 7. This requirement modifies the requirement in section 4 - to "keep intact all notices". -- c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. -- d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - -A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - -### 6. Conveying Non-Source Forms. - -You may convey a covered work in object code form under the terms of -sections 4 and 5, provided that you also convey the machine-readable -Corresponding Source under the terms of this License, in one of these -ways: - -- a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. -- b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the Corresponding - Source from a network server at no charge. -- c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. -- d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. -- e) Convey the object code using peer-to-peer transmission, - provided you inform other peers where the object code and - Corresponding Source of the work are being offered to the general - public at no charge under subsection 6d. - -A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - -A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, -family, or household purposes, or (2) anything designed or sold for -incorporation into a dwelling. In determining whether a product is a -consumer product, doubtful cases shall be resolved in favor of -coverage. For a particular product received by a particular user, -"normally used" refers to a typical or common use of that class of -product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected -to use, the product. A product is a consumer product regardless of -whether the product has substantial commercial, industrial or -non-consumer uses, unless such uses represent the only significant -mode of use of the product. - -"Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to -install and execute modified versions of a covered work in that User -Product from a modified version of its Corresponding Source. The -information must suffice to ensure that the continued functioning of -the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or -updates for a work that has been modified or installed by the -recipient, or for the User Product in which it has been modified or -installed. Access to a network may be denied when the modification -itself materially and adversely affects the operation of the network -or violates the rules and protocols for communication across the -network. - -Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - -### 7. Additional Terms. - -"Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders -of that material) supplement the terms of this License with terms: - -- a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or -- b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or -- c) Prohibiting misrepresentation of the origin of that material, - or requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or -- d) Limiting the use for publicity purposes of names of licensors - or authors of the material; or -- e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or -- f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions - of it) with contractual assumptions of liability to the recipient, - for any liability that these contractual assumptions directly - impose on those licensors and authors. - -All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; the -above requirements apply either way. - -### 8. Termination. - -You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - -### 9. Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run -a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - -### 10. Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - -An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - -### 11. Patents. - -A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - -A contributor's "essential patent claims" are all patent claims owned -or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - -In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - -If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - -A patent license is "discriminatory" if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on -the non-exercise of one or more of the rights that are specifically -granted under this License. You may not convey a covered work if you -are a party to an arrangement with a third party that is in the -business of distributing software, under which you make payment to the -third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties -who would receive the covered work from you, a discriminatory patent -license (a) in connection with copies of the covered work conveyed by -you (or copies made from those copies), or (b) primarily for and in -connection with specific products or compilations that contain the -covered work, unless you entered into that arrangement, or that patent -license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - -### 12. No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under -this License and any other pertinent obligations, then as a -consequence you may not convey it at all. For example, if you agree to -terms that obligate you to collect a royalty for further conveying -from those to whom you convey the Program, the only way you could -satisfy both those terms and this License would be to refrain entirely -from conveying the Program. - -### 13. Use with the GNU Affero General Public License. - -Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - -### 14. Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions -of the GNU General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in -detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies that a certain numbered version of the GNU General Public -License "or any later version" applies to it, you have the option of -following the terms and conditions either of that numbered version or -of any later version published by the Free Software Foundation. If the -Program does not specify a version number of the GNU General Public -License, you may choose any version ever published by the Free -Software Foundation. - -If the Program specifies that a proxy can decide which future versions -of the GNU General Public License can be used, that proxy's public -statement of acceptance of a version permanently authorizes you to -choose that version for the Program. - -Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - -### 15. Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT -WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND -PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - -### 16. Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR -CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR -LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM -TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER -PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -### 17. Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - -END OF TERMS AND CONDITIONS - -## How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - -To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively state -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper -mail. - -If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands \`show w' and \`show c' should show the -appropriate parts of the General Public License. Of course, your -program's commands might be different; for a GUI interface, you would -use an "about box". - -You should also get your employer (if you work as a programmer) or -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. For more information on this, and how to apply and follow -the GNU GPL, see . - -The GNU General Public License does not permit incorporating your -program into proprietary programs. If your program is a subroutine -library, you may consider it more useful to permit linking proprietary -applications with the library. If this is what you want to do, use the -GNU Lesser General Public License instead of this License. But first, -please read . diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..03b6d4f --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022-2024 Vergara Technologies LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 5a6f046..e655847 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -2,18 +2,6 @@ // // This file is part of Zcash-Haskell. // -// Zcash-Haskell is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -// details. -// -// You should have received a copy of the GNU Lesser General Public License along with -// Zcash-Haskell. If not, see . use std::{ marker::PhantomData, diff --git a/package.yaml b/package.yaml index 122cf17..6072301 100644 --- a/package.yaml +++ b/package.yaml @@ -1,7 +1,7 @@ name: zcash-haskell version: 0.4.1 git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell" -license: LGPL-3 +license: MIT author: "Rene Vergara" maintainer: "pitmutt@vergara.tech" copyright: "(c)2022-2024 Vergara Technologies LLC" diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 3d83a26..f93c6c9 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -1,19 +1,7 @@ {- Copyright 2022-2024 Vergara Technologies LLC This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . -} {-# LANGUAGE DerivingStrategies #-} diff --git a/src/ZcashHaskell/Keys.hs b/src/ZcashHaskell/Keys.hs index 7bedf5f..c0b8bdc 100644 --- a/src/ZcashHaskell/Keys.hs +++ b/src/ZcashHaskell/Keys.hs @@ -1,24 +1,10 @@ -{- Copyright 2022-2024 Vergara Technologies LLC - - This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . --} +-- Copyright 2022-2024 Vergara Technologies LLC +-- This file is part of Zcash-Haskell. +-- -- | -- Module : ZcashHaskell.Keys -- Copyright : 2022-2024 Vergara Technologies --- License : LGPL-3 +-- License : MIT -- -- Maintainer : pitmutt@vergara.tech -- Stability : experimental diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 44d17a5..1472c9c 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -1,24 +1,11 @@ -{- Copyright 2022-2024 Vergara Technologies LLC - - This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . --} +-- Copyright 2022-2024 Vergara Technologies LLC +-- +-- This file is part of Zcash-Haskell. +-- -- | -- Module : ZcashHaskell.Orchard -- Copyright : 2022-2024 Vergara Technologies --- License : LGPL-3 +-- License : MIT -- -- Maintainer : rene@vergara.network -- Stability : experimental diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 2847529..bf89874 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -1,34 +1,20 @@ -{- Copyright 2022-2024 Vergara Technologies LLC - - This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . - --} -{-# LANGUAGE OverloadedStrings #-} - +-- Copyright 2022-2024 Vergara Technologies LLC +-- +-- This file is part of Zcash-Haskell. +-- -- | -- Module : ZcashHaskell.Sapling -- Copyright : 2022-2024 Vergara Technologies --- License : LGPL-3 +-- License : MIT -- --- Maintainer : rene@vergara.network +-- Maintainer : pitmutt@vergara.tech -- Stability : experimental -- Portability : unknown -- -- Functions to interact with the Sapling shielded pool of the Zcash blockchain. -- +{-# LANGUAGE OverloadedStrings #-} + module ZcashHaskell.Sapling where import C.Zcash diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 0d605e0..6de9200 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -1,27 +1,11 @@ -{- Copyright 2022-2024 Vergara Technologies LLC - - This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . - --} -{-# LANGUAGE OverloadedStrings #-} - +-- Copyright 2022-2024 Vergara Technologies LLC +-- +-- This file is part of Zcash-Haskell. +-- -- | -- Module : ZcashHaskell.Transparent -- Copyright : 2022-2024 Vergara Technologies --- License : LGPL-3 +-- License : MIT -- -- Maintainer : pitmutt@vergara.tech -- Stability : experimental diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 759e148..098dbf7 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -1,32 +1,11 @@ -{- Copyright 2022-2024 Vergara Technologies LLC - - This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . - --} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DerivingVia #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE OverloadedStrings #-} - +-- Copyright 2022-2024 Vergara Technologies LLC +-- +-- This file is part of Zcash-Haskell. +-- -- | -- Module : ZcashHaskell.Types -- Copyright : 2022-2024 Vergara Technologies --- License : LGPL-3 +-- License : MIT -- -- Maintainer : pitmut@vergara.tech -- Stability : experimental @@ -34,6 +13,13 @@ -- -- The types used by the ZcashHaskell library to interact with the Zcash blockchain -- +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE OverloadedStrings #-} + module ZcashHaskell.Types where import Codec.Borsh diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index d7577ca..53324c5 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -1,27 +1,13 @@ -{- Copyright 2022-2024 Vergara Technologies LLC - - This file is part of Zcash-Haskell. - - Zcash-Haskell is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) any - later version. - - Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along with - Zcash-Haskell. If not, see . - --} +-- Copyright 2022-2024 Vergara Technologies LLC +-- +-- This file is part of Zcash-Haskell. +-- -- | -- Module : ZcashHaskell.Utils --- Copyright : Vergara Technologies (c)2023 --- License : BOSL +-- Copyright : 2022-2024 Vergara Technologies LLC +-- License : MIT -- --- Maintainer : rene@vergara.network +-- Maintainer : pitmutt@vergara.tech -- Stability : experimental -- Portability : unknown -- diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 1eea1ba..1f6643b 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -13,6 +13,7 @@ author: Rene Vergara maintainer: pitmutt@vergara.tech copyright: (c)2022-2024 Vergara Technologies LLC license: LGPL-3 +license-file: LICENSE build-type: Simple extra-source-files: README.md From d5728525cea47a79caf7f527c7195c795c629ec7 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 18 Jan 2024 13:21:37 -0600 Subject: [PATCH 004/149] Update docs --- zcash-haskell.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 1f6643b..5b53120 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -12,7 +12,7 @@ category: Blockchain author: Rene Vergara maintainer: pitmutt@vergara.tech copyright: (c)2022-2024 Vergara Technologies LLC -license: LGPL-3 +license: MIT license-file: LICENSE build-type: Simple extra-source-files: From c001fb5343eb9468a9f8862630b0dd2daf5f5cb5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 6 Feb 2024 13:10:06 -0600 Subject: [PATCH 005/149] Implement `HexString` --- librustzcash-wrapper/src/lib.rs | 33 +++++++++++--------- package.yaml | 2 ++ src/ZcashHaskell/Sapling.hs | 26 +++------------- src/ZcashHaskell/Types.hs | 54 ++++++++++++--------------------- stack.yaml | 2 ++ test/Spec.hs | 37 +++++++++++----------- zcash-haskell.cabal | 2 ++ 7 files changed, 68 insertions(+), 88 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index e655847..ff83321 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -145,16 +145,21 @@ impl HshieldedOutput { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Hhex { + bytes: Vec +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Haction { - nf: Vec, - rk: Vec, - cmx: Vec, - eph_key: Vec, - enc_txt: Vec, - out_txt: Vec, - cv: Vec, - auth: Vec + nf: Hhex, + rk: Hhex, + cmx: Hhex, + eph_key: Hhex, + enc_txt: Hhex, + out_txt: Hhex, + cv: Hhex, + auth: Hhex } impl FromHaskell for Haction { @@ -498,12 +503,12 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt( let fvk_input: Vec = marshall_from_haskell_var(key, key_len, RW); let note_input: Haction = marshall_from_haskell_var(note, note_len, RW); let action: Action> = Action::from_parts( - Nullifier::from_bytes(&to_array(note_input.nf)).unwrap(), - VerificationKey::try_from(to_array(note_input.rk)).unwrap(), - ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx)).unwrap(), - TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key), enc_ciphertext: to_array(note_input.enc_txt), out_ciphertext: to_array(note_input.out_txt)}, - ValueCommitment::from_bytes(&to_array(note_input.cv)).unwrap(), - Signature::from(to_array(note_input.auth))); + Nullifier::from_bytes(&to_array(note_input.nf.bytes)).unwrap(), + VerificationKey::try_from(to_array(note_input.rk.bytes)).unwrap(), + ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx.bytes)).unwrap(), + TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key.bytes), enc_ciphertext: to_array(note_input.enc_txt.bytes), out_ciphertext: to_array(note_input.out_txt.bytes)}, + ValueCommitment::from_bytes(&to_array(note_input.cv.bytes)).unwrap(), + Signature::from(to_array(note_input.auth.bytes))); let fvk_array = to_array(fvk_input); let domain = OrchardDomain::for_nullifier(*action.nullifier()); let dec_fvk = FullViewingKey::from_bytes(&fvk_array); diff --git a/package.yaml b/package.yaml index 6072301..649fb1a 100644 --- a/package.yaml +++ b/package.yaml @@ -36,6 +36,7 @@ library: - base58-bytestring - cryptonite - memory + - hexstring pkg-config-dependencies: - rustzcash_wrapper-uninstalled @@ -54,3 +55,4 @@ tests: - text - aeson - haskoin-core + - hexstring diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index bf89874..ea9d37e 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -26,6 +26,7 @@ import C.Zcash ) import Data.Aeson import qualified Data.ByteString as BS +import Data.HexString (HexString(..), toBytes) import Foreign.Rust.Marshall.Variable (withPureBorshVarBuffer) import ZcashHaskell.Types ( DecodedNote(..) @@ -40,8 +41,8 @@ import ZcashHaskell.Utils (decodeBech32) isValidShieldedAddress :: BS.ByteString -> Bool isValidShieldedAddress = rustWrapperIsShielded -getShieldedOutputs :: BS.ByteString -> [BS.ByteString] -getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse t +getShieldedOutputs :: HexString -> [BS.ByteString] +getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t -- | Check if given bytestring is a valid Sapling viewing key isValidSaplingViewingKey :: BS.ByteString -> Bool @@ -76,24 +77,7 @@ instance FromJSON RawTxResponse where c <- obj .: "confirmations" b <- obj .: "blocktime" case o of - Nothing -> - pure $ - RawTxResponse - i - (decodeHexText h) - (getShieldedOutputs (decodeHexText h)) - [] - ht - c - b + Nothing -> pure $ RawTxResponse i h (getShieldedOutputs h) [] ht c b Just o' -> do a <- o' .: "actions" - pure $ - RawTxResponse - i - (decodeHexText h) - (getShieldedOutputs (decodeHexText h)) - a - ht - c - b + pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 098dbf7..2354ca7 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -28,6 +28,7 @@ import Data.Aeson import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C +import Data.HexString import Data.Int import Data.Structured import qualified Data.Text as T @@ -44,7 +45,6 @@ type Seed = C.ByteString -- | A mnemonic phrase used to derive seeds type Phrase = BS.ByteString --- -- | Type to represent data after Bech32 decoding data RawData = RawData { hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding @@ -116,8 +116,8 @@ instance FromJSON BlockResponse where -- | Type to represent response from the `zcashd` RPC `getrawtransaction` data RawTxResponse = RawTxResponse - { rt_id :: T.Text - , rt_hex :: BS.ByteString + { rt_id :: !HexString + , rt_hex :: !HexString , rt_shieldedOutputs :: [BS.ByteString] , rt_orchardActions :: [OrchardAction] , rt_blockheight :: Integer @@ -148,12 +148,12 @@ data TransparentAddress = TransparentAddress -- * Sapling -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ShieldedOutput = ShieldedOutput - { s_cv :: BS.ByteString -- ^ Value commitment to the input note - , s_cmu :: BS.ByteString -- ^ The u-coordinate of the note commitment for the output note - , s_ephKey :: BS.ByteString -- ^ Ephemeral Jubjub public key - , s_encCipherText :: BS.ByteString -- ^ The output note encrypted to the recipient - , s_outCipherText :: BS.ByteString -- ^ A ciphertext enabling the sender to recover the output note - , s_proof :: BS.ByteString -- ^ Zero-knowledge proof using the Sapling Output circuit + { s_cv :: HexString -- ^ Value commitment to the input note + , s_cmu :: HexString -- ^ The u-coordinate of the note commitment for the output note + , s_ephKey :: HexString -- ^ Ephemeral Jubjub public key + , s_encCipherText :: HexString -- ^ The output note encrypted to the recipient + , s_outCipherText :: HexString -- ^ A ciphertext enabling the sender to recover the output note + , s_proof :: HexString -- ^ Zero-knowledge proof using the Sapling Output circuit } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -168,14 +168,7 @@ instance FromJSON ShieldedOutput where encText <- obj .: "encCiphertext" outText <- obj .: "outCiphertext" p <- obj .: "proof" - pure $ - ShieldedOutput - (decodeHexText cv) - (decodeHexText cmu) - (decodeHexText ephKey) - (decodeHexText encText) - (decodeHexText outText) - (decodeHexText p) + pure $ ShieldedOutput cv cmu ephKey encText outText p -- * Orchard -- | Type to represent a Unified Address @@ -211,14 +204,14 @@ data UnifiedFullViewingKey = UnifiedFullViewingKey -- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf) data OrchardAction = OrchardAction - { nf :: BS.ByteString -- ^ The nullifier of the input note - , rk :: BS.ByteString -- ^ The randomized validating key for @auth@ - , cmx :: BS.ByteString -- ^ The x-coordinate of the note commitment for the output note - , eph_key :: BS.ByteString -- ^ An encoding of an ephemeral Pallas public key - , enc_ciphertext :: BS.ByteString -- ^ The output note encrypted to the recipient - , out_ciphertext :: BS.ByteString -- ^ A ciphertext enabling the sender to recover the output note - , cv :: BS.ByteString -- ^ A value commitment to the net value of the input note minus the output note - , auth :: BS.ByteString -- ^ A signature authorizing the spend in this Action + { nf :: HexString -- ^ The nullifier of the input note + , rk :: HexString -- ^ The randomized validating key for @auth@ + , cmx :: HexString -- ^ The x-coordinate of the note commitment for the output note + , eph_key :: HexString -- ^ An encoding of an ephemeral Pallas public key + , enc_ciphertext :: HexString -- ^ The output note encrypted to the recipient + , out_ciphertext :: HexString -- ^ A ciphertext enabling the sender to recover the output note + , cv :: HexString -- ^ A value commitment to the net value of the input note minus the output note + , auth :: HexString -- ^ A signature authorizing the spend in this Action } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -235,16 +228,7 @@ instance FromJSON OrchardAction where outText <- obj .: "outCiphertext" cval <- obj .: "cv" a <- obj .: "spendAuthSig" - pure $ - OrchardAction - (decodeHexText n) - (decodeHexText r) - (decodeHexText c) - (decodeHexText ephKey) - (decodeHexText encText) - (decodeHexText outText) - (decodeHexText cval) - (decodeHexText a) + pure $ OrchardAction n r c ephKey encText outText cval a -- | Type to represent a decoded note data DecodedNote = DecodedNote diff --git a/stack.yaml b/stack.yaml index 45d827d..ab6c7c0 100644 --- a/stack.yaml +++ b/stack.yaml @@ -40,6 +40,8 @@ extra-deps: commit: 787c2e813eb3a5d16c375d4b37dfefbd2adcdf05 - git: https://github.com/well-typed/borsh.git commit: d2fcfa159e0a844b1ec5e8ed3e232d4b380fa831 + - git: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git + commit: fe2df6f7d63272ac147911c1573550bed1d38a37 - vector-0.13.0.0@sha256:fa5cac81a17a5af388716792e8b99c24b3b66770086756d0d8b23f8272a0244c,9112 - aeson-2.1.2.1@sha256:f10f3c661bd5cf57aee46b94420e47736240b8e209ac15f4bfc1a4e4d55831fa,6344 - generically-0.1.1 diff --git a/test/Spec.hs b/test/Spec.hs index a360e77..c5aafdb 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -24,6 +24,7 @@ import Data.Bool (Bool(True)) import qualified Data.ByteString as BS import Data.Either (isRight) import Data.Foldable (sequenceA_) +import Data.HexString import Data.Maybe import qualified Data.Text as T import qualified Data.Text.Encoding as E @@ -283,7 +284,7 @@ main = do case t of Left s -> s `shouldBe` "" Right x -> - rt_id x `shouldBe` + toText (rt_id x) `shouldBe` "5242b51f22a7d6fe9dee237137271cde704d306a5fff6a862bffaebb6f0e7e56" describe "Seeds" $ do it "generate seed phrase" $ do @@ -370,7 +371,7 @@ main = do let rawKey = decodeBech32 svk let badKey = decodeBech32 badvk let rawTx = - decodeHexText + fromText "050000800a27a726b4d0d6c200000000ff8e210000000001146cc65bd6d252d09b8eb0a8ab0aab6d7a798325aefc1d3032fc6d31373a85a25a3a16b447a698f720ade1bc290a74d85574b5b20515391035a67f8d5883dc65ea3ba4a17b009d6f325d41072b3ce240270959a7ffd040e5f16c697d8148973c62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d23301c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668f0d8ffffffffffff9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900c734b62e56792f6c8bed48e4f108a817e83d64d6a59e38cfdb55c0f8a89bc7507c89326266f7ac03a3941f448cb879bd792bb116d0be8876c0856a76ddec0f0c02e16f0338626013ee5f6037fc6a3c69fa291204039d04d17c11295ee3024aea8f5d381e9b7eb3f938b6f9182bf4f889f1e53e30f998b1cdd23f45cfaa20aaef058248cc2e1c487fcdf54a4bc22a68a17cb6fa7b2fbf333b99feb84643d321398b675634929602126b2fb40171e514769bf82f18c267ce9cda0c24300caa9a5a361144d3b7b9ab2243ee9811d9b2e72c8bb1d145cdfcf6b29994a969b41c47208f5dba8d6d871e490e9b970afec4d8bca40ba51825cdc78cc7cde6b6f235a4105b1d1b5e2765efd753095ce770f070b02cce3316721b9345680c146c2f428c0bbca90d5a8cd0a1c4c31cbfa8ec165ea9f9c71d2d05e3cf8bae5e779786f179c45a3cd8087d820cae812aded04f8acda9068af80ea834f79f1bd03bfd66f8a19074649a85ce877df1a621a867debb423ec0d19015b326fcf6f143aba34029c1da2fc7b099378a366c38c9609ef6a9d9e175e21b0c1ab94a84e28ee7f1a00e39cb6fb59f44e4567e9f85f8f98158263c52ec433c042397c784edb07c28d2bca036f59090e819157375d610acb1993a4107b48da13a371f5383429baee209b2c0cc150fcef79a042749668ba1f89ad24a8c746142191ed0e8fd63624a331d98d50daa84ccf9043076947cf5115b9f8787acd36000c5e72c8d783b29bb28a3e46036d0a592ce8a158ee5a7ac210be72d3a6185c13645d96a8446021b64043ab8b589a20091c152e7d5a993ba94770eea988e289e1536d0d81dbc7046ca9c6d918446bf970894f073c920006681ccf6d1a3f138519c68eba0296069e42dc60f2bcd0f17c400efe4f4e87de8606606dc4fdf31494df4d454d14a440b1d9db4265c7aa9bc8683c68cb149f2cc826427575e2af82e842199a9cb9fdc7243b3bc12f1a71c37eac5cf88ba830cb95728897fa4c177a290d6b2b3814173262da14db9b4ef39fc54f888a6ffef4221ae672fb03bc78ebef479360a682ddb12ea0369a428a6c2960ff8327e9a2f5e5d98ce1eae748db8f6a4631c789b4d751d6b99c97c149a813998d44a7b57ba06c8bcb8a6c73c6388cdcfeb1346cec8fee7bdebf2a2388d9722183eb2d2e0e183cdd092152ef640880f4514f3c5e836cc3a8249413500630aa8da85f9e3cd92bdadbb69a2bab8d71f0b3ec5832a7ddbddd67b34c33b2e12a0c8468e852e4a8f7df45657e9632088aa7c6c5048a2686019cfec33b27fc88e23759938dd55a5dff589c1c21a37da617609e9d8be37dbf9bd6e84ee160fe10268171d969e4611afe9d3482ed4b132dcdd11ee516f36d512a333da20266fd984caebf4937fdfd18ed07b4a45771cf5c8c16c6b258b289a07d136a22acc766011f366c420bafb8fc1a10e42219bede5a3d1166c525491ab60bbd1f973fd3fb2e94cea888e24d5fb0adce51faeda75d62de70094d4b36d38d03cd824d284fad577c3ead4d98bcc8ceccd18174a889b22380bfcc12656e764ea0b8fe1409971283008ed02cbef89d6f544c62c3b001bfe96723fda9190deecba534d69cfa358036fdaf16127b89f925c52d4e750919ffb7182b6a8ad13d0a8e00e0b906978dd24ee11869c1a63837a80e46e1216e2e273aba07aa5b0d97558db0ba7f9ac4c89403c65f1719394e479311f5cf84746e6be6f1abcac03194aa8bf1735811198b5df90dd6cac345779c185c24beda0101b932048dc4144af664a63acc0c395052882ee1f18bd0ddf13bb583861923bc00ed5ae815b964698ca097eda1c4281e039139fa3091890244f926cc4ab773ca8a35d5263d3bb48fd6ac53a4bb4d7d60b36446dbc714c35b5e13a17c5b0c70f67207839d1f7404604aff63b2fa83a4da7dac92aac96b3f250412f8d04a9e298004313b02edefd076c67d8a1316355777814e2e1ab03690e426b672d32ff65c03c592ecce6a70e34fea2e15b9a6b4fd092d027199caf27e84e25c09380b38a5eb8985355b3259aa1d94be74269b84f953053b02ba3be9df872ae5fb2d893188575bdfe222ba267b5461a0d0be274a7d9e6ee51490d98e4cd97978804c4f0f8e9f4908fd8c102b01080f5a02b7578591e95d60f3f56d8e48514b1ce7ea6894f55a32c8ac8564985d18c6b82f8dcde5b315624e9321bdd49dd350c87907cc373c0238a79321e6250e38a0ceb2c060ecee6708c11cb30a49687da9923bcdf011f9aca27e6eb5a8477a2bae2dcff9884cc2349b51a66b5179ed2d8f69e4bbba74c694194e83d04a8566228227eb732a95180c6788483d1f259d52c52fe43357656d50a1cf2902c3124d60d15fc85f0447a1203f824c1106452cfec1c92b18de003f82a0000000000001cbd27436a221a53d08c4838831d1bc60ff7e93df41a51412ef6096eec98bb28fd601c53a5371b23a497062635b5cdde715c23840d37f1cf328f0a2ba96260357689ae3f84a80dbdca1520df68513be1285177d3c0da664c64944de78d8b8d5864f5ac15444cd3204adc4fe487503066c18fbbef8d0515248b0a97577f5aea1d255788ed4bb66d4d56303efe135063392c312b4671963daa20e0ade262984e11263a1588eba3cf829e6131ab506e6a850aacce603e8ecfd6e794c90a772603d80fd2aad6027b34854072a0d23079252adb1ba637bbc650ed4afd35d977e1498d998020bc1c814718b48ba7378a92c56827d3c2f20daa231fa51f0a9188520e2a11149e162489f0d6dbd27cf94fd5775311d3dfbcfeb431bafc3515bbb8c4ba4488c320dca0dfec548fe9f46d8810b3f6b16bb3e3eb0ea130747d3d127c5953ca8d561f8d425a35dc3f2cd831743139fbdcada42308b524313782e23b32d5d54a265eae408623e3b2779fe60e13cf47d54dfe520f9f4e57c68aed31f78629a9074d72ab87bea993a38f95ab40df3ef01735e7d44ad365a786e0d3032f1c1dc4e6839c974185dbe63f8725e79831ebe269f94c96705639ab38d5d0700da04c6a9f686e1ea13391885287ba43cf3ccef1c2227918f15ed55441c45adca84153530bbfea3cf37adbf84831a2bfcbf0ca4a4bbd90e623789fe993dc17503ec11b1ef3049f27b27ff778af364d634a46165cda1dd8241cb88740bce74a73e7e3d656df2dee05bb561a85e64671b191ec802c5bfaca49b8168e44271cf13df756395896ff41a99654f55b6951f20d04b2007938a420218db8e37445ef3267130e288e3270b13a92596a26043e1ae84f3934cdb13363bc2843f74a0f6608a36b52c985132aa427c56b7275a864b3c76502c37b8abb8d0286b3199c78492ba8103f5a23c6cdca2292c75d7d6d7080108850807f78af3dc7e418371c6b8951bd89b79fa586af4e16096b08ac1f4dc2b1e4feaa5c040bb002b57311523197b6e2bef5b79ac9c9b4a339be6f6bf7fbe9b5c93862c87be6647949c70bb2c7e268e2ab39cbadd69de628376b3af744eeabc85b599bbdd09defacefa443e05c9b5f259a7783743fecf1a749c57cacc85703269ed67db1d8d475f6fe25d66f84a77379411ba123d98fcb3ae4eec306489a08372893616a91268ea6bf34ddbf0fdef1360ab9e82f4ac80a24e41f439af06fadc223c61f445b7261eda5e1320e269d1277631ee2245cf930244bf8c04050c514e2d59035b80827586cbfeb7da7a59c1208aa86390b9dc7a9b6ef38879ba4deea5eef47c5c98d9167594cd730abdfaf082090efe759d1b13199d739c112ae324ba24b275bf1d89867b81f4580a7ea3a8d3d07b45e2de6c1c7099de3606873b13f3083ecd1e84456c9a1b1d358075c68b1a7cf0b1f26031a2909e226f5da7877d0085b879165ec4b1d9abb7b0732ab4a6f22d9a7bbd0d494ef3f9af4903dc733fe92c6b2f557d1406d223a93e8ad6e579ebcde9c39a5652ad31335df924e5b6a09a0191821b4a0c8f886e2d7860b75ae79ad9dfbebf3500c8b9762dcd131eb5c8b866b5efb4fbfdcc5e31605c2b7d2ff8db5198a6c41bcf880065ff232ff8f84ca3f8022d3428359dc9fb19f57a6ad3f3d174d8a348879a754b37095f01d9a7f6f873798b97dfc5d7c7eaf0383b3fdccdcc11b30dbb3a0fe3186a36c4ddc9674624e38a81cca60a9bbc1b124021b61a383b7547d6af187022c133ba9d6dadf711a3af3b0255b859b214ef6c5dec592248fc94339a64f19196ca0fdad80f7f8e3d78b1f783b1f038008d0d106bd86e23e33ae5728872d42a555bb36d0e3303f0b4ab41180f4251590ee3ee244b77191c31b9f3f990f71c6e237b9dbcf7ca21c9b4c2446b856c67861785bb9edb920b8f530a7a088313ef044419a879f26db137e1557d079315844f9f60bae03d8cffa7a28bd2857a001fd5d2d999fca95ff91df0e228567f6c9ff592b77b7ccdc93a951f7e34910361a8f4fb517e1c9fb956a3bb50ddf37ed37e8d26adfc0f71e059ba95ec77a1e34e1b3420c893f89b79fc72e3a1d864fd35526727f939badc29740f5ef9c0bb9a3f72e1e08b2ef2ab366f80d8e14e03e92162736e2ccb7cde82b2af08de8a6a81c03c63e2396974ec29fb122d818a2d2d5b29d11b704d3ac3b39431099b7b2f6aae04d28a2182b55380503127e4986ee9e8d5c0c2058e09e4592d08d013a4f088e45403720160622236bd56fbd9cc240efceb1b23c19ceafec49e9d5776ca9da5f7810ac979cf6ec5f678c09257abf79c9ab55dea00054e11b62c0a0ed4363d0a96a37ae1a323aa93bb1af253885afb684ed30caf5cf3b37afe6a6463a16f34cc28b4530c6bc6281f597bea476cd9a773205b96d47ed4b0bfeeee39b7ea44ff194911bcddbb161c2c0ce9488978b99e880d8e43624dbb4a567483ed293348d752634b2f46219575175e3c249b8e4e853142b66491aa1c142e7bb558955747cf2bd61ac802a2a4784d9fe080f771dd537d0ff928b3b04029d9ac03175c2d535ad7f3c123eff30c0437b32dd9fa31b2976e369b89b79a2a95e31aee15a462c5fe25fd937ce6b0795808d16163f2cea8f19b7c83913cb4a793576aa5c0ddfb6415326d8f2be0621017616c85ee46aa768b077dbec72311cb8a0f78ba0621a277a79af6607063839a52c6825a20e1c5818b24628862aff5fbbfe87311866b9956eeea7412ef69a3e4da84699b8d8b45c74ac96c3356989fd4962ba79fc26a92488304fd9f42486266b433eeb57368d26ae91ba4e7ed812581f790314cc7f44639aa7f6775618111369e8e2d68a6ab24388824bbbe3c3b0fbfb88635c1fc3216af1af40eba3555c0b0390a18ccba9e68afbcd21ecb212aaf82846f0a55793945902c48cac5d19332e23248a464529f4cca177137c508b6b13637e7df523254f24b8343d19164174202bcb00d5fe618b760c374f69b5065b1f9acf91af95abd7eb271586cae14fc835f633aacb4cd2ecd0f0ac08b688ef4d13b8a7b4c487ad46485bde0e340309672dc38af275e6ab525971409a39eff0ad134b1674db5d1f9725874e36d8730dc034b0a596c6e0a26e521c199d3e3f86815a64d148ffc394290b19f15390934b5d0da27dc8365360511628b93ebc375d2a531e4f4cfa031eeb501afe96d201c7b6078bbceb8e5d8615599f4c613bbb81a88f4eeaa57a9c008125073a30154044c422eaf2a32cecb15aab0774bb44e52b1792d154b8036ff9224af53e023fc011ab251d47d7d76e55c5015db1926d43c56d055feca1259de53ce98a2faecb5843ce17a3e83ccf678f5d13a8321f278a670c684e62b720e1eefd0abb2e9d0a3ddea81d5eb6e380a1c22af5587daac852e93a86f5e4293c18bb26b32035c7e5ca20cd2e3eabd3de0e55092a79a42c7ccc0aef033d6683043c2a29531a2ef1f503595c0e464ac042153c4685b062275f88bab93cc017f1ef9dc6f8aeab7b0b234f1c543420324512554f1786c82b37836238a4dfdf86f97d09ae466eb39d5f3ab159e2060be309d1284be133ab40abd61468c1706f7f9e57a7bc747479693a03edc8863dd196fd7cb2721260e42f4f606389c4972c74d357e7467b61cb7f455562015d29a59c7cafe0df03f26b77bca81c2bbac8cbccf8a65190b0c4e5ca832e82ce4e11044433aa397106cafc05634ad778270d20d8a13068586bc6b582ea24524fd921a5ee22dbae5296ee86d80f12b78bba26f8b42c1401b75d5cfcc4775c5cd1cb0a9248dabc8f82d216787b2a2780f7d13a5ee8c6ab56399a8dd5db3a152677d01eb8ee98d1927ca5069e0d1ba3907971a2199ba3634b48e570dd97d93729a6c43e4f359e2d89795218d52270a338a1f511b1f008cd04553c1a89caf987fd18c329be7ac2282084ef1789615d7eb7afd2261f606d3953b8863abe57796289e7761b01c3ca0faf2291287f9ad7027d7f0876b5f77d2a7f87dfa6ad4db905d4bcad042f403824aee3b4f8d7b5ab027fe1eda9d683db24f56f694b0b10ec72ba0df40bdd6e52b4a7b8d064ad46c7490c4705c14b06ef55222435d2d6316c7dfee83d225eaa431c11a4b85b0bafbe66fda1abadaec8eabcc2f8c688a7b9cb2942597f20cfafd7520892c535bbf6359a6989a84dd89989d95b8e5222c3aca2e8b0f8881d759e450466b75b5f36b7b723b0a212edf52abd591e7e545a3974b8b31b84b523af7b47e3804b5a268c86ae0bd7c80bc6b578b79f749eccdb4a00813925ef40259ac10bbd0fc4f2fc536c30f7c1efe68a52bee22f57021d23f445211b36fee6302202c9b62c6cf9064a2df424563f9f805e51c4092482253e4c258d53b80a2d26eec9fcdc104f7124d876a3ad573a7f419b0d67a41a34dac9d8f28cf9519b9c2677c9d1e720667d5fce26091d64c6df8b46c98b58017de0d055651e8caa3a230f57aef214ae2f27fa85400e34ce7087538fb6b854a6ad534780052210b8b8c90b4de4c2afbad9f58a71770ec186cfa44b61b53876bf904972078673845ec3181caaccf11f71a8e2a502deaf144f16df1df3bd81277dfbf6e1ae5a17363725ec31759b743066fbd4cafb5eda6e09418bc375f42e0dcbd4624dbe26c5fabb77152124f400e677004fcf3c862a9b5576140cbeb800fcde4409caae06286cb643842687bc6b89738374c7c759c911d7bbec8613f1fdd996d4b970fb6f2a9290a84c34d5fea6b8006357c8e6e9d4048d5a8ec476dab0b55e8358ecf7d27c31da86681f3fa74d072b1150223eaa21c4027378a99c8a2dbdc07d4062c401e92eddfff82841292bda2798c4e2ee9e09f618cc181c91a4dbefa44c410dff5cb705ee005e3a0470c13baddf9066109797a3b51e73a0ef229796d330aee0c0160529a4ca3b39e861ee5c4a0f78619007ecdf32266c7c42f0c972b91cccc45f6f8688b2692f298721cb5da39cbca9a5adef6969a2592ba421680241f8a5384bb92e70acf79c2f41d1171dfc6e1939887b9c8ea94429bcbf3532919fdcfcd0f443d3c95515b41e3f9c84bdcd3de1fd481f98482f667f2d017e3579208341e9a225f85516c8ca133cbada77598b6f596e6151eca377fcc8029cc99a879b26d975684173c0874509117ece4136bd2d69848f858c05e8ba3421499d7fb5e3e7645fc135117d8fdd1dc46bebaedfbad4dc7cc23fad6e696fe349712cf7579b4e63b38cc7d02a4c6a33ee4117d7ccdb86ea02cd791756b2a3c516d59d39ed83a8c328823f1934731820c187624219b487ca86edcc2f61a064e4e8d17f58f4a71462f3f0cfef6be95c0eb3737616de5954096d761a51534b36d798c651541acfe2e5bed6e58c45c46e014923a342409d49e782054f2a4877332e0adb0663dce0e84df0ff0d71c4c5ef18d9cacc8b8d47c78d53fc7649bab719334601c79a345d2101e65d4c3f1616741b24fdd9f7d6569fb8de67799648b323856c2ca96ac91823aa12249934c7050505258c43763ac9b174d55fee71c7817d65056c30d7853500656040621eff9f291e9db198772451cd3d58bc9421f6191a863777edf49125543b22ba127ea3063e1989bc4635d50bb22270948ba594b2066d91e1589aa0c1f476af8b170820ae0d2409df83819579621161ba55cc8d020e1ea68cedbccc9a737746d824b06e12cd2e3730260fdc52a68b6e142bdb997d8f93f25d241360285e7372939828fd54c015ba90bb4d553558ffa2558a43098644357860785addc455bf1ef4ffa5aebb8f002eb0f6a8ddd6f45edcc7cfe88d7ae7141b9113a4ab851b5fa8ff39c7024d2b3202841266f256b1f4ce4e4ea8b83d0067555ddd56fb1f8ed6cb5ce7340cdc403fac1dd3b48c373629202c70f1f95c0001b4f94a9b4bad9e1c64429b541ff57a2f2ea0c1fb2af8f0e490fac415cc3bcdd5739f2069e2bfb873206b8c8811619f1db39386cd2dd748c6301239b4e873f3d4618a43c52ef7d8abc0ca127881dc8809dc20a8730718c64c5a132cf638ec1e3b3cedaae55763637b4c6341baeff17e14c1e3c34e5d23befdddec0c7e25f0ebfa90a0bca07a0f5446507b7fb9cf91cb3cb8d24a111f46e7dbe3ea62b384eebecaf8de49b15b17f9cf151c4c3d33220b3adcf9f87222813ff2125120e77c638ab19c1daf9631d53b3efee67c3c40c9c23495848d4c5ecafc0998bf2704bb78ade666f414e9c6bb100b90451d93397d062b741a3ef20e7e6ebebab8238e2adf415775179c866eb4a2f628924674b20f711d2515d3b024e7e0fd3af6e116cfb0030709fb9bed4a441646c03dfa0b11cb460415ccb3c0999b738a0b07d55be741f3d2646a3d9e22bbfeb46b87acb6fcc2d83cdf44b656d2269a0734259a738d83a7429bfc99da7238b9497fbb10e0f623dc1f793e3be2326a7e55677e20ebb152f71794fd70d7698d0cff1334341b3a8eee38d66a021cae6f8dfe67913fdeb8d46efd9b77f28950aee7ce112f8f0dfc04132a99d95dfa923d391e06a578056ec8d15ddfb0fe8cd2790ed55f8254c6351d471c7273c2e61c2694bf6b0a1173cdc2d422246ca5cc51a9b8669dc331fe33bfe330d5167a244cf041f8444775562eaa8ddb92e0623d4c689db0919ece87a7feeb1ddd2d034a0d884d6bc8b5fa5fc7df00cf667dfc82c9e98e9b57fbe07fed391729034c467b46d3d16ed72c8ded069a117ca2d28e71ef4c37f1b1939590af19433ff2a784ff38134aacc59719673c53ed047d79e0c36f3acc03f64458826107853ff8d02de9362ffada79feabc597d019de6afb6577dbbefcaeaee801a49b1718151690b43a4d70b2fcb73ece3e3ea5fe2a9616cc6a9a494b8c6c7121497688652eb854e7bae19af3e5ae1a8df75138ce18621d11f15054ac8971f6ac084bd90e3909b46db321916c9e3c8950f3cc8e0a10dd255bd6b359c73dfde7de6e01538d03609253968177af14d327337bc1bc4e866c75ab5b69125f9e9a758f061418110a615b97f733f899382e2a14fa0ff0d1bda3b7449e1f54362697aef064fc9dd50692ad2b9ec286a1b7c1f845b09f4b1ccd46142f9e272d018eabf76c23c26b1831762a8a2c9152ef0cc843ebba20bf979e851e76141f41e817cca0ac60c1baf239a6c920a244b0be031a3b65d76eebae15bd9d49c53dd3e4303ca379f26840901f1da886d330d8ab4408a25eaa280ddb545b968b4aa59ca6f9cecbf82984264a3232b97498be0729f3d97dbd64823393b42794ec1676244212af382c37d53d66019a5db34c3d8ac156cac852ecb1c57f0f6057815460eb9f666c368acca6dfee0d2b7bdacd2148d74cea3bbac8b528f00809f403b1964b9d199d2db93363a53732efd97c66f28cb51442dbfb1c912dce078944e02889f9395ff2655e2d7118a11848e3ca64efb5eca119240b537b5d4548a744d96962e9b2a99af73bc4d753df16fe75a08005da7773b7bef68a44f56951a3a93995c5d0516a8b2c39abd2057b3d86ba7fe511dc33292596fcc4d3c67df88812e26d3628527752a81c8ce6eba711ae7ea8748229caa2580e18937f45cbfb0041f0a9254eade41c95ef4f390db6174e6b0ba89af344c215393b627348ebf00901e343c1599c832bd2cfb751750808719f18119ab3fee6baf5fd4b8bf7c92a271ff24576984610abaf1b2297c18a2784f57d7aad26f52c0d60fa6cd2484492a1524dd14924eebf53348ee2dca0643ef47381541b91d1843817067a94bb7c79e4e08c4d81c7266afefeda4a0db21b0db629cedf13b4f6ba11e6480116a7c9780009ee0e1e2bd543dbb68bea356a6010a1e3ded4229c4d8035420833fc83337dce4bc483eec76f479f33be2d6a8fb013813952eb66bc247f37f9def7e9bea0f277f380079aeb48c192b9c2c24f909435586bcb994ae6f75eea111c8842b855f90df21e956f839aef89bb1b96899e343cc66814ac08eed4defe5e2a62c54cf83a27f0499224e02bac652aebe6529143372c483edcdbfbdd5a02106fd3651875f23724d2154c0138ace745d038d6cf6b233ae2bbb8313c8ab59dc896844da5a6610845d9147e57518ada61b7debcd66111d609587632cccb936751d099639735f09f77b2b9aa1177e95ac0f8070bcbeb01bbfb3e7a638811d919016ed628865e2405aa17f5129ea43a836a76ec7cbd943e20b1e388d47db2df3394785e27dd7efbe350ccec6057af7483303a6a588d4005c05ff7f3c90dc5b9a0f95c3c541ea40615f6b0ed6cd3787e608e7612b52199062991a63511b3055dfab25b18ab55948d9d529aff91a0d8aa7705afc187e32d1107c07623a309390f32c62e1abb830bb090a7dc4c6817b554720fce354563662d78cc2bcd0483149b304ab86502f0ad36ba2c262fa60b1b84be2499e56fd72129c0fdf1961214df8dd6c9b831109fadc7464df090aac96d221ab387b6eee3080ea0d94e195ca795b5d80c42946c0c353e39075bb1aa4f3fac36a77445cd57b11b3c7ef0e89aca911be35fbb72e1e2fff84b7ece66bb8b3dea52b0aff473475d298f730e79ff77e955856817514908baaec1b53f39f9d5dcbb1cef29536a483a1845d8538e5ec43f634914bae4f2319579adcf3f0fbb151c80b54f02be54be612f9417f916579863dc16da5af07fab05db2094c77612b445b7b9784198abbfc8387486449969118d3a83142022017d69b939d30fd6b1c5ba360c0e37f264b5d604c7ebdaf327874b22d4092dcf35aad93f0f0b3037f90000d4a1892dab827d3c2f1ddede948d330706ef17bf442f8228c168401541ea89a837f81cfffde8480e1b6014f2f5d3132ee740e04622600ad93dba41e39267f17554b29102390f8196094de19e8d966d3c03a9e3348494b54045629224612734a1304ed76d17a465c305675bf99b7aff1950dd2012e6cbe8d15a9e35a7c16081bf647e2766c7b8e9d12ef8a2c94fd7531f6d1a7ac7ce6bac028917a2a71c8eded4f3ce3be8c7d98422094e88414338fc25839a582b5965c71179ce3ddf7a645bb163f3265f4bc4ebe91ff25d86520696d237741ba73084070e23494d6a890e1ae748091a6936a3137d16dd14571f86ee118bcb17c8ecd2ed20c25cbab79a78c53fba1d5db4fdb15e6a124bdb2d41fef080e7318f78c67451beff04b58a182f592fec3feab6011be4663dca8b62ad2a8e40c716b3c8a170e1574517c44e146109d223529b0de480aa168b5a4b0522cea03dd26a684b08c58aa2e1fe41f44ba8837737a74a4681b0508d9604aa7484c5ec68ccb369c056702b03f75722033597ce5b55e5a88f14e3ee23b1c96f5d38604128cf2e901a745996f4a29d412e5ce1b4ee82cf2f248aa80f8c8f2eb7b6951f1b0910857a17edc319bc76b1a59fe05f9394a5b16a91ef15afba9ee2e115c9c9afc51a8d7c4a61cb335724855007f564fec341342d53717023118917304c2dfcf0bd79e456c94718b8388767564d6055aad1d505f36506fd6e8cb1a10f6fe8a67e772030dab78b43ec15fae0322fe991944be86cd0bd78ba00110474dec84dd9597a602c4a6c97a27f13159ff1e32fa63e28f63278b09c9d051cabf8bb49f2780d4c33ca061ff16c6288ff29d46a22e31c719dfaf6087590ea8197c066f9057e0c24b40f5eb205b730985daaf7e7d85fecd7449677c0ec1abd7111d795de6de4a6ab30cf266987dd55569f2a705ae16f51de738fcc4cc218f2cb9dab8af509ba26ed93ddc7a73028d9187527fa6d554b491faade05852eb50847affb81b58d2501989d5b4a9de4237b33bea154c4d016b08f3e1c39ee61382fb500be2123ccfcd30e37c88979a19d636c9dcf5fcec822e5558cb2939e40e1af039aa942b503c9fce95f8ff51a2977f58e4d5032f9099fb4783b7764dd1299a1a89f21c1f5e38038f219e9a4e9d6c6edaef70132ee1f2a580227b256c9139d339183e278277a9dd260bf7d8a9853c4185f9b6509ab9b67a3563ed0b9afca6f2db28258025d8c2036cc4cd9fb4f7007dca96597c26476def29191cb0c77c44df80994ad3c79833820ce2f1108d17a083fa857397d2839c37899ce2e634578b3eee0135f66cfe1aeb7bcb8732fedba16093a6e666321236f56e285058d00c1bba2db3263844e4b0ea013e7a8d370ecae9ac7a9620eaaa244b25be1eb6e71368f550736d3ca13747edfb6254c9a373aadcc30a148907d39e81bdda8a53e8644b39a1625de851fc5d759686c63c76bb162b244415175eb0ccaf3a63d4ad910369e30313bdeb175ace63ad33e400e642dbdcb472a336d82d2f818e6888e5aef472d03fd298a5d14b8da432b72e6c5f5694887a7e4e165bc41055379bd204298d65796d5351a9121f949904bd6c87510649127ef5797d432c6c759939c99026d57076db225d7d0df01ab9b9e3e1221e7e323ff752eb7988cd2b2bbfc8ae7fdd23c3f7cfd2ae2679014e986e3c01b6c66a67fbf69483ad3e89c1f2dd0867f411151e048d81212c0b5e2fbba9d317f283f97c9576180121f41856355b906a22c69869bc27729db5974c5b1257b35d186dcb4e9f7bc51dc331c0ff9baed5955e5052a6ac7743b" let x = getShieldedOutputs rawTx describe "extract Shielded Output bytes" $ do @@ -391,39 +392,39 @@ main = do let res = decodeUfvk uvk let a = OrchardAction - (decodeHexText + (fromText "248b16d98dfa33f7ba69a0610a63b606699da76c288840b81d7691ee42764416") - (decodeHexText + (fromText "17fcc27cce560733edaf91439a8020c4a029a4e7d5893ce024d5ff4b40bbd0a9") - (decodeHexText + (fromText "34796d541864832acca43f083892e98a46c912802d5643672d3f25bea177c61c") - (decodeHexText + (fromText "a6d2ca10e3fc7446e372266ef45ee3dc0ba373bd378e6bf3092519a7f272bd8c") - (decodeHexText + (fromText "08beafdf59110b5d045e4acc13731ef1a27bfa3a9cabe1d575640c18f18ee6697fbb132d36e982ae3eadf5f37fd35f42c2bb07def14759deab1fbe2f98dc1d5913e4a6ef388b714e2cfd6d89ba2302800e02ab5f45e0e02e3895448518cd8afd2c37bb48a66d8b988a37de9d0838d92876894a311bb9f314ba842e5c18ff7a3d8c7f0ff1a7209e2d661595db8f4a4aa267b9593258914bf63c09286eeda7c9b27ddbb4646208c0d03a8fbdc5d96633335a5a65316f5b25189bdce735bdea7e900de56d3b475ae51b7c35eb7ae79ba104baeb0a5a09d1cd8bb347ab34fb26d62ddbf024f5394710626ec0a665b9c917e65b00256db635145164a0329db7bc5358f435d573b2662adf8a6128801825ec8fb7d8aeef567d35c875ddd784fceb7620355e3f056a648b39b4b2d29a1f5e7b7c4ec5fd2b1874ff1e832b308f8644e83878d90582b9a6fd6c293e19dd3e24dbe1b4c15c96608169843d46551900a8cb787b15f0f1696b736dd4c8ebacf1e3288b14e469bdc004fa8557d6b1395700eaba59334906bb012f876e4cd7acd2157719ebd2e28bd0cd4ab4ac458f8848e1c30e729803dd47102200fe703932a15c3618862ec83b40d3aa0ec2343641bcb9afbf931ab21aa4afdbe7e51deca24283c2ccab0eef6e07aac5a4bf3a775bf7d2ddfc8d8766c3bf8e35df1435cf515d93c3b9549477bd9f53d133f05dd256fbcc0b13a63e3e7f8cce6301ab4f19c114f5af079f8c581537458e861b553218a890ea3e77fb99781c7088cd43c67c155ec611c1148721cab5fd0168e4a5ec390b506ec44145474c") - (decodeHexText + (fromText "1e40d33446d9f0f0fad40f8829c1ffe860c11c3439e2c15d37c6c40282f9e933dc01798c800e6c92edb4d20478b92559510eda67f3855f68f5ab22ca31e1885c7fa9d4c9ebfb62ceb5e73267bcad0ba7") - (decodeHexText + (fromText "63d0d6e8e92691f700bf8af246dcd4ae1041b13e3969f7a9d819a06e0f9429bc") - (decodeHexText + (fromText "fe362be160accf2794841c244e8d80bbeb80b9bc95bb653d297a98d32bddf5a05dd5f874891d55924a83f722f75f576f63796770c31074067694cffb2cce7a2a") let b = OrchardAction - (decodeHexText + (fromText "8921446787f1bd28fa0e4cc5c945ea7fc71165a25f40cd2a325dae0c4467d12c") - (decodeHexText + (fromText "240b08b7861aa78989c68cbedd0038af9b3e3456bdc7ff582d597df571d54da2") - (decodeHexText + (fromText "e1bc8ccba69ab9f429bf735417aa005cf439d27500b0d3086dbf1be764b42a36") - (decodeHexText + (fromText "c89c58ef8553e7d09ba4090654edd1a8c98763c44d3dfb9dad18286c7ef363ae") - (decodeHexText + (fromText "0eee1ca5a3a4959cd4b8bc277e6e633f950680c4acb978c14ad8d944a784f46771c9d666a203ca3ac693943d79dd23f8b76a734a62e81932cbe98e8c851f47a11aaef50249e53151f38f88262a4bae8cf26f5f8b2db1d165aff9b57b64713a677c167608585c038e34ca7bbe468e5f86475ccec0a4a8b9a43b56e342e77a6bd09415787c9f4a1c6f20599f57545f1ac32c3a338d7a5bb2d35456adb880cb65c1455969e10df5d94b8c74b244e7093b1a88cc10697a7c2f4d34b6eae3296e64b820573b4d52e06b4427af5b8f5d6722d3a93fd85da615fceac732976ad2c1be4150b4821c149521f5419ea0746fb132d47f593cfc8a3aab6b2b4480c12fadf21280ccd3142e7188d9e5aef3fcd8c5dc0c066dc975bead023ef7f89a486b615b146110ae68b703a8349a5fc225b26a08b2adaf36fb44c9ad1be59d7ced134eb84e3f0b4aec19b71b2d26e910628a11446b97c5e6bbf97e1befa4e04b5947f83c65161b92f58088d28e57adc2a2873e27008e29772c5803502842045cb355d1ea5a9d27c2683dcb38cb49d26af39625ba99b1342f700387b939e7ff6c129417ca8836fe1e96331e35c8bc0763879e8c17cd4535fbcb27a2785c0a47294e07cb54837bb997df34882ce0bececc6adca365c76fc7533cf0503458937dcfb6058b016dbbd399b9f0cca44cbc881016f4957b5e10daada3393d5b2a4cb15ed983506d4d264f9855ce2ef87a7d4a1fc03293a22c28a53c4455447d546813fa33008e5d2d81848825fae2f437ab9575ba99c230e78f4b23e575e7647beff0e4c4e2b0a1f7320e9460") - (decodeHexText + (fromText "d727aeec27bb0f7463c6ed4f5b3f4085cfd3e7218478db0dcebfca875e025320fb64bc4062251823859e963446cadd9924c559e5f981480df5a4f036daf5a8033d4c8241e128902aa1aeaf6adc149730") - (decodeHexText + (fromText "98e72813aeb6ea05347798e35379bc881d9cf2b37d38850496ee956fbecd8eab") - (decodeHexText + (fromText "cb9926f519041343c957a74f2f67900ed3d250c4dbcd26b9e2addd5247b841a9fde2219d2ef8c9ae8145fecc7792ca6770830c58c95648087f3c8a0a69369402") let decryptedNote = (`decryptOrchardAction` a) =<< res let decryptedNote2 = (`decryptOrchardAction` b) =<< res diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 5b53120..1df1725 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -48,6 +48,7 @@ library , cryptonite , foreign-rust , generics-sop + , hexstring , http-conduit , memory , text @@ -66,6 +67,7 @@ test-suite zcash-haskell-test , base >=4.7 && <5 , bytestring , haskoin-core + , hexstring , hspec , text , zcash-haskell From c91e23ab9c38cb16e5acb4a5ee2e7eb3e70d375c Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 9 Feb 2024 13:23:58 -0600 Subject: [PATCH 006/149] Add function documentation --- src/ZcashHaskell/Keys.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Keys.hs b/src/ZcashHaskell/Keys.hs index c0b8bdc..a3b20b3 100644 --- a/src/ZcashHaskell/Keys.hs +++ b/src/ZcashHaskell/Keys.hs @@ -27,7 +27,7 @@ import ZcashHaskell.Types (Phrase, Seed) generateWalletSeedPhrase :: IO Phrase generateWalletSeedPhrase = withBorshVarBuffer rustWrapperGenSeedPhrase --- | Get +-- | Derive a cryptographic seed from the given seed phrase. getWalletSeed :: Phrase -> Maybe Seed getWalletSeed p = if BS.length result > 0 From 88a5189ebafb17988d0b4d5d90caa4a1a33ee983 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 9 Feb 2024 13:24:34 -0600 Subject: [PATCH 007/149] Add `Read` implementations --- src/ZcashHaskell/Types.hs | 10 +++++----- stack.yaml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 2354ca7..c126984 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -123,27 +123,27 @@ data RawTxResponse = RawTxResponse , rt_blockheight :: Integer , rt_confirmations :: Integer , rt_blocktime :: Integer - } deriving (Prelude.Show, Eq) + } deriving (Prelude.Show, Eq, Read) data ZcashNet = MainNet | TestNet | RegTestNet - deriving (Eq, Prelude.Show) + deriving (Eq, Prelude.Show, Read) -- * Transparent -- | Type to represent the two kinds of transparent addresses data TransparentType = P2SH | P2PKH - deriving (Eq, Prelude.Show) + deriving (Eq, Prelude.Show, Read) -- | Type to represent a transparent Zcash addresses data TransparentAddress = TransparentAddress { ta_type :: !TransparentType , ta_net :: !ZcashNet , ta_bytes :: !BS.ByteString - } deriving (Eq, Prelude.Show) + } deriving (Eq, Prelude.Show, Read) -- * Sapling -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. @@ -212,7 +212,7 @@ data OrchardAction = OrchardAction , out_ciphertext :: HexString -- ^ A ciphertext enabling the sender to recover the output note , cv :: HexString -- ^ A value commitment to the net value of the input note minus the output note , auth :: HexString -- ^ A signature authorizing the spend in this Action - } deriving stock (Eq, Prelude.Show, GHC.Generic) + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardAction diff --git a/stack.yaml b/stack.yaml index ab6c7c0..b5b06a5 100644 --- a/stack.yaml +++ b/stack.yaml @@ -41,7 +41,7 @@ extra-deps: - git: https://github.com/well-typed/borsh.git commit: d2fcfa159e0a844b1ec5e8ed3e232d4b380fa831 - git: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git - commit: fe2df6f7d63272ac147911c1573550bed1d38a37 + commit: fd1ddce73c0ad18a2a4509a299c6e93f8c6c383d - vector-0.13.0.0@sha256:fa5cac81a17a5af388716792e8b99c24b3b66770086756d0d8b23f8272a0244c,9112 - aeson-2.1.2.1@sha256:f10f3c661bd5cf57aee46b94420e47736240b8e209ac15f4bfc1a4e4d55831fa,6344 - generically-0.1.1 From 790ffec7173614b8a38f3e52c2180602f1309999 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 9 Feb 2024 13:28:15 -0600 Subject: [PATCH 008/149] Update change log --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 820998f..95aad13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Function to encode a human-readable transparent address - Function to generate a seed phrase +- Implementations of `Read` for types + +### Changed + + ## [0.3.0] From 75c95b03dc9139cce19c539cadbdad6df6cc17c3 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 12 Feb 2024 11:55:42 -0600 Subject: [PATCH 009/149] Implement Zebra RPC calling functions --- src/ZcashHaskell/Types.hs | 92 +++++++++++++++++++++++++++------------ src/ZcashHaskell/Utils.hs | 14 ++++++ 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index c126984..c1d3cad 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -47,20 +47,27 @@ type Phrase = BS.ByteString -- | Type to represent data after Bech32 decoding data RawData = RawData - { hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding - , bytes :: BS.ByteString -- ^ Decoded bytes + { hrp :: !BS.ByteString -- ^ Human-readable part of the Bech32 encoding + , bytes :: !BS.ByteString -- ^ Decoded bytes } deriving stock (Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawData --- * `zcashd` RPC +-- | Type for the different networks of the Zcash blockchain +data ZcashNet + = MainNet + | TestNet + | RegTestNet + deriving (Eq, Prelude.Show, Read) + +-- * RPC -- | A type to model Zcash RPC calls data RpcCall = RpcCall - { jsonrpc :: T.Text - , callId :: T.Text - , method :: T.Text - , parameters :: [Data.Aeson.Value] + { jsonrpc :: !T.Text + , callId :: !T.Text + , method :: !T.Text + , parameters :: ![Data.Aeson.Value] } deriving stock (Prelude.Show, GHC.Generic) instance ToJSON RpcCall where @@ -69,24 +76,24 @@ instance ToJSON RpcCall where -- | A type to model the response of the Zcash RPC data RpcResponse r = MakeRpcResponse - { err :: Maybe RpcError - , respId :: T.Text - , result :: Maybe r + { err :: !(Maybe RpcError) + , respId :: !T.Text + , result :: !(Maybe r) } deriving stock (Prelude.Show, GHC.Generic) deriving anyclass (ToJSON) instance (FromJSON r) => FromJSON (RpcResponse r) where parseJSON = withObject "RpcResponse" $ \obj -> do - e <- obj .: "error" + e <- obj .:? "error" i <- obj .: "id" r <- obj .: "result" pure $ MakeRpcResponse e i r -- | A type to model the errors from the Zcash RPC data RpcError = RpcError - { ecode :: Double - , emessage :: T.Text + { ecode :: !Double + , emessage :: !T.Text } deriving stock (Prelude.Show, GHC.Generic) deriving anyclass (ToJSON) @@ -97,12 +104,13 @@ instance FromJSON RpcError where m <- obj .: "message" pure $ RpcError c m +-- ** `zcashd` -- | Type to represent response from the `zcashd` RPC `getblock` method data BlockResponse = BlockResponse - { bl_confirmations :: Integer -- ^ Block confirmations - , bl_height :: Integer -- ^ Block height - , bl_time :: Integer -- ^ Block time - , bl_txs :: [T.Text] -- ^ List of transaction IDs in the block + { bl_confirmations :: !Integer -- ^ Block confirmations + , bl_height :: !Integer -- ^ Block height + , bl_time :: !Integer -- ^ Block time + , bl_txs :: ![T.Text] -- ^ List of transaction IDs in the block } deriving (Prelude.Show, Eq) instance FromJSON BlockResponse where @@ -118,18 +126,48 @@ instance FromJSON BlockResponse where data RawTxResponse = RawTxResponse { rt_id :: !HexString , rt_hex :: !HexString - , rt_shieldedOutputs :: [BS.ByteString] - , rt_orchardActions :: [OrchardAction] - , rt_blockheight :: Integer - , rt_confirmations :: Integer - , rt_blocktime :: Integer + , rt_shieldedOutputs :: ![BS.ByteString] + , rt_orchardActions :: ![OrchardAction] + , rt_blockheight :: !Integer + , rt_confirmations :: !Integer + , rt_blocktime :: !Integer } deriving (Prelude.Show, Eq, Read) -data ZcashNet - = MainNet - | TestNet - | RegTestNet - deriving (Eq, Prelude.Show, Read) +-- ** `zebrad` +-- | Type for the response from the `zebrad` RPC method `getinfo` +data ZebraGetInfo = ZebraGetInfo + { zgi_build :: !T.Text + , zgi_subversion :: !T.Text + } deriving (Prelude.Show, Eq) + +instance FromJSON ZebraGetInfo where + parseJSON = + withObject "ZebraGetInfo" $ \obj -> do + b <- obj .: "build" + s <- obj .: "subversion" + pure $ ZebraGetInfo b s + +-- | Type for the response from the `zebrad` RPC method `getblockchaininfo` +data ZebraGetBlockChainInfo = ZebraGetBlockChainInfo + { zgb_best :: !HexString + , zgb_blocks :: !Integer + , zgb_net :: !ZcashNet + } deriving (Prelude.Show, Eq) + +instance FromJSON ZebraGetBlockChainInfo where + parseJSON = + withObject "ZebraGetBlockChainInfo" $ \obj -> do + be <- obj .: "bestblockhash" + b <- obj .: "blocks" + c <- obj .: "chain" + pure $ + ZebraGetBlockChainInfo + be + b + (case (c :: String) of + "main" -> MainNet + "test" -> TestNet + _ -> RegTestNet) -- * Transparent -- | Type to represent the two kinds of transparent addresses diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 53324c5..32e56d0 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -58,3 +58,17 @@ makeZcashCall username password m p = do setRequestBasicAuth username password $ setRequestMethod "POST" defaultRequest httpJSON myRequest + +-- | Make a Zebra RPC call +makeZebraCall :: + (MonadIO m, FromJSON a) + => Int -- ^ Port for `zebrad` + -> T.Text -- ^ RPC method to call + -> [Data.Aeson.Value] -- ^ List of parameters + -> m (Response a) +makeZebraCall port m params = do + let payload = RpcCall "2.0" "zh" m params + let myRequest = + setRequestBodyJSON payload $ + setRequestPort port $ setRequestMethod "POST" defaultRequest + httpJSON myRequest From f95ebc7b00dc2be16143874428aaaf96f3bdc788 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 14 Feb 2024 08:19:34 -0600 Subject: [PATCH 010/149] Add host parameter for Zebra nodes --- CHANGELOG.md | 4 +++- package.yaml | 2 +- src/ZcashHaskell/Utils.hs | 10 +++++++--- zcash-haskell.cabal | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95aad13..b6b0375 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.4.0] + +## [Unreleased] ### Added - Function to encode a human-readable transparent address - Function to generate a seed phrase - Implementations of `Read` for types +- Function to make RPC calls to `zebrad` ### Changed diff --git a/package.yaml b/package.yaml index 649fb1a..1749c60 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: zcash-haskell -version: 0.4.1 +version: 0.4.2 git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell" license: MIT author: "Rene Vergara" diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 32e56d0..853857c 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -26,6 +26,7 @@ import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS import qualified Data.Text as T +import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable import Network.HTTP.Simple import ZcashHaskell.Types @@ -62,13 +63,16 @@ makeZcashCall username password m p = do -- | Make a Zebra RPC call makeZebraCall :: (MonadIO m, FromJSON a) - => Int -- ^ Port for `zebrad` + => T.Text -- ^ Hostname for `zebrad` + -> Int -- ^ Port for `zebrad` -> T.Text -- ^ RPC method to call -> [Data.Aeson.Value] -- ^ List of parameters -> m (Response a) -makeZebraCall port m params = do +makeZebraCall host port m params = do let payload = RpcCall "2.0" "zh" m params let myRequest = setRequestBodyJSON payload $ - setRequestPort port $ setRequestMethod "POST" defaultRequest + setRequestPort port $ + setRequestHost (E.encodeUtf8 host) $ + setRequestMethod "POST" defaultRequest httpJSON myRequest diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 1df1725..3b60510 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.1 +version: 0.4.2 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From f39838f24b1f229c3c74ef172c66023b661e029d Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 19 Feb 2024 08:29:39 -0600 Subject: [PATCH 011/149] Pin the Rust nightly to avoid `ahash` error --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5943f32..8bb5f0c 100644 --- a/Makefile +++ b/Makefile @@ -28,4 +28,4 @@ haskell: src/ZcashHaskell/Orchard.hs src/ZcashHaskell/Sapling.hs src/ZcashHaskel stack build $(rustlib)/rustzcash_wrapper.h: librustzcash-wrapper/src/lib.rs librustzcash-wrapper/Cargo.toml - cd librustzcash-wrapper && cargo +nightly cbuild + cd librustzcash-wrapper && cargo +nightly-2024-02-04 cbuild From 5c4786f1d5193987fa2deedf9e7aa29b5156490a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 20 Feb 2024 07:35:05 -0600 Subject: [PATCH 012/149] Add tests for Seed and Phrase generation --- test/Spec.hs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/Spec.hs b/test/Spec.hs index c5aafdb..ae02f36 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -438,6 +438,15 @@ main = do let msg = maybe "" a_memo decryptedNote2 msg `shouldBe` "Hello World!\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL" + describe "Wallet seed phrase" $ do + it "Generate phrase" $ do + p <- generateWalletSeedPhrase + print p + BS.length p `shouldNotBe` 0 + it "Derive seed" $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + maybe 0 BS.length s `shouldBe` 64 describe "Address tests" $ do it "Encode transparent" $ do let ua = From c0e8fa85ce074da5f518590886fa6003e9dbd4c5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 21 Feb 2024 10:51:03 -0600 Subject: [PATCH 013/149] Implement custom build with `cabal` --- Setup.hs | 95 +++++++++++++++++++++++++++++++++ cabal.project | 12 +++++ librustzcash-wrapper/Cargo.toml | 2 + package.yaml | 58 -------------------- stack.yaml | 77 -------------------------- test/Spec.hs | 1 - zcash-haskell.cabal | 33 ++++++------ 7 files changed, 125 insertions(+), 153 deletions(-) create mode 100644 Setup.hs create mode 100644 cabal.project delete mode 100644 package.yaml delete mode 100644 stack.yaml diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..3f8a220 --- /dev/null +++ b/Setup.hs @@ -0,0 +1,95 @@ +import Data.Maybe (fromMaybe) +import Distribution.PackageDescription +import Distribution.Simple +import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..), localPkgDescr) +import Distribution.Simple.PreProcess +import Distribution.Simple.Program.Find + ( defaultProgramSearchPath + , findProgramOnSearchPath + ) +import Distribution.Simple.Setup +import Distribution.Simple.Utils + ( IODataMode(IODataModeBinary) + , maybeExit + , rawSystemStdInOut + ) +import Distribution.Verbosity (Verbosity) +import qualified Distribution.Verbosity as Verbosity +import System.Directory (getCurrentDirectory) +import System.FilePath (()) + +main :: IO () +main = defaultMainWithHooks hooks + where + hooks = + simpleUserHooks + { preConf = + \_ flags -> do + rsMake (fromFlag $ configVerbosity flags) + pure emptyHookedBuildInfo + , hookedPreProcessors = knownSuffixHandlers + , confHook = \a flags -> confHook simpleUserHooks a flags >>= rsAddDirs + , postClean = \_ flags _ _ -> rsClean (fromFlag $ cleanVerbosity flags) + } + +rsFolder :: FilePath +rsFolder = "librustzcash-wrapper" + +execCargo :: Verbosity -> String -> [String] -> IO () +execCargo verbosity command args = do + cargoPath <- + findProgramOnSearchPath Verbosity.silent defaultProgramSearchPath "cargo" + dir <- getCurrentDirectory + let cargoExec = + case cargoPath of + Just (p, _) -> p + Nothing -> "cargo" + cargoArgs = command : args + workingDir = Just (dir rsFolder) + thirdComponent (_, _, c) = c + maybeExit . fmap thirdComponent $ + rawSystemStdInOut + verbosity + cargoExec + cargoArgs + workingDir + Nothing + Nothing + IODataModeBinary + +rsMake :: Verbosity -> IO () +rsMake verbosity = execCargo verbosity "+nightly-2024-02-04" ["cbuild"] + +rsAddDirs :: LocalBuildInfo -> IO LocalBuildInfo +rsAddDirs lbi' = do + dir <- getCurrentDirectory + let rustIncludeDir = + dir rsFolder "target/x86_64-unknown-linux-gnu/debug" + rustLibDir = dir rsFolder "target/x86_64-unknown-linux-gnu/debug" + updateLbi lbi = lbi {localPkgDescr = updatePkgDescr (localPkgDescr lbi)} + updatePkgDescr pkgDescr = + pkgDescr {library = updateLib <$> library pkgDescr} + updateLib lib = lib {libBuildInfo = updateLibBi (libBuildInfo lib)} + updateLibBi libBuild = + libBuild + { includeDirs = rustIncludeDir : includeDirs libBuild + , extraLibDirs = rustLibDir : extraLibDirs libBuild + } + pure $ updateLbi lbi' + +rsClean :: Verbosity -> IO () +rsClean verbosity = execCargo verbosity "clean" [] + +cabalFlag :: FlagName -> ConfigFlags -> Bool +cabalFlag name = + fromMaybe False . lookupFlagAssignment name . configConfigurationsFlags + +unlessFlagM :: FlagName -> ConfigFlags -> IO () -> IO () +unlessFlagM name flags action + | cabalFlag name flags = pure () + | otherwise = action + +applyUnlessM :: FlagName -> ConfigFlags -> (a -> IO a) -> a -> IO a +applyUnlessM name flags apply a + | cabalFlag name flags = pure a + | otherwise = apply a diff --git a/cabal.project b/cabal.project new file mode 100644 index 0000000..60a9728 --- /dev/null +++ b/cabal.project @@ -0,0 +1,12 @@ +packages: ./*.cabal +with-compiler: ghc-9.4.8 + +source-repository-package + type: git + location: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git + tag: 335e804454cd30da2c526457be37e477f71e4665 + +source-repository-package + type: git + location: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git + tag: fd1ddce73c0ad18a2a4509a299c6e93f8c6c383d diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 7496e7c..cf068e4 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type=["staticlib"] [dependencies] haskell-ffi.git = "https://github.com/BeFunctional/haskell-rust-ffi.git" diff --git a/package.yaml b/package.yaml deleted file mode 100644 index 1749c60..0000000 --- a/package.yaml +++ /dev/null @@ -1,58 +0,0 @@ -name: zcash-haskell -version: 0.4.2 -git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell" -license: MIT -author: "Rene Vergara" -maintainer: "pitmutt@vergara.tech" -copyright: "(c)2022-2024 Vergara Technologies LLC" - -extra-source-files: -- README.md -- CHANGELOG.md -- configure - -# Metadata used when publishing your package -synopsis: Utilities to interact with the Zcash blockchain -category: Blockchain - -# To avoid duplicated efforts in documentation and dealing with the -# complications of embedding Haddock markup inside cabal files, it is -# common to point users to the README.md file. -description: Please see the README on the repo at - -dependencies: -- base >= 4.7 && < 5 - -library: - source-dirs: src - dependencies: - - bytestring - - borsh >= 0.2 - - text - - foreign-rust - - generics-sop - - aeson - - http-conduit - - base58-bytestring - - cryptonite - - memory - - hexstring - pkg-config-dependencies: - - rustzcash_wrapper-uninstalled - -tests: - zcash-haskell-test: - main: Spec.hs - source-dirs: test - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N - dependencies: - - zcash-haskell - - hspec - - bytestring - - text - - aeson - - haskoin-core - - hexstring diff --git a/stack.yaml b/stack.yaml deleted file mode 100644 index b5b06a5..0000000 --- a/stack.yaml +++ /dev/null @@ -1,77 +0,0 @@ -# This file was automatically generated by 'stack init' -# -# Some commonly used options have been documented as comments in this file. -# For advanced use and comprehensive documentation of the format, please see: -# https://docs.haskellstack.org/en/stable/yaml_configuration/ - -# Resolver to choose a 'specific' stackage snapshot or a compiler version. -# A snapshot resolver dictates the compiler version and the set of packages -# to be used for project dependencies. For example: -# -# resolver: lts-3.5 -# resolver: nightly-2015-09-21 -# resolver: ghc-7.10.2 -# -# The location of a snapshot can be provided as a file or url. Stack assumes -# a snapshot provided as a file might change, whereas a url resource does not. -# -# resolver: ./custom-snapshot.yaml -# resolver: https://example.com/snapshots/2018-01-01.yaml -resolver: lts-21.22 - -# User packages to be built. -# Various formats can be used as shown in the example below. -# -# packages: -# - some-directory -# - https://example.com/foo/bar/baz-0.0.2.tar.gz -# subdirs: -# - auto-update -# - wai -packages: -- . -# Dependency packages to be pulled from upstream that are not in the resolver. -# These entries can reference officially published versions as well as -# forks / in-progress versions pinned to a git hash. For example: -# -extra-deps: -# - acme-missiles-0.3 - - git: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git - commit: 787c2e813eb3a5d16c375d4b37dfefbd2adcdf05 - - git: https://github.com/well-typed/borsh.git - commit: d2fcfa159e0a844b1ec5e8ed3e232d4b380fa831 - - git: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git - commit: fd1ddce73c0ad18a2a4509a299c6e93f8c6c383d - - vector-0.13.0.0@sha256:fa5cac81a17a5af388716792e8b99c24b3b66770086756d0d8b23f8272a0244c,9112 - - aeson-2.1.2.1@sha256:f10f3c661bd5cf57aee46b94420e47736240b8e209ac15f4bfc1a4e4d55831fa,6344 - - generically-0.1.1 - - semialign-1.2.0.1@sha256:ee3468e349e72ec0a604ae05573a4de7181d97d10366254244a0cca8a76d6c35,2852 - - strict-0.4.0.1@sha256:d6205a748eb8db4cd17a7179be970c94598809709294ccfa43159c7f3cc4bf5d,4187 - - these-1.1.1.1@sha256:2991c13e264b0c35c696c8f5f85c428c53bc42e93b1dfbd19a582052112d948a,2748 - - assoc-1.0.2@sha256:9decd0933cb6b903a40a8ace02d634bf90048ee2e5b0a514dccad7056c041881,1253 -# -# extra-deps: [] - -# Override default flag values for local packages and extra-deps -# flags: {} - -# Extra package databases containing global packages -# extra-package-dbs: [] - -# Control whether we use the GHC we find on the path -# system-ghc: true -# -# Require a specific version of Stack, using version ranges -# require-stack-version: -any # Default -# require-stack-version: ">=2.9" -# -# Override the architecture used by Stack, especially useful on Windows -# arch: i386 -# arch: x86_64 -# -# Extra directories used by Stack for building -# extra-include-dirs: [/path/to/dir] -# extra-lib-dirs: [/path/to/dir] -# -# Allow a newer minor version of GHC than the snapshot specifies -# compiler-check: newer-minor diff --git a/test/Spec.hs b/test/Spec.hs index ae02f36..c387f83 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -32,7 +32,6 @@ import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO import Data.Word import GHC.Float.RealFracMethods (properFractionDoubleInteger) -import Haskoin.Keys.Mnemonic import Test.Hspec import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) import ZcashHaskell.Orchard diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 3b60510..8f422e9 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -1,11 +1,11 @@ -cabal-version: 1.12 +cabal-version: 3.0 -- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.2 +version: 0.4.2.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -14,15 +14,17 @@ maintainer: pitmutt@vergara.tech copyright: (c)2022-2024 Vergara Technologies LLC license: MIT license-file: LICENSE -build-type: Simple -extra-source-files: +build-type: Custom +extra-doc-files: README.md CHANGELOG.md - configure -source-repository head - type: git - location: https://git.vergara.tech/Vergara_Tech/zcash-haskell +custom-setup + setup-depends: + base >= 4.7 && < 5 + , Cabal >= 3.0.0.0 + , directory >= 1.3.6.0 + , filepath >= 1.3.0.2 library exposed-modules: @@ -33,12 +35,8 @@ library ZcashHaskell.Transparent ZcashHaskell.Types ZcashHaskell.Utils - other-modules: - Paths_zcash_haskell hs-source-dirs: src - pkgconfig-depends: - rustzcash_wrapper-uninstalled build-depends: aeson , base >=4.7 && <5 @@ -48,17 +46,17 @@ library , cryptonite , foreign-rust , generics-sop - , hexstring + , hexstring >=0.12 , http-conduit , memory , text - default-language: Haskell2010 + build-tool-depends: + c2hs:c2hs + default-language: GHC2021 test-suite zcash-haskell-test type: exitcode-stdio-1.0 main-is: Spec.hs - other-modules: - Paths_zcash_haskell hs-source-dirs: test ghc-options: -threaded -rtsopts -with-rtsopts=-N @@ -71,4 +69,5 @@ test-suite zcash-haskell-test , hspec , text , zcash-haskell - default-language: Haskell2010 + extra-libraries: rustzcash_wrapper + default-language: GHC2021 From faaa84caa6d5d912682fb6c4ab24d80cc863e28d Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 21 Feb 2024 15:19:23 -0600 Subject: [PATCH 014/149] Add `static` flag for `cabal` --- cabal.project | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cabal.project b/cabal.project index 60a9728..4fce043 100644 --- a/cabal.project +++ b/cabal.project @@ -1,6 +1,8 @@ packages: ./*.cabal with-compiler: ghc-9.4.8 +static: true + source-repository-package type: git location: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git From 9d7a88be436bb16939dbdceed5610e821753dca6 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 21 Feb 2024 15:44:03 -0600 Subject: [PATCH 015/149] Add `shared` flag for `cabal` --- cabal.project | 1 + 1 file changed, 1 insertion(+) diff --git a/cabal.project b/cabal.project index 4fce043..3075981 100644 --- a/cabal.project +++ b/cabal.project @@ -1,6 +1,7 @@ packages: ./*.cabal with-compiler: ghc-9.4.8 +shared: true static: true source-repository-package From f818586b33c338cff0d453f19897a81df3ab44c5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 21 Feb 2024 19:33:28 -0600 Subject: [PATCH 016/149] Set the Rust tool-chain --- Setup.hs | 2 +- librustzcash-wrapper/rust-toolchain.toml | 4 ++++ zcash-haskell.cabal | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 librustzcash-wrapper/rust-toolchain.toml diff --git a/Setup.hs b/Setup.hs index 3f8a220..ad6bb6a 100644 --- a/Setup.hs +++ b/Setup.hs @@ -58,7 +58,7 @@ execCargo verbosity command args = do IODataModeBinary rsMake :: Verbosity -> IO () -rsMake verbosity = execCargo verbosity "+nightly-2024-02-04" ["cbuild"] +rsMake verbosity = execCargo verbosity "cbuild" [] rsAddDirs :: LocalBuildInfo -> IO LocalBuildInfo rsAddDirs lbi' = do diff --git a/librustzcash-wrapper/rust-toolchain.toml b/librustzcash-wrapper/rust-toolchain.toml new file mode 100644 index 0000000..af255e0 --- /dev/null +++ b/librustzcash-wrapper/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2024-02-04" +components = [ "rustfmt", "rustc-dev"] +profile = "minimal" diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 8f422e9..1a54fba 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.2.1 +version: 0.4.2.2 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From ee7e32feb64a778a5bb5f3a12ce0429b43a0c4b5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 21 Feb 2024 20:32:36 -0600 Subject: [PATCH 017/149] Remove flags --- cabal.project | 3 --- 1 file changed, 3 deletions(-) diff --git a/cabal.project b/cabal.project index 3075981..60a9728 100644 --- a/cabal.project +++ b/cabal.project @@ -1,9 +1,6 @@ packages: ./*.cabal with-compiler: ghc-9.4.8 -shared: true -static: true - source-repository-package type: git location: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git From 9713203e2bd10d5b3b623561564c82a8d7614ec8 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 25 Feb 2024 15:22:53 -0600 Subject: [PATCH 018/149] Update installation package --- CHANGELOG.md | 4 +++ Setup.hs | 49 +++++++++++++++++++++++++++++++-- configure | 4 +-- librustzcash-wrapper/Cargo.toml | 3 -- zcash-haskell.cabal | 11 ++++++-- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b0375..a2fed36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Update installation to `cabal` +### Removed + +- `Makefile` ## [0.3.0] diff --git a/Setup.hs b/Setup.hs index ad6bb6a..5efaca2 100644 --- a/Setup.hs +++ b/Setup.hs @@ -1,3 +1,5 @@ +import Control.Exception (throw) +import Control.Monad (forM_, when) import Data.Maybe (fromMaybe) import Distribution.PackageDescription import Distribution.Simple @@ -15,8 +17,23 @@ import Distribution.Simple.Utils ) import Distribution.Verbosity (Verbosity) import qualified Distribution.Verbosity as Verbosity -import System.Directory (getCurrentDirectory) +import GHC.Generics +import System.Directory + ( XdgDirectory(..) + , copyFile + , createDirectory + , createDirectoryIfMissing + , doesDirectoryExist + , doesFileExist + , getCurrentDirectory + , getDirectoryContents + , getHomeDirectory + , getXdgDirectory + ) +import System.Environment import System.FilePath (()) +import Text.Regex +import Text.Regex.Base main :: IO () main = defaultMainWithHooks hooks @@ -58,10 +75,13 @@ execCargo verbosity command args = do IODataModeBinary rsMake :: Verbosity -> IO () -rsMake verbosity = execCargo verbosity "cbuild" [] +rsMake verbosity = do + execCargo verbosity "cbuild" [] rsAddDirs :: LocalBuildInfo -> IO LocalBuildInfo rsAddDirs lbi' = do + localData <- getXdgDirectory XdgData "zcash-haskell" + createDirectoryIfMissing True localData dir <- getCurrentDirectory let rustIncludeDir = dir rsFolder "target/x86_64-unknown-linux-gnu/debug" @@ -73,8 +93,12 @@ rsAddDirs lbi' = do updateLibBi libBuild = libBuild { includeDirs = rustIncludeDir : includeDirs libBuild + , extraLibs = "rustzcash_wrapper" : extraLibs libBuild , extraLibDirs = rustLibDir : extraLibDirs libBuild + , extraBundledLibs = "rustzcash_wrapper" : extraBundledLibs libBuild + , ldOptions = ("-L" ++ localData) : ldOptions libBuild } + copyDir rustLibDir localData pure $ updateLbi lbi' rsClean :: Verbosity -> IO () @@ -93,3 +117,24 @@ applyUnlessM :: FlagName -> ConfigFlags -> (a -> IO a) -> a -> IO a applyUnlessM name flags apply a | cabalFlag name flags = pure a | otherwise = apply a + +copyDir :: FilePath -> FilePath -> IO () +copyDir src dst = do + whenM (not <$> doesDirectoryExist src) $ + throw (userError "source does not exist") + --whenM (doesFileOrDirectoryExist dst) $ + --throw (userError "destination already exists") + createDirectoryIfMissing True dst + content <- getDirectoryContents src + let xs = filter (`notElem` [".", ".."]) content + forM_ xs $ \name -> do + let srcPath = src name + let dstPath = dst name + isDirectory <- doesDirectoryExist srcPath + if isDirectory + then copyDir srcPath dstPath + else copyFile srcPath dstPath + where + doesFileOrDirectoryExist x = orM [doesDirectoryExist x, doesFileExist x] + orM xs = or <$> sequence xs + whenM s r = s >>= flip when r diff --git a/configure b/configure index 21dc7bb..bb38dfe 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #!/bin/bash -echo "export PKG_CONFIG_PATH=$(pwd)/librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug:\$PKG_CONFIG_PATH" | tee -a ~/.bashrc -echo "export LD_LIBRARY_PATH=$(pwd)/librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug:\$LD_LIBRARY_PATH" | tee -a ~/.bashrc +echo "export PKG_CONFIG_PATH=$HOME/.local/share/zcash-haskell:\$PKG_CONFIG_PATH" | tee -a ~/.bashrc +echo "export LD_LIBRARY_PATH=$HOME/.local/share/zcash-haskell:\$LD_LIBRARY_PATH" | tee -a ~/.bashrc source ~/.bashrc diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index cf068e4..fa4fb55 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -4,9 +4,6 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -crate-type=["staticlib"] - [dependencies] haskell-ffi.git = "https://github.com/BeFunctional/haskell-rust-ffi.git" haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274" diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 1a54fba..79b6db2 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -1,11 +1,11 @@ -cabal-version: 3.0 +cabal-version: 3.8 -- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.2.2 +version: 0.4.2.3 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -15,6 +15,8 @@ copyright: (c)2022-2024 Vergara Technologies LLC license: MIT license-file: LICENSE build-type: Custom +extra-source-files: + Setup.hs extra-doc-files: README.md CHANGELOG.md @@ -25,6 +27,9 @@ custom-setup , Cabal >= 3.0.0.0 , directory >= 1.3.6.0 , filepath >= 1.3.0.2 + , envy + , regex-compat + , regex-base library exposed-modules: @@ -69,5 +74,5 @@ test-suite zcash-haskell-test , hspec , text , zcash-haskell - extra-libraries: rustzcash_wrapper + pkgconfig-depends: rustzcash_wrapper default-language: GHC2021 From 26b21e882b92b4a810086f763a16927f854c1fab Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 25 Feb 2024 16:10:52 -0600 Subject: [PATCH 019/149] Remove Makefile --- Makefile | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 8bb5f0c..0000000 --- a/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2022-2024 Vergara Technologies LLC -# -# This file is part of Zcash-Haskell. -# -# Zcash-Haskell is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. - -# You should have received a copy of the GNU Lesser General Public License along with -# Zcash-Haskell. If not, see . - -rustlib := librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug - -.PHONY: all - -all: haskell - -test: test/Spec.hs haskell - stack test - -haskell: src/ZcashHaskell/Orchard.hs src/ZcashHaskell/Sapling.hs src/ZcashHaskell/Types.hs src/ZcashHaskell/Utils.hs src/C/Zcash.chs package.yaml stack.yaml $(rustlib)/rustzcash_wrapper.h $(rustlib)/librustzcash_wrapper.a $(rustlib)/librustzcash_wrapper.so $(rustlib)/rustzcash_wrapper-uninstalled.pc - stack build - -$(rustlib)/rustzcash_wrapper.h: librustzcash-wrapper/src/lib.rs librustzcash-wrapper/Cargo.toml - cd librustzcash-wrapper && cargo +nightly-2024-02-04 cbuild From 6b870d91f8a96805febbc9e5d9392468e509a64e Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 25 Feb 2024 16:11:10 -0600 Subject: [PATCH 020/149] Correct needed libraries --- Setup.hs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Setup.hs b/Setup.hs index 5efaca2..81a2622 100644 --- a/Setup.hs +++ b/Setup.hs @@ -93,10 +93,7 @@ rsAddDirs lbi' = do updateLibBi libBuild = libBuild { includeDirs = rustIncludeDir : includeDirs libBuild - , extraLibs = "rustzcash_wrapper" : extraLibs libBuild , extraLibDirs = rustLibDir : extraLibDirs libBuild - , extraBundledLibs = "rustzcash_wrapper" : extraBundledLibs libBuild - , ldOptions = ("-L" ++ localData) : ldOptions libBuild } copyDir rustLibDir localData pure $ updateLbi lbi' From 6e11ae7779937c367731569f159f80a6180ebc38 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 25 Feb 2024 16:14:04 -0600 Subject: [PATCH 021/149] Update install instructions --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ffa015..a6f7cc1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,10 @@ A Haskell library to interact with the Zcash blockchain. ``` ./configure ``` -- Compile the Rust and Haskell portions: `make` +- Compile the Rust and Haskell portions: + ``` + cabal build + ``` ## Use From 050c36e76d59451e0cf1497ed8c279b7e0828c2b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 25 Feb 2024 17:50:47 -0600 Subject: [PATCH 022/149] Update `.gitignore` --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bb46a99..f9cbc7a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *~ librustzcash-wrapper/target/ stack.yaml.lock +dist-newstyle/ From 2ada9aac41c546e46d2f832ff78a6fdffe41dd9a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 26 Feb 2024 08:49:47 -0600 Subject: [PATCH 023/149] Correct `cabal` versioning --- zcash-haskell.cabal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 79b6db2..058058e 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -1,4 +1,4 @@ -cabal-version: 3.8 +cabal-version: 3.0 -- This file has been generated from package.yaml by hpack version 0.36.0. -- @@ -57,7 +57,7 @@ library , text build-tool-depends: c2hs:c2hs - default-language: GHC2021 + default-language: Haskell2010 test-suite zcash-haskell-test type: exitcode-stdio-1.0 @@ -75,4 +75,4 @@ test-suite zcash-haskell-test , text , zcash-haskell pkgconfig-depends: rustzcash_wrapper - default-language: GHC2021 + default-language: Haskell2012 From 67d6e85d05e21adc3c20adf97990053e02d7c7c4 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 26 Feb 2024 08:57:12 -0600 Subject: [PATCH 024/149] Fix typo in cabal file --- zcash-haskell.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 058058e..ad2e0d3 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -75,4 +75,4 @@ test-suite zcash-haskell-test , text , zcash-haskell pkgconfig-depends: rustzcash_wrapper - default-language: Haskell2012 + default-language: Haskell2010 From cb44b6d4e04cfe017e2907520fdfe9c32dc32d26 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 27 Feb 2024 19:06:58 -0600 Subject: [PATCH 025/149] Correct type for blockheight --- src/ZcashHaskell/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index c1d3cad..f0cbcdb 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -150,7 +150,7 @@ instance FromJSON ZebraGetInfo where -- | Type for the response from the `zebrad` RPC method `getblockchaininfo` data ZebraGetBlockChainInfo = ZebraGetBlockChainInfo { zgb_best :: !HexString - , zgb_blocks :: !Integer + , zgb_blocks :: !Int , zgb_net :: !ZcashNet } deriving (Prelude.Show, Eq) From 9a5ad756b87f6ace1dbaf42abf781b84ec20e097 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sat, 2 Mar 2024 07:53:48 -0600 Subject: [PATCH 026/149] Add `Read` implementation to `UnifiedAddress` --- src/ZcashHaskell/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index f0cbcdb..7650dbb 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -215,7 +215,7 @@ data UnifiedAddress = UnifiedAddress , o_rec :: BS.ByteString , s_rec :: BS.ByteString , t_rec :: Maybe TransparentAddress - } deriving (Prelude.Show, Eq) + } deriving (Prelude.Show, Eq, Read) -- | Helper type for marshalling UAs data RawUA = RawUA From 3cc0e96c448b41d3fc44c682546e121220e76e3b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 3 Mar 2024 08:23:48 -0600 Subject: [PATCH 027/149] Rust updates for `bech32` encode/decode --- CHANGELOG.md | 2 ++ librustzcash-wrapper/Cargo.lock | 8 +++++++- librustzcash-wrapper/Cargo.toml | 2 +- librustzcash-wrapper/src/lib.rs | 26 ++++++++++++++++++++------ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2fed36..bbea7b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Function to generate a seed phrase - Implementations of `Read` for types - Function to make RPC calls to `zebrad` +- Function to encode unified addresses from receivers ### Changed - Update installation to `cabal` +- Updated `bech32` Rust crate to 0.11 ### Removed diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index a77c068..2672c97 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -88,6 +88,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bincode" version = "1.3.3" @@ -1282,7 +1288,7 @@ dependencies = [ name = "rustzcash-wrapper" version = "0.1.0" dependencies = [ - "bech32 0.9.1", + "bech32 0.11.0", "borsh 0.10.3", "f4jumble", "haskell-ffi", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index fa4fb55..49fbb9a 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -10,7 +10,7 @@ haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274" f4jumble = "0.1" zcash_address = "0.2.0" borsh = "0.10" -bech32 = "0.9.1" +bech32 = "0.11" orchard = "0.4.0" zcash_note_encryption = "0.3.0" zcash_primitives = "0.11.0" diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index ff83321..4b9e27a 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -71,10 +71,8 @@ use zcash_note_encryption::EphemeralKeyBytes; use bech32::{ decode, - u5, - FromBase32, - ToBase32, - Variant + Hrp, + Bech32m }; pub enum RW {} @@ -330,8 +328,8 @@ pub extern "C" fn rust_wrapper_bech32decode( let input: String = marshall_from_haskell_var(input, input_len, RW); let decodedBytes = bech32::decode(&input); match decodedBytes { - Ok((hrp, bytes, variant)) => { - let rd = RawData {hrp: hrp.into(), bytes: Vec::::from_base32(&bytes).unwrap()}; + Ok((hrp, bytes)) => { + let rd = RawData {hrp: hrp.as_bytes().to_vec(), bytes}; marshall_to_haskell_var(&rd, out, out_len, RW); } Err(_e) => { @@ -341,6 +339,22 @@ pub extern "C" fn rust_wrapper_bech32decode( } } +#[no_mangle] +pub extern "C" fn rust_wrapper_bech32_encode( + hr: *const u8, + hr_len: usize, + b: *const u8, + b_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let hr: String = marshall_from_haskell_var(hr, hr_len, RW); + let hrp = Hrp::parse(&hr).unwrap(); + let b: Vec = marshall_from_haskell_var(b, b_len, RW); + let string = bech32::encode::(hrp, &b).unwrap(); + marshall_to_haskell_var(&string, out, out_len, RW); +} + #[no_mangle] pub extern "C" fn rust_wrapper_svk_decode( input: *const u8, From 97b338bddcdb242a6cddd142dfb5565b7a155e71 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Sun, 3 Mar 2024 16:19:06 -0500 Subject: [PATCH 028/149] Commit for revision... --- librustzcash-wrapper/src/lib.rs | 23 ++++++++++-- src/C/Zcash.chs | 6 ++++ src/ZcashHaskell/Sapling.hs | 24 +++++++++++-- src/ZcashHaskell/Transparent.hs | 20 +++++++++++ src/ZcashHaskell/Types.hs | 10 ++++++ src/ZcashHaskell/Utils.hs | 17 +++++++++ test/Spec.hs | 63 +++++++++++++++++++++++++++++++-- zcash-haskell.cabal | 5 +++ 8 files changed, 162 insertions(+), 6 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index ff83321..045a7b9 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -2,7 +2,6 @@ // // This file is part of Zcash-Haskell. // - use std::{ marker::PhantomData, io::{ @@ -56,7 +55,9 @@ use zcash_address::{ ZcashAddress }; -use zcash_client_backend::keys::sapling::ExtendedFullViewingKey; +use zcash_client_backend::keys::{sapling, sapling::ExtendedFullViewingKey}; +use zcash_primitives::zip32::AccountId; +use std::slice; use orchard::{ Action, @@ -606,3 +607,21 @@ pub extern "C" fn rust_wrapper_recover_seed( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_sapling_spendingkey( + input: *const u8, + input_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + // - Retrieve parameters + +// let extsk = sapling::spending_key(&seed[0..32], +// cointype, +// accountid); +// println!("SpendingKey -> {:?}", extsk); +// let s = extsk.to_bytes(); +// let xsk : Vec = s.iter().cloned().collect(); +// marshall_to_haskell_var(&xsk, out, out_len, RW); +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index f93c6c9..bfaab7b 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -125,3 +125,9 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey + { toBorshVar* `SaplingSKeyParams'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index ea9d37e..a0ca034 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -23,19 +23,27 @@ import C.Zcash , rustWrapperSaplingNoteDecode , rustWrapperSaplingVkDecode , rustWrapperTxParse + , rustWrapperSaplingSpendingkey ) import Data.Aeson import qualified Data.ByteString as BS +import Data.ByteString.Lazy as BL import Data.HexString (HexString(..), toBytes) -import Foreign.Rust.Marshall.Variable (withPureBorshVarBuffer) +import Foreign.Rust.Marshall.Variable + ( withPureBorshVarBuffer + , withPureBorshVarBuffer + ) import ZcashHaskell.Types ( DecodedNote(..) , RawData(..) , RawTxResponse(..) , ShieldedOutput(..) , decodeHexText + , SaplingSKeyParams(..) ) -import ZcashHaskell.Utils (decodeBech32) +import ZcashHaskell.Utils + +import Data.Word -- | Check if given bytesting is a valid encoded shielded address isValidShieldedAddress :: BS.ByteString -> Bool @@ -81,3 +89,15 @@ instance FromJSON RawTxResponse where Just o' -> do a <- o' .: "actions" pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b + +-- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID +genSaplingSpendingKey :: + BS.ByteString -> Word32 -> Word32 -> Maybe BS.ByteString +genSaplingSpendingKey seed coin_type account_id = do + if BS.length res > 0 + then Just res + else Nothing + where + let params = SaplingSKeyParams seed coin_type account_id + res = (withPureBorshVarBuffer . rustWrapperSaplingSpendingkey) params + diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 6de9200..f7f2a48 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -28,6 +28,10 @@ import ZcashHaskell.Types , ZcashNet(..) ) +import Haskoin.Crypto.Keys.Extended +import Data.Word +import Crypto.Secp256k1 + encodeTransparent :: TransparentAddress -> T.Text encodeTransparent t = case ta_type t of @@ -48,3 +52,19 @@ encodeTransparent t = sha256 bs = BA.convert (hash bs :: Digest SHA256) digest = BS.pack [a, b] <> h checksum = sha256 $ sha256 digest + +-- | Attempts to generate an Extended Private Key from a known HDSeed. +genTransparentPrvKey :: + BS.ByteString -> XPrvKey +genTransparentPrvKey hdseed = do + makeXPrvKey hdseed + +-- | Attempts to obtain an Extended Public Key from a known Extended Private Key +genTransparentPubKey :: + XPrvKey -> IO XPubKey +genTransparentPubKey xpvk = do + ioCtx <- createContext + let xpubk = deriveXPubKey ioCtx xpvk + return xpubk + + diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index f0cbcdb..131a7cb 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -278,6 +278,16 @@ data DecodedNote = DecodedNote deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote +-- } Type to represent parameters to call rust zcash library +data SaplingSKeyParams = SaplingSKeyParams + { hdseed :: BS.ByteString -- ^ seed required for sappling spending key generation + , coin_type :: Word32 -- ^ coin_type + , account_id :: Word32 -- ^ account id + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingSKeyParams + -- * Helpers -- | Helper function to turn a hex-encoded string to bytestring decodeHexText :: String -> BS.ByteString diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 853857c..503ccc5 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -31,6 +31,13 @@ import Foreign.Rust.Marshall.Variable import Network.HTTP.Simple import ZcashHaskell.Types +import Foreign.C.Types +import Foreign.Marshal.Array (allocaArray, peekArray) +import Foreign.Ptr (Ptr) + +import Data.Word +-- | + -- | Decode the given bytestring using Bech32 decodeBech32 :: BS.ByteString -> RawData decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode @@ -76,3 +83,13 @@ makeZebraCall host port m params = do setRequestHost (E.encodeUtf8 host) $ setRequestMethod "POST" defaultRequest httpJSON myRequest + + +-- + Misc functions +-- Convert an array of Word8 to a ByteString +word8ArrayToByteString :: [Word8] -> BS.ByteString +word8ArrayToByteString = BS.pack + +-- Convert [Word8] to String +word8ToString :: [Word8] -> String +word8ToString = map (toEnum . fromEnum) diff --git a/test/Spec.hs b/test/Spec.hs index c387f83..c9ceab8 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -30,7 +30,7 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as E import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO -import Data.Word + import GHC.Float.RealFracMethods (properFractionDoubleInteger) import Test.Hspec import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) @@ -41,8 +41,10 @@ import ZcashHaskell.Sapling , isValidSaplingViewingKey , isValidShieldedAddress , matchSaplingAddress + , genSaplingSpendingKey ) -import ZcashHaskell.Transparent (encodeTransparent) +import ZcashHaskell.Transparent + --(encodeTransparent) import ZcashHaskell.Types ( BlockResponse(..) , DecodedNote(..) @@ -56,6 +58,10 @@ import ZcashHaskell.Types ) import ZcashHaskell.Utils +import Foreign.C.Types +import Data.Word +import Haskoin.Crypto.Keys.Extended + main :: IO () main = do hspec $ do @@ -455,3 +461,56 @@ main = do Nothing -> "Bad UA" Just u -> maybe "No transparent" encodeTransparent $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" + describe "Transparent Private and Publicc Key Generation" $ do + it "Obtain a transparent extended private key from HDSeed" $ do + let hdseed = [206, 61, 120, 38, + 206, 40, 201, 62, + 83, 175, 151, 131, + 218, 141, 206, 254, + 28, 244, 172, 213, + 128, 248, 156, 45, + 204, 44, 169, 3, + 162, 188, 16, 173, + 192, 164, 96, 148, + 91, 52, 244, 83, + 149, 169, 82, 196, + 199, 53, 177, 170, + 1, 6, 0, 120, + 170, 2, 238, 219, + 241, 243, 172, 178, + 104, 81, 159, 144 + ] :: [Word8] + let xtpvk = genTransparentPrvKey (word8ArrayToByteString hdseed) + let testpvk = XPrvKey 0 "0000000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" + xtpvk `shouldBe` testpvk +-- describe "Obtain transparent public key from private key" $ do + it "Obtain a transparent extended public key from private key" $ do + let testpvk = XPrvKey 0 "0000000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" + let testpbk = XPubKey 0 "00000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8" + let xtpubkIO = genTransparentPubKey testpvk + xtpubk <- xtpubkIO + ---print $ show xtpubk + xtpubk `shouldBe` testpbk + describe "Sapling SpendingKey test" $ do + it "Call function with parameters" $ do + let hdseed = [206, 61, 120, 38, + 206, 40, 201, 62, + 83, 175, 151, 131, + 218, 141, 206, 254, + 28, 244, 172, 213, + 128, 248, 156, 45, + 204, 44, 169, 3, + 162, 188, 16, 173, + 192, 164, 96, 148, + 91, 52, 244, 83, + 149, 169, 82, 196, + 199, 53, 177, 170, + 1, 6, 0, 120, + 170, 2, 238, 219, + 241, 243, 172, 178, + 104, 81, 159, 144 + ] :: [Word8] + let coin = 1 :: Word32 + let account = 0 :: Word32 + res <- genSaplingSpendingKey (word8ArrayToByteString hdseed) coin account + res `shouldBe` "genSaplingSpendingKey Function called." \ No newline at end of file diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index ad2e0d3..1eab3f4 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -55,6 +55,8 @@ library , http-conduit , memory , text + , haskoin-core + , secp256k1-haskell build-tool-depends: c2hs:c2hs default-language: Haskell2010 @@ -74,5 +76,8 @@ test-suite zcash-haskell-test , hspec , text , zcash-haskell + , binary + , cryptonite + , secp256k1-haskell pkgconfig-depends: rustzcash_wrapper default-language: Haskell2010 From 2ae18463fc8d56ad2c6da8a5097cfc87bb22a1f4 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 3 Mar 2024 15:29:50 -0600 Subject: [PATCH 029/149] Increase verbosity of Rust compile --- Setup.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Setup.hs b/Setup.hs index 81a2622..968fd0c 100644 --- a/Setup.hs +++ b/Setup.hs @@ -55,7 +55,7 @@ rsFolder = "librustzcash-wrapper" execCargo :: Verbosity -> String -> [String] -> IO () execCargo verbosity command args = do cargoPath <- - findProgramOnSearchPath Verbosity.silent defaultProgramSearchPath "cargo" + findProgramOnSearchPath Verbosity.normal defaultProgramSearchPath "cargo" dir <- getCurrentDirectory let cargoExec = case cargoPath of From 53a5b62b6bec0a8e09f454a1418d60ddaffbdd6a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 4 Mar 2024 11:58:04 -0600 Subject: [PATCH 030/149] Correct casing on Rust code --- librustzcash-wrapper/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 4b9e27a..0fee899 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -326,8 +326,8 @@ pub extern "C" fn rust_wrapper_bech32decode( out_len: &mut usize ) { let input: String = marshall_from_haskell_var(input, input_len, RW); - let decodedBytes = bech32::decode(&input); - match decodedBytes { + let decoded_bytes = bech32::decode(&input); + match decoded_bytes { Ok((hrp, bytes)) => { let rd = RawData {hrp: hrp.as_bytes().to_vec(), bytes}; marshall_to_haskell_var(&rd, out, out_len, RW); From 0b1d9e09203ef214b61104de01412250fa2e5fd5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 4 Mar 2024 11:59:07 -0600 Subject: [PATCH 031/149] Implement UA encoding per ZIP-316 --- src/C/Zcash.chs | 8 ++++++++ src/ZcashHaskell/Orchard.hs | 34 ++++++++++++++++++++++++++++++++++ src/ZcashHaskell/Types.hs | 12 ++++++------ src/ZcashHaskell/Utils.hs | 5 +++++ test/Spec.hs | 15 +++++++++++---- 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index f93c6c9..0013f83 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -38,6 +38,14 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (T.Text)'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble { toBorshVar* `BS.ByteString'& , getVarBuffer `Buffer (BS.ByteString)'& diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 1472c9c..f0dba87 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE OverloadedStrings #-} + -- Copyright 2022-2024 Vergara Technologies LLC -- -- This file is part of Zcash-Haskell. @@ -22,8 +24,13 @@ import C.Zcash , rustWrapperUfvkDecode ) import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as C +import qualified Data.Text as T +import qualified Data.Text.Encoding as E +import Data.Word import Foreign.Rust.Marshall.Variable import ZcashHaskell.Types +import ZcashHaskell.Utils (encodeBech32m, f4Jumble) -- | Checks if given bytestring is a valid encoded unified address isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress @@ -49,6 +56,33 @@ isValidUnifiedAddress str = then Just $ TransparentAddress P2SH whichNet (raw_to x) else Nothing) +-- | Encode a 'UnifiedAddress' per [ZIP-316](https://zips.z.cash/zip-0316) +encodeUnifiedAddress :: UnifiedAddress -> T.Text +encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b + where + hr = + case ua_net ua of + MainNet -> "u" + TestNet -> "utest" + b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding + tReceiver = + case t_rec ua of + Nothing -> BS.empty + Just t -> + case ta_type t of + P2SH -> packReceiver 0x01 $ ta_bytes t + P2PKH -> packReceiver 0x00 $ ta_bytes t + sReceiver = packReceiver 0x02 $ s_rec ua + oReceiver = packReceiver 0x03 $ o_rec ua + padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr + packReceiver :: Word8 -> BS.ByteString -> BS.ByteString + packReceiver typeCode receiver = + if BS.length receiver > 1 + then BS.singleton typeCode `BS.append` + (BS.singleton . toEnum . BS.length) receiver `BS.append` + receiver + else BS.empty + -- | Attempts to decode the given bytestring into a Unified Full Viewing Key decodeUfvk :: BS.ByteString -> Maybe UnifiedFullViewingKey decodeUfvk str = diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 7650dbb..356bdc4 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -186,12 +186,12 @@ data TransparentAddress = TransparentAddress -- * Sapling -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ShieldedOutput = ShieldedOutput - { s_cv :: HexString -- ^ Value commitment to the input note - , s_cmu :: HexString -- ^ The u-coordinate of the note commitment for the output note - , s_ephKey :: HexString -- ^ Ephemeral Jubjub public key - , s_encCipherText :: HexString -- ^ The output note encrypted to the recipient - , s_outCipherText :: HexString -- ^ A ciphertext enabling the sender to recover the output note - , s_proof :: HexString -- ^ Zero-knowledge proof using the Sapling Output circuit + { s_cv :: !HexString -- ^ Value commitment to the input note + , s_cmu :: !HexString -- ^ The u-coordinate of the note commitment for the output note + , s_ephKey :: !HexString -- ^ Ephemeral Jubjub public key + , s_encCipherText :: !HexString -- ^ The output note encrypted to the recipient + , s_outCipherText :: !HexString -- ^ A ciphertext enabling the sender to recover the output note + , s_proof :: !HexString -- ^ Zero-knowledge proof using the Sapling Output circuit } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 853857c..838afb1 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -19,6 +19,7 @@ module ZcashHaskell.Utils where import C.Zcash ( rustWrapperBech32Decode + , rustWrapperBech32Encode , rustWrapperF4Jumble , rustWrapperF4UnJumble ) @@ -35,6 +36,10 @@ import ZcashHaskell.Types decodeBech32 :: BS.ByteString -> RawData decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode +-- | Encode the given Human Readable Part and bytestring as a Bech32m string +encodeBech32m :: BS.ByteString -> BS.ByteString -> T.Text +encodeBech32m h d = withPureBorshVarBuffer $ rustWrapperBech32Encode h d + -- | Apply the F4Jumble transformation to the given bytestring f4Jumble :: BS.ByteString -> BS.ByteString f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble diff --git a/test/Spec.hs b/test/Spec.hs index c387f83..08e08e4 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -60,11 +60,14 @@ main :: IO () main = do hspec $ do describe "Bech32" $ do - let s = "bech321qqqsyrhqy2a" + let s = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu" let decodedString = decodeBech32 s - it "hrp matches" $ do hrp decodedString `shouldBe` "bech32" - it "data matches" $ do + it "hrp matches" $ do hrp decodedString `shouldBe` "abc" + xit "data matches" $ do bytes decodedString `shouldBe` BS.pack ([0x00, 0x01, 0x02] :: [Word8]) + it "encoding works" $ do + encodeBech32m "abc" (bytes decodedString) `shouldBe` + E.decodeUtf8Lenient s describe "F4Jumble" $ do it "jumble a string" $ do let input = @@ -337,6 +340,11 @@ main = do let ua = "u1salpdyefbreakingtheaddressh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur" isValidUnifiedAddress ua `shouldBe` Nothing + it "encodes UA correctly" $ do + let ua = + "u1salpdyefywvsg2dlmxg9589yznh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur" + (encodeUnifiedAddress <$> isValidUnifiedAddress ua) `shouldBe` + Just (E.decodeUtf8Lenient ua) describe "Decode UVK from YWallet" $ do let uvk = "uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm" @@ -440,7 +448,6 @@ main = do describe "Wallet seed phrase" $ do it "Generate phrase" $ do p <- generateWalletSeedPhrase - print p BS.length p `shouldNotBe` 0 it "Derive seed" $ do p <- generateWalletSeedPhrase From a6c358cd5d87d1c462bd8e612554f0ea047df3df Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Mon, 4 Mar 2024 18:46:39 -0500 Subject: [PATCH 032/149] Commit for debuggin process Erros with Parameters for rustWrapperSaplingSpendingkey function x in Sapling.hs module --- src/C/Zcash.chs | 7 +++++-- src/ZcashHaskell/Sapling.hs | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index bfaab7b..0526a45 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -127,7 +127,10 @@ import ZcashHaskell.Types #} {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey - { toBorshVar* `SaplingSKeyParams'& + { toBorshVar* `BS.ByteString'& + , toBorshFixed Int + , toBorshFixed Int + , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' -#} +#} \ No newline at end of file diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index a0ca034..1f564f9 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -42,7 +42,6 @@ import ZcashHaskell.Types , SaplingSKeyParams(..) ) import ZcashHaskell.Utils - import Data.Word -- | Check if given bytesting is a valid encoded shielded address @@ -98,6 +97,5 @@ genSaplingSpendingKey seed coin_type account_id = do then Just res else Nothing where - let params = SaplingSKeyParams seed coin_type account_id - res = (withPureBorshVarBuffer . rustWrapperSaplingSpendingkey) params + res = (withPureBorshVarBuffer . rustWrapperSaplingSpendingkey) seed coin_type account_id From 9acbe5b98c48c64868f3d5db34fdbd28eaf5e9e0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 5 Mar 2024 07:36:45 -0600 Subject: [PATCH 033/149] Fix compilation for Spending Key --- librustzcash-wrapper/src/lib.rs | 5 +++++ src/C/Zcash.chs | 6 +++--- src/ZcashHaskell/Sapling.hs | 20 ++++++++++---------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 045a7b9..702879b 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -612,9 +612,14 @@ pub extern "C" fn rust_wrapper_recover_seed( pub extern "C" fn rust_wrapper_sapling_spendingkey( input: *const u8, input_len: usize, + coin: u32, + acc_id: u32, out: *mut u8, out_len: &mut usize ){ + let seed: Vec = marshall_from_haskell_var(input, input_len, RW); + let fake_response = "It's working"; + marshall_to_haskell_var(&fake_response.as_bytes().to_vec(), out, out_len, RW); // - Retrieve parameters // let extsk = sapling::spending_key(&seed[0..32], diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 0526a45..f0ad1b8 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -128,9 +128,9 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey { toBorshVar* `BS.ByteString'& - , toBorshFixed Int - , toBorshFixed Int + , `Int' + , `Int' , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' -#} \ No newline at end of file +#} diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 1f564f9..81d6507 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -21,15 +21,16 @@ import C.Zcash ( rustWrapperIsShielded , rustWrapperSaplingCheck , rustWrapperSaplingNoteDecode + , rustWrapperSaplingSpendingkey , rustWrapperSaplingVkDecode , rustWrapperTxParse - , rustWrapperSaplingSpendingkey ) import Data.Aeson import qualified Data.ByteString as BS -import Data.ByteString.Lazy as BL +import Data.ByteString.Lazy as BL import Data.HexString (HexString(..), toBytes) -import Foreign.Rust.Marshall.Variable +import Data.Word +import Foreign.Rust.Marshall.Variable ( withPureBorshVarBuffer , withPureBorshVarBuffer ) @@ -37,12 +38,11 @@ import ZcashHaskell.Types ( DecodedNote(..) , RawData(..) , RawTxResponse(..) + , SaplingSKeyParams(..) , ShieldedOutput(..) , decodeHexText - , SaplingSKeyParams(..) ) -import ZcashHaskell.Utils -import Data.Word +import ZcashHaskell.Utils -- | Check if given bytesting is a valid encoded shielded address isValidShieldedAddress :: BS.ByteString -> Bool @@ -90,12 +90,12 @@ instance FromJSON RawTxResponse where pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b -- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID -genSaplingSpendingKey :: - BS.ByteString -> Word32 -> Word32 -> Maybe BS.ByteString +genSaplingSpendingKey :: BS.ByteString -> Int -> Int -> Maybe BS.ByteString genSaplingSpendingKey seed coin_type account_id = do if BS.length res > 0 then Just res else Nothing where - res = (withPureBorshVarBuffer . rustWrapperSaplingSpendingkey) seed coin_type account_id - + res = + withPureBorshVarBuffer + (rustWrapperSaplingSpendingkey seed coin_type account_id) From f09c3c500ba072e0c14177f67ecc2f4ec2f5dfa6 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 5 Mar 2024 14:43:33 -0600 Subject: [PATCH 034/149] Update versions of Zcash crates --- librustzcash-wrapper/Cargo.lock | 474 ++++++++++++-------------------- librustzcash-wrapper/Cargo.toml | 9 +- librustzcash-wrapper/src/lib.rs | 40 ++- 3 files changed, 203 insertions(+), 320 deletions(-) diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 2672c97..7f59103 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -58,12 +58,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base64" version = "0.21.2" @@ -123,6 +117,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "bitvec" version = "1.0.1" @@ -288,10 +288,14 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.13.0" +name = "bs58" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "sha2 0.10.6", + "tinyvec", +] [[package]] name = "byteorder" @@ -361,12 +365,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "const_fn" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" - [[package]] name = "constant_time_eq" version = "0.2.5" @@ -441,6 +439,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.9.0" @@ -461,12 +468,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "either" version = "1.8.1" @@ -710,11 +711,11 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" +checksum = "361c467824d4d9d4f284be4b2608800839419dccc4d4608f28345237fe354623" dependencies = [ - "serde", + "either", ] [[package]] @@ -765,12 +766,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - [[package]] name = "jubjub" version = "0.10.0" @@ -891,6 +886,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -934,9 +935,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6f418f2c25573923f81a091f38b4b19bc20f6c92b5070fb8f0711e64a2b998" +checksum = "5d31e68534df32024dcc89a8390ec6d7bef65edd87d91b45cfb481a2eb2d77c5" dependencies = [ "aes", "bitvec", @@ -960,6 +961,36 @@ dependencies = [ "zcash_note_encryption", ] +[[package]] +name = "orchard" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" +dependencies = [ + "aes", + "bitvec", + "blake2b_simd", + "ff", + "fpe", + "group", + "halo2_gadgets", + "halo2_proofs", + "hex", + "incrementalmerkletree", + "lazy_static", + "memuse", + "nonempty", + "pasta_curves", + "rand", + "reddsa", + "serde", + "subtle", + "tracing", + "zcash_note_encryption", + "zcash_spec", + "zip32", +] + [[package]] name = "pairing" version = "0.23.0" @@ -1038,6 +1069,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1046,12 +1083,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 1.0.109", + "syn 2.0.32", ] [[package]] @@ -1063,12 +1100,6 @@ dependencies = [ "toml", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "1.0.66" @@ -1080,9 +1111,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.9" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", "prost-derive", @@ -1090,53 +1121,53 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.9" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" dependencies = [ "bytes", "heck", "itertools", - "lazy_static", "log", "multimap", + "once_cell", "petgraph", "prettyplease", "prost", "prost-types", "regex", - "syn 1.0.109", + "syn 2.0.32", "tempfile", "which", ] [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.32", ] [[package]] name = "prost-types" -version = "0.11.9" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ "prost", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1223,7 +1254,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1243,7 +1274,7 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.32", ] [[package]] @@ -1261,22 +1292,13 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -1292,20 +1314,15 @@ dependencies = [ "borsh 0.10.3", "f4jumble", "haskell-ffi", - "orchard", + "orchard 0.7.1", "proc-macro2", - "zcash_address", + "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", "zcash_primitives", + "zip32", ] -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1321,67 +1338,26 @@ dependencies = [ "zeroize", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" -version = "1.0.159" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.32", ] -[[package]] -name = "serde_json" -version = "1.0.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sha2" version = "0.9.9" @@ -1406,76 +1382,30 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shardtree" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19f96dde3a8693874f7e7c53d95616569b4009379a903789efbd448f4ea9cc7" +dependencies = [ + "bitflags 2.4.2", + "either", + "incrementalmerkletree", + "tracing", +] + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "subtle" version = "2.4.1" @@ -1495,9 +1425,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -1541,46 +1471,27 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.32", ] [[package]] name = "time" -version = "0.2.27" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", ] [[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" +name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tinyvec" @@ -1608,15 +1519,15 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ "prettyplease", "proc-macro2", "prost-build", "quote", - "syn 1.0.109", + "syn 2.0.32", ] [[package]] @@ -1639,7 +1550,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.32", ] [[package]] @@ -1706,60 +1617,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.13", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.13", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - [[package]] name = "which" version = "4.4.0" @@ -1771,28 +1628,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" @@ -1875,36 +1710,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "804268e702b664fc09d3e2ce82786d0addf4ae57ba6976469be63e09066bf9f7" dependencies = [ "bech32 0.8.1", - "bs58", + "bs58 0.4.0", + "f4jumble", + "zcash_encoding", +] + +[[package]] +name = "zcash_address" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bce173f1d9ed4f806e310bc3a873301531e7a6dc209928584d6404e3f8228ef4" +dependencies = [ + "bech32 0.9.1", + "bs58 0.5.0", "f4jumble", "zcash_encoding", ] [[package]] name = "zcash_client_backend" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55db8d2cb4ca82a71fa66ccd9fa5b211f5ab90c866721311ddd85f8f90d0701" +checksum = "d6a382af39be9ee5a3788157145c404b7cd19acc440903f6c34b09fb44f0e991" dependencies = [ "base64", "bech32 0.9.1", "bls12_381", - "bs58", + "bs58 0.5.0", "crossbeam-channel", "group", + "hex", + "incrementalmerkletree", "memuse", "nom", - "orchard", + "orchard 0.6.0", "percent-encoding", "prost", "rayon", "secrecy", + "shardtree", "subtle", "time", "tonic-build", "tracing", "which", - "zcash_address", + "zcash_address 0.3.1", "zcash_encoding", "zcash_note_encryption", "zcash_primitives", @@ -1922,9 +1772,9 @@ dependencies = [ [[package]] name = "zcash_note_encryption" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb2149e6cd5fbee36c5b87c601715a8c35554602f7fe84af38b636afa2db318" +checksum = "5b4580cd6cee12e44421dac43169be8d23791650816bdb34e6ddfa70ac89c1c5" dependencies = [ "chacha20", "chacha20poly1305", @@ -1935,9 +1785,9 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914d2195a478d5b63191584dff126f552751115181857b290211ec88e68acc3e" +checksum = "d17e4c94ca8d69d2fcf2be97522da5732a580eb2125cda3b150761952f8df8e6" dependencies = [ "aes", "bip0039", @@ -1956,16 +1806,25 @@ dependencies = [ "lazy_static", "memuse", "nonempty", - "orchard", + "orchard 0.6.0", "rand", "rand_core", "sha2 0.10.6", "subtle", - "zcash_address", + "zcash_address 0.3.1", "zcash_encoding", "zcash_note_encryption", ] +[[package]] +name = "zcash_spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" +dependencies = [ + "blake2b_simd", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -1983,5 +1842,16 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.32", +] + +[[package]] +name = "zip32" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22" +dependencies = [ + "blake2b_simd", + "memuse", + "subtle", ] diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 49fbb9a..9c8f966 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -11,10 +11,11 @@ f4jumble = "0.1" zcash_address = "0.2.0" borsh = "0.10" bech32 = "0.11" -orchard = "0.4.0" -zcash_note_encryption = "0.3.0" -zcash_primitives = "0.11.0" -zcash_client_backend = "0.9.0" +orchard = "0.7.0" +zcash_note_encryption = "0.4.0" +zcash_primitives = "0.13.0" +zcash_client_backend = "0.10.0" +zip32 = "0.1.0" proc-macro2 = "1.0.66" [features] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 0fee899..ed160e6 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -9,7 +9,6 @@ use std::{ Write, Cursor }, - fmt::{Debug, Display, Formatter} }; use f4jumble; @@ -23,24 +22,19 @@ use haskell_ffi::{ FromHaskell, HaskellSize, ToHaskell }; +use zip32; + use zcash_primitives::{ zip32::Scope as SaplingScope, zip339::{Count, Mnemonic}, transaction::components::sapling::{ - read_zkproof, GrothProofBytes, OutputDescription, - CompactOutputDescription }, sapling::{ - value::ValueCommitment as SaplingValueCommitment, - keys::{ - FullViewingKey as SaplingViewingKey, - PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey - }, - note_encryption::SaplingDomain, PaymentAddress, - note::ExtractedNoteCommitment as SaplingExtractedNoteCommitment + keys::PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, + note_encryption::SaplingDomain }, transaction::Transaction, consensus::{ @@ -60,17 +54,14 @@ use zcash_client_backend::keys::sapling::ExtendedFullViewingKey; use orchard::{ Action, - keys::{FullViewingKey, PreparedIncomingViewingKey, Scope}, + keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, value::ValueCommitment }; -use zcash_note_encryption::EphemeralKeyBytes; - use bech32::{ - decode, Hrp, Bech32m }; @@ -620,3 +611,24 @@ pub extern "C" fn rust_wrapper_recover_seed( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_derive_orchard_spending_key( + seed: *const u8, + seed_len: usize, + coin_type: u32, + acc_id: u32, + out: *mut u8, + out_len: &mut usize + ){ + let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); + let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); + match sk { + Ok(key) => { + marshall_to_haskell_var(&key.to_bytes(), out, out_len, RW); + }, + Err(_e) => { + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } + } +} From 694ab8fd172ba0328f94c5f73d6ca0a9e3167585 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 5 Mar 2024 14:44:00 -0600 Subject: [PATCH 035/149] Add new Orchard spending key generation --- src/C/Zcash.chs | 9 +++++++++ src/ZcashHaskell/Orchard.hs | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 0013f83..84a641b 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -133,3 +133,12 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_derive_orchard_spending_key as rustWrapperGenOrchardSpendKey + { toBorshVar* `BS.ByteString'& + , `Word32' + , `Word32' + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index f0dba87..ffe5248 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -18,7 +18,8 @@ module ZcashHaskell.Orchard where import C.Zcash - ( rustWrapperOrchardCheck + ( rustWrapperGenOrchardSpendKey + , rustWrapperOrchardCheck , rustWrapperOrchardNoteDecode , rustWrapperUADecode , rustWrapperUfvkDecode @@ -32,6 +33,12 @@ import Foreign.Rust.Marshall.Variable import ZcashHaskell.Types import ZcashHaskell.Utils (encodeBech32m, f4Jumble) +-- | Derives an Orchard spending key for the given seed and account ID +genOrchardSpendingKey :: Seed -> CoinType -> AccountId -> BS.ByteString +genOrchardSpendingKey s coinType accountId = + withPureBorshVarBuffer $ + rustWrapperGenOrchardSpendKey s (getValue coinType) (fromIntegral accountId) + -- | Checks if given bytestring is a valid encoded unified address isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress isValidUnifiedAddress str = From 53716685a89ab65404820eaf5ae9a08349e9961a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 5 Mar 2024 14:44:19 -0600 Subject: [PATCH 036/149] Add new types for constants for Zcash protocol --- src/ZcashHaskell/Types.hs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 356bdc4..11668d1 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -61,6 +61,22 @@ data ZcashNet | RegTestNet deriving (Eq, Prelude.Show, Read) +type AccountId = Int + +-- ** Constants +-- | Types for coin types on the different networks +data CoinType + = MainNetCoin + | TestNetCoin + | RegTestNetCoin + +getValue :: CoinType -> Word32 +getValue c = + case c of + MainNetCoin -> 133 + TestNetCoin -> 1 + RegTestNetCoin -> 1 + -- * RPC -- | A type to model Zcash RPC calls data RpcCall = RpcCall From 3822e9f2ff966f9c271e9b5a316ec016168fd172 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 5 Mar 2024 15:09:35 -0600 Subject: [PATCH 037/149] Add tests for Orchard spending key --- librustzcash-wrapper/src/lib.rs | 2 +- src/ZcashHaskell/Orchard.hs | 14 +++++++++++--- test/Spec.hs | 7 +++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index ed160e6..3b7297a 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -625,7 +625,7 @@ pub extern "C" fn rust_wrapper_derive_orchard_spending_key( let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); match sk { Ok(key) => { - marshall_to_haskell_var(&key.to_bytes(), out, out_len, RW); + marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW); }, Err(_e) => { marshall_to_haskell_var(&vec![0], out, out_len, RW); diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index ffe5248..9d33959 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -34,10 +34,18 @@ import ZcashHaskell.Types import ZcashHaskell.Utils (encodeBech32m, f4Jumble) -- | Derives an Orchard spending key for the given seed and account ID -genOrchardSpendingKey :: Seed -> CoinType -> AccountId -> BS.ByteString +genOrchardSpendingKey :: Seed -> CoinType -> AccountId -> Maybe BS.ByteString genOrchardSpendingKey s coinType accountId = - withPureBorshVarBuffer $ - rustWrapperGenOrchardSpendKey s (getValue coinType) (fromIntegral accountId) + if BS.length k /= 32 + then Nothing + else Just k + where + k = + withPureBorshVarBuffer $ + rustWrapperGenOrchardSpendKey + s + (getValue coinType) + (fromIntegral accountId) -- | Checks if given bytestring is a valid encoded unified address isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress diff --git a/test/Spec.hs b/test/Spec.hs index 08e08e4..c06a689 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -19,6 +19,7 @@ {-# LANGUAGE OverloadedStrings #-} import C.Zcash (rustWrapperUADecode) +import Control.Monad.IO.Class (liftIO) import Data.Aeson import Data.Bool (Bool(True)) import qualified Data.ByteString as BS @@ -45,6 +46,7 @@ import ZcashHaskell.Sapling import ZcashHaskell.Transparent (encodeTransparent) import ZcashHaskell.Types ( BlockResponse(..) + , CoinType(..) , DecodedNote(..) , OrchardAction(..) , RawData(..) @@ -453,6 +455,11 @@ main = do p <- generateWalletSeedPhrase let s = getWalletSeed p maybe 0 BS.length s `shouldBe` 64 + it "Generate Orchard spending key" $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + genOrchardSpendingKey (fromMaybe "" s) MainNetCoin 1 `shouldNotBe` + Nothing describe "Address tests" $ do it "Encode transparent" $ do let ua = From c13d321da5784f9f93b5a289d88bc4b49137510f Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 5 Mar 2024 15:09:57 -0600 Subject: [PATCH 038/149] Bump version --- CHANGELOG.md | 2 ++ zcash-haskell.cabal | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbea7b4..6a768c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implementations of `Read` for types - Function to make RPC calls to `zebrad` - Function to encode unified addresses from receivers +- Function to generate an Orchard spending key +- Constants for Zcash protocol ### Changed diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index ad2e0d3..e4a2f3b 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.2.3 +version: 0.4.3.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 7b65d322e68ebee907b8da6ad5237490206fc705 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Tue, 5 Mar 2024 17:01:17 -0500 Subject: [PATCH 039/149] Commit 003 - before merging with dev040 - with orchard spending key --- src/C/Zcash.chs | 4 ++-- src/ZcashHaskell/Sapling.hs | 2 +- test/Spec.hs | 11 +++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index f0ad1b8..fcabf7c 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -128,8 +128,8 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey { toBorshVar* `BS.ByteString'& - , `Int' - , `Int' + , `Word32' + , `Word32' , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 81d6507..d32668e 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -90,7 +90,7 @@ instance FromJSON RawTxResponse where pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b -- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID -genSaplingSpendingKey :: BS.ByteString -> Int -> Int -> Maybe BS.ByteString +genSaplingSpendingKey :: BS.ByteString -> Word32 -> Word32 -> Maybe BS.ByteString genSaplingSpendingKey seed coin_type account_id = do if BS.length res > 0 then Just res diff --git a/test/Spec.hs b/test/Spec.hs index c9ceab8..8032b6d 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -62,6 +62,10 @@ import Foreign.C.Types import Data.Word import Haskoin.Crypto.Keys.Extended +m2bs :: Maybe BS.ByteString -> BS.ByteString +m2bs x = fromMaybe "" x + + main :: IO () main = do hspec $ do @@ -512,5 +516,8 @@ main = do ] :: [Word8] let coin = 1 :: Word32 let account = 0 :: Word32 - res <- genSaplingSpendingKey (word8ArrayToByteString hdseed) coin account - res `shouldBe` "genSaplingSpendingKey Function called." \ No newline at end of file + let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) coin account + case msg of + Nothing -> "Bad response from genSapingSpendingKey" + Just msg -> fromMaybe "BadResponse" msg + "mm " `shouldBe` "genSaplingSpendingKey Function called." \ No newline at end of file From 0e6f7503d4947e57b657e93210a2cec9737f28d9 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Tue, 5 Mar 2024 22:10:05 -0500 Subject: [PATCH 040/149] Sapling Extended Spemding Key generation from HDSedd (64 byte array) --- librustzcash-wrapper/Cargo.lock | 1 + librustzcash-wrapper/Cargo.toml | 1 + librustzcash-wrapper/src/lib.rs | 35 ++++++++++++++++++++------------- src/ZcashHaskell/Sapling.hs | 15 ++++++-------- test/Spec.hs | 18 ++++++++++------- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 7f59103..bc4b733 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -1314,6 +1314,7 @@ dependencies = [ "borsh 0.10.3", "f4jumble", "haskell-ffi", + "nom", "orchard 0.7.1", "proc-macro2", "zcash_address 0.2.0", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 9c8f966..d7b2495 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -17,6 +17,7 @@ zcash_primitives = "0.13.0" zcash_client_backend = "0.10.0" zip32 = "0.1.0" proc-macro2 = "1.0.66" +nom = "7.1.3" [features] capi = [] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index bfb2794..8963325 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -49,7 +49,11 @@ use zcash_address::{ ZcashAddress }; -use zcash_client_backend::keys::{sapling, sapling::ExtendedFullViewingKey}; +use zcash_client_backend::keys::{ + sapling, + sapling::ExtendedFullViewingKey, + sapling::ExtendedSpendingKey}; + use zcash_primitives::zip32::AccountId; use std::slice; @@ -615,24 +619,27 @@ pub extern "C" fn rust_wrapper_recover_seed( #[no_mangle] pub extern "C" fn rust_wrapper_sapling_spendingkey( - in_seed: *const u8, - seed_len: usize, + iseed: *const u8, + iseed_len: usize, coin_type: u32, acc_id: u32, out: *mut u8, out_len: &mut usize ){ - let seed: Vec = marshall_from_haskell_var(in_seed, input_len, RW); - let fake_response = "It's working"; - marshall_to_haskell_var(&fake_response.as_bytes().to_vec(), out, out_len, RW); - -// let extsk = sapling::spending_key(&seed[0..32], -// cointype, -// accountid); -// println!("SpendingKey -> {:?}", extsk); -// let s = extsk.to_bytes(); -// let xsk : Vec = s.iter().cloned().collect(); -// marshall_to_haskell_var(&xsk, out, out_len, RW); + println!("From Rust\n========="); + let seed: Vec = marshall_from_haskell_var(iseed, iseed_len, RW); + if ( seed.len() <= 0 ) { + println!("Seed error, returning a null vector..."); + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } else { + println!("Seed in rust : {:?}\n", seed); + println!("Coin Type -> {}\nAccount Id -> {}",coin_type,acc_id); + let su8 = &seed; + let seedu8 : &[u8] = &su8; + let extsk: ExtendedSpendingKey = sapling::ExtendedSpendingKey::master(&seedu8); + let extsk_bytes = extsk.to_bytes().to_vec(); + marshall_to_haskell_var(&extsk_bytes, out, out_len, RW); + } } #[no_mangle] diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index d32668e..c34314c 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -41,6 +41,8 @@ import ZcashHaskell.Types , SaplingSKeyParams(..) , ShieldedOutput(..) , decodeHexText + , AccountId + , CoinType ) import ZcashHaskell.Utils @@ -88,14 +90,9 @@ instance FromJSON RawTxResponse where Just o' -> do a <- o' .: "actions" pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b - +-- -- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID -genSaplingSpendingKey :: BS.ByteString -> Word32 -> Word32 -> Maybe BS.ByteString +genSaplingSpendingKey :: BS.ByteString -> Word32-> AccountId -> BS.ByteString genSaplingSpendingKey seed coin_type account_id = do - if BS.length res > 0 - then Just res - else Nothing - where - res = - withPureBorshVarBuffer - (rustWrapperSaplingSpendingkey seed coin_type account_id) + let res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed (fromIntegral coin_type) (fromIntegral account_id) ) + res \ No newline at end of file diff --git a/test/Spec.hs b/test/Spec.hs index 9f85b03..abfd17c 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -57,6 +57,9 @@ import ZcashHaskell.Types , UnifiedAddress(..) , UnifiedFullViewingKey(..) , decodeHexText + , AccountId + , CoinType + , getValue ) import ZcashHaskell.Utils @@ -528,10 +531,11 @@ main = do 241, 243, 172, 178, 104, 81, 159, 144 ] :: [Word8] - let coin = 1 :: Word32 - let account = 0 :: Word32 - let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) coin account - case msg of - Nothing -> "Bad response from genSapingSpendingKey" - Just msg -> fromMaybe "BadResponse" msg - "mm " `shouldBe` "genSaplingSpendingKey Function called." \ No newline at end of file + let cointype = getValue TestNetCoin + let account = 0 :: AccountId + let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) cointype account + let msgArr = BS.unpack msg + if (length msgArr) == 169 + then True + else False +-- msgArr `shouldBe` "It's working." \ No newline at end of file From d118a839933d056491ae5e177a453f0441dd6ae9 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 6 Mar 2024 13:05:00 -0600 Subject: [PATCH 041/149] Add constants to library --- src/ZcashHaskell/Transparent.hs | 11 ++----- src/ZcashHaskell/Types.hs | 53 ++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 6de9200..b7bac10 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -26,19 +26,12 @@ import ZcashHaskell.Types ( TransparentAddress(..) , TransparentType(..) , ZcashNet(..) + , getTransparentPrefix ) encodeTransparent :: TransparentAddress -> T.Text encodeTransparent t = - case ta_type t of - P2SH -> - case ta_net t of - MainNet -> encodeTransparent' (0x1c, 0xbd) $ ta_bytes t - _ -> encodeTransparent' (0x1c, 0xba) $ ta_bytes t - P2PKH -> - case ta_net t of - MainNet -> encodeTransparent' (0x1c, 0xb8) $ ta_bytes t - _ -> encodeTransparent' (0x1d, 0x25) $ ta_bytes t + encodeTransparent' (getTransparentPrefix (ta_net t) (ta_type t)) $ ta_bytes t where encodeTransparent' :: (Word8, Word8) -> BS.ByteString -> T.Text encodeTransparent' (a, b) h = diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 11668d1..374474d 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -64,7 +64,7 @@ data ZcashNet type AccountId = Int -- ** Constants --- | Types for coin types on the different networks +-- | Type for coin types on the different networks data CoinType = MainNetCoin | TestNetCoin @@ -77,6 +77,57 @@ getValue c = TestNetCoin -> 1 RegTestNetCoin -> 1 +-- | Type for Sapling Human-readable part +data SaplingHrp + = SapExtSpendingKey + | SapExtFullViewingKey + | SapPaymentAddress + | SapTestExtSpendingKey + | SapTestExtFullViewingKey + | SapTestPaymentAddress + +getSaplingHrp :: SaplingHrp -> String +getSaplingHrp s = + case s of + SapExtSpendingKey -> "secret-extended-key-main" + SapExtFullViewingKey -> "zxviews" + SapPaymentAddress -> "zs" + SapTestExtSpendingKey -> "secret-extended-key-test" + SapTestExtFullViewingKey -> "zxviewtestsapling" + SapTestPaymentAddress -> "ztestsapling" + +-- | Type for Unified Human-readable part +data UnifiedHrp + = UniPaymentAddress + | UniFullViewingKey + | UniIncomingViewingKey + | UniTestPaymentAddress + | UniTestFullViewingKey + | UniTestIncomingViewingKey + +getUnifiedHrp :: UnifiedHrp -> String +getUnifiedHrp s = + case s of + UniPaymentAddress -> "u" + UniFullViewingKey -> "uview" + UniIncomingViewingKey -> "uivk" + UniTestPaymentAddress -> "utest" + UniTestFullViewingKey -> "uviewtest" + UniTestIncomingViewingKey -> "uivktest" + +-- | Function to get the Base58 prefix for encoding a 'TransparentAddress' +getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) +getTransparentPrefix n t = + case t of + P2SH -> + case n of + MainNet -> (0x1c, 0xbd) + _ -> (0x1c, 0xba) + P2PKH -> + case n of + MainNet -> (0x1c, 0xb8) + _ -> (0x1d, 0x25) + -- * RPC -- | A type to model Zcash RPC calls data RpcCall = RpcCall From b568ee5ff4f93bc822e3f1b943e59c3f3b64174c Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 6 Mar 2024 15:02:48 -0600 Subject: [PATCH 042/149] Pin dependencies --- cabal.project | 1 + cabal.project.freeze | 207 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 cabal.project.freeze diff --git a/cabal.project b/cabal.project index 60a9728..a64c126 100644 --- a/cabal.project +++ b/cabal.project @@ -1,4 +1,5 @@ packages: ./*.cabal + with-compiler: ghc-9.4.8 source-repository-package diff --git a/cabal.project.freeze b/cabal.project.freeze new file mode 100644 index 0000000..49f5987 --- /dev/null +++ b/cabal.project.freeze @@ -0,0 +1,207 @@ +active-repositories: hackage.haskell.org:merge +constraints: any.Cabal ==3.8.1.0, + any.Cabal-syntax ==3.8.1.0, + any.HUnit ==1.6.2.0, + any.OneTuple ==0.4.1.1, + any.QuickCheck ==2.14.3, + QuickCheck -old-random +templatehaskell, + any.StateVar ==1.2.2, + any.aeson ==2.2.1.0, + aeson +ordered-keymap, + any.alex ==3.5.1.0, + any.ansi-terminal ==1.1, + ansi-terminal -example, + any.ansi-terminal-types ==1.1, + any.appar ==0.1.8, + any.array ==0.5.4.0, + any.asn1-encoding ==0.9.6, + any.asn1-parse ==0.9.5, + any.asn1-types ==0.3.4, + any.assoc ==1.1, + assoc +tagged, + any.async ==2.2.5, + async -bench, + any.attoparsec ==0.14.4, + attoparsec -developer, + any.attoparsec-aeson ==2.2.0.1, + any.base ==4.17.2.1, + any.base-orphans ==0.9.1, + any.base16 ==1.0, + any.base16-bytestring ==1.0.2.0, + any.base58-bytestring ==0.1.0, + any.base64-bytestring ==1.2.1.0, + any.basement ==0.0.16, + any.bifunctors ==5.6.1, + bifunctors +tagged, + any.binary ==0.8.9.1, + any.binary-orphans ==1.0.4.1, + any.bitvec ==1.1.5.0, + bitvec +simd, + any.blaze-builder ==0.4.2.3, + any.borsh ==0.3.0, + any.byteorder ==1.0.4, + any.bytes ==0.17.3, + any.bytestring ==0.11.5.3, + any.c2hs ==0.28.8, + c2hs +base3 -regression, + any.call-stack ==0.4.0, + any.case-insensitive ==1.2.1.0, + any.cborg ==0.2.10.0, + cborg +optimize-gmp, + any.cereal ==0.5.8.3, + cereal -bytestring-builder, + any.colour ==2.3.6, + any.comonad ==5.0.8, + comonad +containers +distributive +indexed-traversable, + any.conduit ==1.3.5, + any.conduit-extra ==1.3.6, + any.containers ==0.6.7, + any.contravariant ==1.5.5, + contravariant +semigroups +statevar +tagged, + any.cookie ==0.5.0, + any.crypton ==0.34, + crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes, + any.crypton-connection ==0.3.2, + any.crypton-x509 ==1.7.6, + any.crypton-x509-store ==1.6.9, + any.crypton-x509-system ==1.6.7, + any.crypton-x509-validation ==1.6.12, + any.cryptonite ==0.30, + cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes, + any.data-default ==0.7.1.1, + any.data-default-class ==0.1.2.0, + any.data-default-instances-containers ==0.0.1, + any.data-default-instances-dlist ==0.0.1, + any.data-default-instances-old-locale ==0.0.1, + any.data-fix ==0.3.2, + any.deepseq ==1.4.8.0, + any.directory ==1.3.7.1, + any.distributive ==0.6.2.1, + distributive +semigroups +tagged, + any.dlist ==1.0, + dlist -werror, + any.entropy ==0.4.1.10, + entropy -donotgetentropy, + any.envy ==2.1.2.0, + any.exceptions ==0.10.5, + any.filepath ==1.4.2.2, + any.foldable1-classes-compat ==0.1, + foldable1-classes-compat +tagged, + any.foreign-rust ==0.1.0, + any.generically ==0.1.1, + any.generics-sop ==0.5.1.4, + any.ghc-bignum ==1.3, + any.ghc-boot-th ==9.4.8, + any.ghc-prim ==0.9.1, + any.half ==0.3.1, + any.happy ==1.20.1.1, + any.hashable ==1.4.3.0, + hashable +integer-gmp -random-initial-seed, + any.haskell-lexer ==1.1.1, + any.haskoin-core ==1.0.4, + any.hexstring ==0.12.0, + any.hourglass ==0.2.12, + any.hsc2hs ==0.68.10, + hsc2hs -in-ghc-tree, + any.hspec ==2.11.7, + any.hspec-core ==2.11.7, + any.hspec-discover ==2.11.7, + any.hspec-expectations ==0.8.4, + any.http-client ==0.7.16, + http-client +network-uri, + any.http-client-tls ==0.3.6.3, + any.http-conduit ==2.3.8.3, + http-conduit +aeson, + any.http-types ==0.12.4, + any.indexed-traversable ==0.1.3, + any.indexed-traversable-instances ==0.1.1.2, + any.integer-conversion ==0.1.0.1, + any.integer-gmp ==1.1, + any.integer-logarithms ==1.0.3.1, + integer-logarithms -check-bounds +integer-gmp, + any.iproute ==1.7.12, + any.language-c ==0.9.3, + language-c -allwarnings +iecfpextension +usebytestrings, + any.memory ==0.18.0, + memory +support_bytestring +support_deepseq, + any.mime-types ==0.1.2.0, + any.mono-traversable ==1.0.17.0, + any.mtl ==2.2.2, + any.murmur3 ==1.0.5, + any.network ==3.1.4.0, + network -devel, + any.network-uri ==2.6.4.2, + any.old-locale ==1.0.0.7, + any.old-time ==1.1.0.4, + any.parsec ==3.1.16.1, + any.pem ==0.2.4, + any.pretty ==1.1.3.6, + any.primitive ==0.9.0.0, + any.process ==1.6.18.0, + any.quickcheck-io ==0.2.0, + any.random ==1.2.1.2, + any.regex-base ==0.94.0.2, + any.regex-compat ==0.95.2.1, + any.regex-posix ==0.96.0.1, + regex-posix -_regex-posix-clib, + any.resourcet ==1.3.0, + any.rts ==1.0.2, + any.safe ==0.3.21, + any.scientific ==0.3.7.0, + scientific -bytestring-builder -integer-simple, + any.secp256k1-haskell ==1.1.0, + any.semialign ==1.3, + semialign +semigroupoids, + any.semigroupoids ==6.0.0.1, + semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers, + any.serialise ==0.2.6.1, + serialise +newtime15, + any.socks ==0.6.1, + any.sop-core ==0.5.0.2, + any.split ==0.2.5, + any.splitmix ==0.1.0.5, + splitmix -optimised-mixer, + any.stm ==2.5.1.0, + any.streaming-commons ==0.2.2.6, + streaming-commons -use-bytestring-builder, + any.strict ==0.5, + any.string-conversions ==0.4.0.1, + any.tagged ==0.8.8, + tagged +deepseq +transformers, + any.template-haskell ==2.19.0.0, + any.text ==2.0.2, + any.text-iso8601 ==0.1, + any.text-short ==0.1.5, + text-short -asserts, + any.tf-random ==0.5, + any.th-abstraction ==0.6.0.0, + any.th-compat ==0.1.4, + any.these ==1.2, + any.time ==1.12.2, + any.time-compat ==1.9.6.1, + time-compat -old-locale, + any.tls ==2.0.1, + tls -devel, + any.transformers ==0.5.6.2, + any.transformers-compat ==0.7.2, + transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, + any.typed-process ==0.2.11.1, + any.unix ==2.7.3, + any.unix-time ==0.4.12, + any.unliftio-core ==0.2.1.0, + any.unordered-containers ==0.2.20, + unordered-containers -debug, + any.utf8-string ==1.0.2, + any.uuid-types ==1.0.5.1, + any.vector ==0.13.1.0, + vector +boundschecks -internalchecks -unsafechecks -wall, + any.vector-algorithms ==0.9.0.1, + vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks, + any.vector-stream ==0.1.0.1, + any.void ==0.7.3, + void -safe, + any.wide-word ==0.1.6.0, + any.witherable ==0.4.2, + any.zlib ==0.7.0.0, + zlib -bundled-c-zlib +non-blocking-ffi +pkg-config +index-state: hackage.haskell.org 2024-03-06T20:26:39Z From 977f4e791d0f2f8a3579bbcd760850e61931e9a9 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 6 Mar 2024 15:10:26 -0600 Subject: [PATCH 043/149] Implement Unified HRP constants --- src/ZcashHaskell/Orchard.hs | 4 +-- src/ZcashHaskell/Types.hs | 56 +++++++++++++++---------------------- zcash-haskell.cabal | 2 +- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 9d33959..d71ce64 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -77,8 +77,8 @@ encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b where hr = case ua_net ua of - MainNet -> "u" - TestNet -> "utest" + MainNet -> uniPaymentAddressHrp + TestNet -> uniTestPaymentAddressHrp b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding tReceiver = case t_rec ua of diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 374474d..f9fe6f2 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -77,43 +77,31 @@ getValue c = TestNetCoin -> 1 RegTestNetCoin -> 1 --- | Type for Sapling Human-readable part -data SaplingHrp - = SapExtSpendingKey - | SapExtFullViewingKey - | SapPaymentAddress - | SapTestExtSpendingKey - | SapTestExtFullViewingKey - | SapTestPaymentAddress +-- | Constants for Sapling Human-readable part +sapExtSpendingKeyHrp = "secret-extended-key-main" :: String -getSaplingHrp :: SaplingHrp -> String -getSaplingHrp s = - case s of - SapExtSpendingKey -> "secret-extended-key-main" - SapExtFullViewingKey -> "zxviews" - SapPaymentAddress -> "zs" - SapTestExtSpendingKey -> "secret-extended-key-test" - SapTestExtFullViewingKey -> "zxviewtestsapling" - SapTestPaymentAddress -> "ztestsapling" +sapExtFullViewingKeyHrp = "zxviews" :: String --- | Type for Unified Human-readable part -data UnifiedHrp - = UniPaymentAddress - | UniFullViewingKey - | UniIncomingViewingKey - | UniTestPaymentAddress - | UniTestFullViewingKey - | UniTestIncomingViewingKey +sapPaymentAddressHrp = "zs" :: String -getUnifiedHrp :: UnifiedHrp -> String -getUnifiedHrp s = - case s of - UniPaymentAddress -> "u" - UniFullViewingKey -> "uview" - UniIncomingViewingKey -> "uivk" - UniTestPaymentAddress -> "utest" - UniTestFullViewingKey -> "uviewtest" - UniTestIncomingViewingKey -> "uivktest" +sapTestExtSpendingKeyHrp = "secret-extended-key-test" :: String + +sapTestExtFullViewingKeyHrp = "zxviewtestsapling" :: String + +sapTestPaymentAddressHrp = "ztestsapling" :: String + +-- | Constants for Unified Human-readable part +uniPaymentAddressHrp = "u" :: T.Text + +uniFullViewingKeyHrp = "uview" :: T.Text + +uniIncomingViewingKeyHrp = "uivk" :: T.Text + +uniTestPaymentAddressHrp = "utest" :: T.Text + +uniTestFullViewingKeyHrp = "uviewtest" :: T.Text + +uniTestIncomingViewingKeyHrp = "uivktest" :: T.Text -- | Function to get the Base58 prefix for encoding a 'TransparentAddress' getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index e4a2f3b..d6d04ea 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.3.0 +version: 0.4.3.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From f1174751fc5eb4f5bb13e103573399b781a4e79b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 7 Mar 2024 16:05:59 -0600 Subject: [PATCH 044/149] Add new types for Spending Keys and Receivers --- src/ZcashHaskell/Types.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index f9fe6f2..38d190f 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -45,6 +45,18 @@ type Seed = C.ByteString -- | A mnemonic phrase used to derive seeds type Phrase = BS.ByteString +-- | A spending key for Sapling +type SaplingSpendingKey = BS.ByteString + +-- | A spending key for Orchard +type OrchardSpendingKey = BS.ByteString + +-- | A Sapling receiver +type SaplingReceiver = BS.ByteString + +-- | An Orchard receiver +type OrchardReceiver = BS.ByteString + -- | Type to represent data after Bech32 decoding data RawData = RawData { hrp :: !BS.ByteString -- ^ Human-readable part of the Bech32 encoding From 72e87577a772330b886ef01170c37dda29a0f62d Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 7 Mar 2024 16:06:33 -0600 Subject: [PATCH 045/149] Add generation of Orchard receivers --- librustzcash-wrapper/src/lib.rs | 16 ++++++++++++++++ src/C/Zcash.chs | 8 ++++++++ src/ZcashHaskell/Orchard.hs | 17 +++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 3b7297a..b3806a2 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -632,3 +632,19 @@ pub extern "C" fn rust_wrapper_derive_orchard_spending_key( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_derive_orchard_receiver( + spend_key: *const u8, + spend_key_len: usize, + add_id: u32, + out: *mut u8, + out_len: &mut usize + ){ + let sk_in: Vec = marshall_from_haskell_var(spend_key, spend_key_len, RW); + let sk = SpendingKey::from_bytes(sk_in[0..32].try_into().unwrap()).unwrap(); + let fvk = FullViewingKey::from(&sk); + let o_rec = fvk.address_at(add_id, Scope::External); + marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); + +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 84a641b..af0582a 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -142,3 +142,11 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_derive_orchard_receiver as rustWrapperGenOrchardReceiver + { toBorshVar* `BS.ByteString'& + , `Word32' + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index d71ce64..4a0f530 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -18,7 +18,8 @@ module ZcashHaskell.Orchard where import C.Zcash - ( rustWrapperGenOrchardSpendKey + ( rustWrapperGenOrchardReceiver + , rustWrapperGenOrchardSpendKey , rustWrapperOrchardCheck , rustWrapperOrchardNoteDecode , rustWrapperUADecode @@ -34,7 +35,8 @@ import ZcashHaskell.Types import ZcashHaskell.Utils (encodeBech32m, f4Jumble) -- | Derives an Orchard spending key for the given seed and account ID -genOrchardSpendingKey :: Seed -> CoinType -> AccountId -> Maybe BS.ByteString +genOrchardSpendingKey :: + Seed -> CoinType -> AccountId -> Maybe OrchardSpendingKey genOrchardSpendingKey s coinType accountId = if BS.length k /= 32 then Nothing @@ -47,6 +49,17 @@ genOrchardSpendingKey s coinType accountId = (getValue coinType) (fromIntegral accountId) +-- | Derives an Orchard receiver for the given spending key and index +genOrchardReceiver :: Int -> OrchardSpendingKey -> Maybe OrchardReceiver +genOrchardReceiver i osk = + if BS.length k /= 43 + then Nothing + else Just k + where + k = + withPureBorshVarBuffer $ + rustWrapperGenOrchardReceiver osk (fromIntegral i) + -- | Checks if given bytestring is a valid encoded unified address isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress isValidUnifiedAddress str = From b2f56941a43bfeb2c4f678b52b77b1a14616f993 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 7 Mar 2024 16:07:50 -0600 Subject: [PATCH 046/149] Bump version --- CHANGELOG.md | 2 ++ zcash-haskell.cabal | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a768c4..2a39f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Function to encode unified addresses from receivers - Function to generate an Orchard spending key - Constants for Zcash protocol +- Types for Spending Keys and Receivers for Sapling and Orchard +- Function to generate an Orchard receiver ### Changed diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index d6d04ea..b6675d1 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.3.1 +version: 0.4.3.2 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 6c2dfa02fa8a6be6975ecfc8ed0a730b5e57e81c Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 8 Mar 2024 12:44:10 -0600 Subject: [PATCH 047/149] Implement QuickCheck tests for Orchard components --- src/ZcashHaskell/Types.hs | 1 + test/Spec.hs | 60 +++++++++++++++++++++++++++++++-------- zcash-haskell.cabal | 2 ++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 38d190f..4bd0cd9 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -81,6 +81,7 @@ data CoinType = MainNetCoin | TestNetCoin | RegTestNetCoin + deriving (Eq, Prelude.Show, Ord) getValue :: CoinType -> Word32 getValue c = diff --git a/test/Spec.hs b/test/Spec.hs index c06a689..a4abbf2 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -17,6 +17,7 @@ -} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeSynonymInstances #-} import C.Zcash (rustWrapperUADecode) import Control.Monad.IO.Class (liftIO) @@ -34,6 +35,8 @@ import qualified Data.Text.Lazy.IO as LTIO import Data.Word import GHC.Float.RealFracMethods (properFractionDoubleInteger) import Test.Hspec +import Test.Hspec.QuickCheck +import Test.QuickCheck import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) import ZcashHaskell.Orchard import ZcashHaskell.Sapling @@ -49,6 +52,7 @@ import ZcashHaskell.Types , CoinType(..) , DecodedNote(..) , OrchardAction(..) + , Phrase(..) , RawData(..) , RawTxResponse(..) , ShieldedOutput(..) @@ -448,18 +452,12 @@ main = do msg `shouldBe` "Hello World!\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL" describe "Wallet seed phrase" $ do - it "Generate phrase" $ do - p <- generateWalletSeedPhrase - BS.length p `shouldNotBe` 0 - it "Derive seed" $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - maybe 0 BS.length s `shouldBe` 64 - it "Generate Orchard spending key" $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - genOrchardSpendingKey (fromMaybe "" s) MainNetCoin 1 `shouldNotBe` - Nothing + prop "Generated phrases are valid" prop_PhraseLength + prop "Derived seeds are valid" prop_SeedLength + prop "Orchard spending keys are valid" $ + forAll genOrcArgs $ \(c, i, _) -> prop_OrchardSpendingKey c i + prop "Orchard receivers are valid" $ + forAll genOrcArgs $ \(c, i, j) -> prop_OrchardReceiver c i j describe "Address tests" $ do it "Encode transparent" $ do let ua = @@ -469,3 +467,41 @@ main = do Nothing -> "Bad UA" Just u -> maybe "No transparent" encodeTransparent $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" + +-- | Properties +prop_PhraseLength :: Int -> Property +prop_PhraseLength i = + ioProperty $ do + p <- generateWalletSeedPhrase + return $ BS.length p >= 95 + +prop_SeedLength :: Int -> Property +prop_SeedLength i = + ioProperty $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + return $ maybe 0 BS.length s === 64 + +prop_OrchardSpendingKey :: CoinType -> Int -> Property +prop_OrchardSpendingKey c i = + ioProperty $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + return $ genOrchardSpendingKey (fromMaybe "" s) c i =/= Nothing + +prop_OrchardReceiver :: CoinType -> Int -> Int -> Property +prop_OrchardReceiver c i j = + ioProperty $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + let sk = genOrchardSpendingKey (fromMaybe "" s) c i + return $ genOrchardReceiver j (fromMaybe "" sk) =/= Nothing + +-- | Generators +genOrcArgs :: Gen (CoinType, Int, Int) +genOrcArgs = do + i <- arbitrarySizedNatural + j <- arbitrarySizedNatural + c <- elements [MainNetCoin, TestNetCoin, RegTestNetCoin] + return (c, i, j) +-- | Arbitrary instances diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index b6675d1..435f12f 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -72,6 +72,8 @@ test-suite zcash-haskell-test , haskoin-core , hexstring , hspec + , QuickCheck + , quickcheck-transformer , text , zcash-haskell pkgconfig-depends: rustzcash_wrapper From 6e31d839635fa94c8ab67c3128460c4b68029908 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 8 Mar 2024 13:09:13 -0600 Subject: [PATCH 048/149] Update `UnifiedAddress` to use named types for receivers --- src/ZcashHaskell/Types.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 4bd0cd9..931828e 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -279,19 +279,19 @@ instance FromJSON ShieldedOutput where -- * Orchard -- | Type to represent a Unified Address data UnifiedAddress = UnifiedAddress - { ua_net :: ZcashNet - , o_rec :: BS.ByteString - , s_rec :: BS.ByteString - , t_rec :: Maybe TransparentAddress + { ua_net :: !ZcashNet + , o_rec :: !OrchardReceiver + , s_rec :: !SaplingReceiver + , t_rec :: !(Maybe TransparentAddress) } deriving (Prelude.Show, Eq, Read) -- | Helper type for marshalling UAs data RawUA = RawUA - { raw_net :: Word8 - , raw_o :: BS.ByteString - , raw_s :: BS.ByteString - , raw_t :: BS.ByteString - , raw_to :: BS.ByteString + { raw_net :: !Word8 + , raw_o :: !BS.ByteString + , raw_s :: !BS.ByteString + , raw_t :: !BS.ByteString + , raw_to :: !BS.ByteString } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) From 9c4e26c9f2ca159c04a1a76216d0e250473c91e0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 8 Mar 2024 13:35:37 -0600 Subject: [PATCH 049/149] Implement initial changes for ZIP-320 Rev1 --- src/ZcashHaskell/Orchard.hs | 29 ++++++++++++++++++----------- src/ZcashHaskell/Types.hs | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 4a0f530..22a82c2 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -76,8 +76,12 @@ isValidUnifiedAddress str = makeUA x = UnifiedAddress whichNet - (raw_o x) - (raw_s x) + (if BS.length (raw_o x) == 43 + then Just (raw_o x) + else Nothing) + (if BS.length (raw_s x) == 43 + then Just (raw_s x) + else Nothing) (if not (BS.null (raw_t x)) then Just $ TransparentAddress P2PKH whichNet (raw_t x) else if not (BS.null (raw_to x)) @@ -98,18 +102,21 @@ encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b Nothing -> BS.empty Just t -> case ta_type t of - P2SH -> packReceiver 0x01 $ ta_bytes t - P2PKH -> packReceiver 0x00 $ ta_bytes t + P2SH -> packReceiver 0x01 $ Just $ ta_bytes t + P2PKH -> packReceiver 0x00 $ Just $ ta_bytes t sReceiver = packReceiver 0x02 $ s_rec ua oReceiver = packReceiver 0x03 $ o_rec ua padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr - packReceiver :: Word8 -> BS.ByteString -> BS.ByteString - packReceiver typeCode receiver = - if BS.length receiver > 1 - then BS.singleton typeCode `BS.append` - (BS.singleton . toEnum . BS.length) receiver `BS.append` - receiver - else BS.empty + packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString + packReceiver typeCode receiver' = + case receiver' of + Just receiver -> + if BS.length receiver > 1 + then BS.singleton typeCode `BS.append` + (BS.singleton . toEnum . BS.length) receiver `BS.append` + receiver + else BS.empty + Nothing -> BS.empty -- | Attempts to decode the given bytestring into a Unified Full Viewing Key decodeUfvk :: BS.ByteString -> Maybe UnifiedFullViewingKey diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 931828e..41e41e8 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -280,8 +280,8 @@ instance FromJSON ShieldedOutput where -- | Type to represent a Unified Address data UnifiedAddress = UnifiedAddress { ua_net :: !ZcashNet - , o_rec :: !OrchardReceiver - , s_rec :: !SaplingReceiver + , o_rec :: !(Maybe OrchardReceiver) + , s_rec :: !(Maybe SaplingReceiver) , t_rec :: !(Maybe TransparentAddress) } deriving (Prelude.Show, Eq, Read) From d3cf05d00eff562427e1c106bff14081b23712af Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 8 Mar 2024 13:42:18 -0600 Subject: [PATCH 050/149] Update version --- CHANGELOG.md | 9 ++++++++- zcash-haskell.cabal | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a39f77..fff58dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update installation to `cabal` -- Updated `bech32` Rust crate to 0.11 +- Updated Rust crates: + - `bech32` to 0.11 + - `orchard` to 0.7.0 + - `zcash_note_encryption` to 0.4.0 + - `zcash_primitives` to 0.13.0 + - `zcash_client_backend` to 0.10.0 + - `zip32` to 0.1.0 +- Changed the `UnifiedAddress` to allow for optional shielded receivers ### Removed diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 435f12f..11cbd72 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.3.2 +version: 0.4.4.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 54b15670117c1c3bc7adec91a3f838ffdb2651c2 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Fri, 8 Mar 2024 14:46:41 -0500 Subject: [PATCH 051/149] Default Paymebt address and DiversifierIndex created x --- librustzcash-wrapper/src/lib.rs | 57 ++++++++++++++++++++++++++++----- src/C/Zcash.chs | 9 ++++-- src/ZcashHaskell/Sapling.hs | 16 ++++++--- test/Spec.hs | 31 +++++++++++++++--- 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 8963325..ce35b57 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -54,7 +54,7 @@ use zcash_client_backend::keys::{ sapling::ExtendedFullViewingKey, sapling::ExtendedSpendingKey}; -use zcash_primitives::zip32::AccountId; +use zcash_primitives::zip32::{ AccountId, DiversifierIndex }; use std::slice; use orchard::{ @@ -621,27 +621,68 @@ pub extern "C" fn rust_wrapper_recover_seed( pub extern "C" fn rust_wrapper_sapling_spendingkey( iseed: *const u8, iseed_len: usize, - coin_type: u32, - acc_id: u32, out: *mut u8, out_len: &mut usize ){ - println!("From Rust\n========="); + println!("Starting extended spending key generation...."); let seed: Vec = marshall_from_haskell_var(iseed, iseed_len, RW); - if ( seed.len() <= 0 ) { - println!("Seed error, returning a null vector..."); + if ( seed.len() != 64 ) { + // invalid seed length marshall_to_haskell_var(&vec![0], out, out_len, RW); } else { - println!("Seed in rust : {:?}\n", seed); - println!("Coin Type -> {}\nAccount Id -> {}",coin_type,acc_id); + // Obtain the ExtendedSpendingKey using the seed + // Returns a byte array (169 bytes) let su8 = &seed; let seedu8 : &[u8] = &su8; + println!("Seed : {:?}\n", &seedu8); let extsk: ExtendedSpendingKey = sapling::ExtendedSpendingKey::master(&seedu8); let extsk_bytes = extsk.to_bytes().to_vec(); marshall_to_haskell_var(&extsk_bytes, out, out_len, RW); } } +#[no_mangle] +pub extern "C" fn rust_wrapper_sapling_paymentaddress( + extspk: *const u8, + extspk_len: usize, +// divIx: u32, + out: *mut u8, + out_len: &mut usize + ){ + let divIx : u32 = 2; + println!("Starting paymentAddress generation...."); + let extspkb: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); + if ( extspkb.len() != 169 ) { + // invalid ExtendedSpenndingKey Array length + println!("Invalid ExtendedSpendingKey...."); + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } else { + // Process + println!("Extended Spending Key validated, continue ...."); + let extspkbu8 = &extspkb; + let xsku8 : &[u8] = &extspkbu8; + let xsk = match sapling::ExtendedSpendingKey::from_bytes(&xsku8) { + Ok ( x ) => x, + Err ( err ) => { + // Error recovering ExtendedSpendingKey from bytes + marshall_to_haskell_var(&vec![0], out, out_len, RW); + return + } + }; + // Obtain the DiversifiableFullViewingKey from ExtendedSpendingKey + let dfvk = xsk.to_diversifiable_full_viewing_key(); + // Obtain the Address from the DiversifiableFullViewingKey +// println!("dfvk -> \n{:?}", dfvk); +// let divIndex : DiversifierIndex = divIx.into(); +// println!("divIndex -> {:?}", divIndex); + let (divIx, paddress) = dfvk.default_address(); + println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx); + let pmtAddress = paddress.to_bytes(); + println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress); + marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW); + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_derive_orchard_spending_key( seed: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index ab3ef8d..14bfc45 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -136,8 +136,13 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey { toBorshVar* `BS.ByteString'& - , `Word32' - , `Word32' + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_sapling_paymentaddress as rustWrapperPaymentAddress + { toBorshVar* `BS.ByteString'& , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index c34314c..62f513f 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -22,6 +22,7 @@ import C.Zcash , rustWrapperSaplingCheck , rustWrapperSaplingNoteDecode , rustWrapperSaplingSpendingkey + , rustWrapperPaymentAddress , rustWrapperSaplingVkDecode , rustWrapperTxParse ) @@ -92,7 +93,14 @@ instance FromJSON RawTxResponse where pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b -- -- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID -genSaplingSpendingKey :: BS.ByteString -> Word32-> AccountId -> BS.ByteString -genSaplingSpendingKey seed coin_type account_id = do - let res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed (fromIntegral coin_type) (fromIntegral account_id) ) - res \ No newline at end of file +genSaplingSpendingKey :: BS.ByteString -> BS.ByteString +genSaplingSpendingKey seed = do + let res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed ) + res +-- +-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey +-- | and a Diversifier Index +genSaplingPaymentAddress :: BS.ByteString -> BS.ByteString +genSaplingPaymentAddress extspk = do + let pmtaddress = withPureBorshVarBuffer (rustWrapperPaymentAddress extspk ) + pmtaddress diff --git a/test/Spec.hs b/test/Spec.hs index abfd17c..c6d5086 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -43,6 +43,7 @@ import ZcashHaskell.Sapling , isValidShieldedAddress , matchSaplingAddress , genSaplingSpendingKey + , genSaplingPaymentAddress ) import ZcashHaskell.Transparent --(encodeTransparent) @@ -531,11 +532,33 @@ main = do 241, 243, 172, 178, 104, 81, 159, 144 ] :: [Word8] - let cointype = getValue TestNetCoin - let account = 0 :: AccountId - let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) cointype account + let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) let msgArr = BS.unpack msg if (length msgArr) == 169 then True else False --- msgArr `shouldBe` "It's working." \ No newline at end of file + describe "Sapling Payment Address generation test" $ do + it "Call genSaplingPaymentAddress" $ do + let hdseed1 = [206, 61, 120, 38, + 206, 40, 201, 62, + 83, 175, 151, 131, + 218, 141, 206, 254, + 28, 244, 172, 213, + 128, 248, 156, 45, + 204, 44, 169, 3, + 162, 188, 16, 173, + 192, 164, 96, 148, + 91, 52, 244, 83, + 149, 169, 82, 196, + 199, 53, 177, 170, + 1, 6, 0, 120, + 170, 2, 238, 219, + 241, 243, 172, 178, + 104, 81, 159, 144 + ] :: [Word8] + let msg1 = genSaplingSpendingKey (word8ArrayToByteString hdseed1) + let pmtaddress = genSaplingPaymentAddress msg1 --(word8ArrayToByteString hdseed1) + let msgArr = BS.unpack pmtaddress + if (length msgArr) == 43 + then True + else False From 477817f37f414a459d70b1f89e950f85438ca267 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Sat, 9 Mar 2024 10:28:36 -0500 Subject: [PATCH 052/149] Find_Address method implemented --- librustzcash-wrapper/src/lib.rs | 66 +++++++++++++++++++++++++++++---- src/C/Zcash.chs | 8 ++++ src/ZcashHaskell/Sapling.hs | 9 ++++- test/Spec.hs | 34 +++++++++++++---- 4 files changed, 101 insertions(+), 16 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 649b699..05c7853 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -649,8 +649,48 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress( out: *mut u8, out_len: &mut usize ){ - let divIx : u32 = 2; - println!("Starting paymentAddress generation...."); +// println!("Starting paymentAddress generation...."); + let extspkb: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); + if ( extspkb.len() != 169 ) { + // invalid ExtendedSpenndingKey Array length + println!("Invalid ExtendedSpendingKey...."); + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } else { + // Process +// println!("Extended Spending Key validated, continue ...."); + let extspkbu8 = &extspkb; + let xsku8 : &[u8] = &extspkbu8; + let xsk = match sapling::ExtendedSpendingKey::from_bytes(&xsku8) { + Ok ( x ) => x, + Err ( err ) => { + // Error recovering ExtendedSpendingKey from bytes + marshall_to_haskell_var(&vec![0], out, out_len, RW); + return + } + }; + // Obtain the DiversifiableFullViewingKey from ExtendedSpendingKey + let dfvk = xsk.to_diversifiable_full_viewing_key(); + // Obtain the Address from the DiversifiableFullViewingKey +// println!("dfvk -> \n{:?}", dfvk); +// let divIndex : DiversifierIndex = divIx.into(); +// println!("divIndex -> {:?}", divIndex); + let (divIx, paddress) = dfvk.default_address(); +// println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx); + let pmtAddress = paddress.to_bytes(); +// println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress); + marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW); + } +} + +#[no_mangle] +pub extern "C" fn rust_wrapper_sapling_find_paymentaddress( + extspk: *const u8, + extspk_len: usize, + divIx: u32, + out: *mut u8, + out_len: &mut usize + ){ + println!("Starting paymentAddress generation using Find_Address()...."); let extspkb: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); if ( extspkb.len() != 169 ) { // invalid ExtendedSpenndingKey Array length @@ -669,20 +709,30 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress( return } }; + println!("Obtain a DiversifierIndex from u32 parameter {:?}....",divIx); + let diversifierIndex : DiversifierIndex = divIx.into(); // Obtain the DiversifiableFullViewingKey from ExtendedSpendingKey let dfvk = xsk.to_diversifiable_full_viewing_key(); // Obtain the Address from the DiversifiableFullViewingKey // println!("dfvk -> \n{:?}", dfvk); // let divIndex : DiversifierIndex = divIx.into(); // println!("divIndex -> {:?}", divIndex); - let (divIx, paddress) = dfvk.default_address(); - println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx); - let pmtAddress = paddress.to_bytes(); - println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress); - marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW); + let result = dfvk.find_address(diversifierIndex) + .map (|(divIx,paddress) | { + println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx); + let pmtAddress = paddress.to_bytes(); + println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress); + marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW); +// + }) + .unwrap_or_else(|| { + // Handle the case where the function returns None + println!("Rust - Error finding payment address.... "); + marshall_to_haskell_var(&vec![0], out, out_len, RW); + return + }); } } - #[no_mangle] pub extern "C" fn rust_wrapper_derive_orchard_spending_key( seed: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 97ad03b..d0f100f 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -148,6 +148,14 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_sapling_find_paymentaddress as rustWrapperFindPaymentAddress + { toBorshVar* `BS.ByteString'& + , `Word32' + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_derive_orchard_spending_key as rustWrapperGenOrchardSpendKey { toBorshVar* `BS.ByteString'& , `Word32' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 62f513f..c470417 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -23,6 +23,7 @@ import C.Zcash , rustWrapperSaplingNoteDecode , rustWrapperSaplingSpendingkey , rustWrapperPaymentAddress + , rustWrapperFindPaymentAddress , rustWrapperSaplingVkDecode , rustWrapperTxParse ) @@ -99,8 +100,14 @@ genSaplingSpendingKey seed = do res -- -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey --- | and a Diversifier Index genSaplingPaymentAddress :: BS.ByteString -> BS.ByteString genSaplingPaymentAddress extspk = do let pmtaddress = withPureBorshVarBuffer (rustWrapperPaymentAddress extspk ) pmtaddress + +-- +-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey +genSaplingFindPaymentAddress :: BS.ByteString -> Word32 -> BS.ByteString +genSaplingFindPaymentAddress extspk divIx = do + let pmtaddress = withPureBorshVarBuffer (rustWrapperFindPaymentAddress extspk divIx) + pmtaddress diff --git a/test/Spec.hs b/test/Spec.hs index 164329d..63cf1be 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -46,7 +46,8 @@ import ZcashHaskell.Sapling , isValidShieldedAddress , matchSaplingAddress , genSaplingSpendingKey - , genSaplingPaymentAddress + , genSaplingPaymentAddress + , genSaplingFindPaymentAddress ) import ZcashHaskell.Transparent --(encodeTransparent) @@ -532,9 +533,7 @@ main = do ] :: [Word8] let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) let msgArr = BS.unpack msg - if (length msgArr) == 169 - then True - else False + length msgArr `shouldBe` 169 describe "Sapling Payment Address generation test" $ do it "Call genSaplingPaymentAddress" $ do let hdseed1 = [206, 61, 120, 38, @@ -557,9 +556,30 @@ main = do let msg1 = genSaplingSpendingKey (word8ArrayToByteString hdseed1) let pmtaddress = genSaplingPaymentAddress msg1 --(word8ArrayToByteString hdseed1) let msgArr = BS.unpack pmtaddress - if (length msgArr) == 43 - then True - else False + length msgArr `shouldBe` 43 + describe "Sapling Payment Find Address generation test" $ do + it "Call genSaplingFindPaymentAddress" $ do + let hdseed1 = [206, 61, 120, 38, + 206, 40, 201, 62, + 83, 175, 151, 131, + 218, 141, 206, 254, + 28, 244, 172, 213, + 128, 248, 156, 45, + 204, 44, 169, 3, + 162, 188, 16, 173, + 192, 164, 96, 148, + 91, 52, 244, 83, + 149, 169, 82, 196, + 199, 53, 177, 170, + 1, 6, 0, 120, + 170, 2, 238, 219, + 241, 243, 172, 178, + 104, 81, 159, 144 + ] :: [Word8] + let msg1 = genSaplingSpendingKey (word8ArrayToByteString hdseed1) + let pmtaddress = genSaplingFindPaymentAddress msg1 0 --(word8ArrayToByteString hdseed1) + let msgArr = BS.unpack pmtaddress + length msgArr `shouldBe` 42 -- | Properties prop_PhraseLength :: Int -> Property From 7538bbfa190c45ccb76b99d0cfcdf124e391a0f0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 10 Mar 2024 07:47:26 -0500 Subject: [PATCH 053/149] Revision of Sapling receiver logic --- CHANGELOG.md | 1 + librustzcash-wrapper/Cargo.lock | 1 - librustzcash-wrapper/Cargo.toml | 1 - librustzcash-wrapper/src/lib.rs | 72 +++---- src/C/Zcash.chs | 3 +- src/ZcashHaskell/Sapling.hs | 41 ++-- src/ZcashHaskell/Types.hs | 10 - src/ZcashHaskell/Utils.hs | 12 +- test/Spec.hs | 347 ++++++++++++++++++++++++-------- zcash-haskell.cabal | 2 +- 10 files changed, 323 insertions(+), 167 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fff58dc..fe73f0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Constants for Zcash protocol - Types for Spending Keys and Receivers for Sapling and Orchard - Function to generate an Orchard receiver +- Function to generate a Sapling receiver ### Changed diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index bc4b733..7f59103 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -1314,7 +1314,6 @@ dependencies = [ "borsh 0.10.3", "f4jumble", "haskell-ffi", - "nom", "orchard 0.7.1", "proc-macro2", "zcash_address 0.2.0", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index d7b2495..9c8f966 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -17,7 +17,6 @@ zcash_primitives = "0.13.0" zcash_client_backend = "0.10.0" zip32 = "0.1.0" proc-macro2 = "1.0.66" -nom = "7.1.3" [features] capi = [] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 649b699..5eea232 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -24,15 +24,23 @@ use haskell_ffi::{ use zip32; use zcash_primitives::{ - zip32::Scope as SaplingScope, + zip32::{ + Scope as SaplingScope, + sapling_find_address, + sapling::DiversifierKey + }, zip339::{Count, Mnemonic}, transaction::components::sapling::{ GrothProofBytes, - OutputDescription, + OutputDescription }, sapling::{ PaymentAddress, - keys::PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, + keys::{ + PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, + ExpandedSpendingKey, + FullViewingKey as SaplingFullViewingKey + }, note_encryption::SaplingDomain }, transaction::Transaction, @@ -49,13 +57,12 @@ use zcash_address::{ ZcashAddress }; -use zcash_client_backend::keys::{ - sapling, - sapling::ExtendedFullViewingKey, - sapling::ExtendedSpendingKey}; +use zcash_client_backend::keys::sapling::{ + ExtendedFullViewingKey, + ExtendedSpendingKey +}; use zcash_primitives::zip32::{ AccountId, DiversifierIndex }; -use std::slice; use orchard::{ Action, @@ -624,7 +631,6 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey( out: *mut u8, out_len: &mut usize ){ - println!("Starting extended spending key generation...."); let seed: Vec = marshall_from_haskell_var(iseed, iseed_len, RW); if ( seed.len() != 64 ) { // invalid seed length @@ -634,8 +640,7 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey( // Returns a byte array (169 bytes) let su8 = &seed; let seedu8 : &[u8] = &su8; - println!("Seed : {:?}\n", &seedu8); - let extsk: ExtendedSpendingKey = sapling::ExtendedSpendingKey::master(&seedu8); + let extsk: ExtendedSpendingKey = ExtendedSpendingKey::master(&seedu8); let extsk_bytes = extsk.to_bytes().to_vec(); marshall_to_haskell_var(&extsk_bytes, out, out_len, RW); } @@ -645,41 +650,22 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey( pub extern "C" fn rust_wrapper_sapling_paymentaddress( extspk: *const u8, extspk_len: usize, -// divIx: u32, + div_ix: u32, out: *mut u8, out_len: &mut usize ){ - let divIx : u32 = 2; - println!("Starting paymentAddress generation...."); - let extspkb: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); - if ( extspkb.len() != 169 ) { - // invalid ExtendedSpenndingKey Array length - println!("Invalid ExtendedSpendingKey...."); - marshall_to_haskell_var(&vec![0], out, out_len, RW); - } else { - // Process - println!("Extended Spending Key validated, continue ...."); - let extspkbu8 = &extspkb; - let xsku8 : &[u8] = &extspkbu8; - let xsk = match sapling::ExtendedSpendingKey::from_bytes(&xsku8) { - Ok ( x ) => x, - Err ( err ) => { - // Error recovering ExtendedSpendingKey from bytes - marshall_to_haskell_var(&vec![0], out, out_len, RW); - return - } - }; - // Obtain the DiversifiableFullViewingKey from ExtendedSpendingKey - let dfvk = xsk.to_diversifiable_full_viewing_key(); - // Obtain the Address from the DiversifiableFullViewingKey -// println!("dfvk -> \n{:?}", dfvk); -// let divIndex : DiversifierIndex = divIx.into(); -// println!("divIndex -> {:?}", divIndex); - let (divIx, paddress) = dfvk.default_address(); - println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx); - let pmtAddress = paddress.to_bytes(); - println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress); - marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW); + let extspk: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); + let expsk = ExpandedSpendingKey::from_spending_key(&extspk); + let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk); + let dk = DiversifierKey::master(&extspk); + let result = sapling_find_address(&fvk, &dk, DiversifierIndex::from(div_ix)); + match result { + Some((_d, p_address)) => { + marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW); + }, + None => { + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 97ad03b..1e32f93 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -141,8 +141,9 @@ import ZcashHaskell.Types -> `()' #} -{# fun unsafe rust_wrapper_sapling_paymentaddress as rustWrapperPaymentAddress +{# fun unsafe rust_wrapper_sapling_paymentaddress as rustWrapperSaplingPaymentAddress { toBorshVar* `BS.ByteString'& + , `Word32' , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 62f513f..7ca8ec5 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -21,14 +21,13 @@ import C.Zcash ( rustWrapperIsShielded , rustWrapperSaplingCheck , rustWrapperSaplingNoteDecode + , rustWrapperSaplingPaymentAddress , rustWrapperSaplingSpendingkey - , rustWrapperPaymentAddress , rustWrapperSaplingVkDecode , rustWrapperTxParse ) import Data.Aeson import qualified Data.ByteString as BS -import Data.ByteString.Lazy as BL import Data.HexString (HexString(..), toBytes) import Data.Word import Foreign.Rust.Marshall.Variable @@ -36,16 +35,18 @@ import Foreign.Rust.Marshall.Variable , withPureBorshVarBuffer ) import ZcashHaskell.Types - ( DecodedNote(..) + ( AccountId + , CoinType + , DecodedNote(..) , RawData(..) , RawTxResponse(..) - , SaplingSKeyParams(..) + , SaplingReceiver + , SaplingSpendingKey(..) + , Seed(..) , ShieldedOutput(..) , decodeHexText - , AccountId - , CoinType ) -import ZcashHaskell.Utils +import ZcashHaskell.Utils (decodeBech32) -- | Check if given bytesting is a valid encoded shielded address isValidShieldedAddress :: BS.ByteString -> Bool @@ -91,16 +92,26 @@ instance FromJSON RawTxResponse where Just o' -> do a <- o' .: "actions" pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b + -- --- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID -genSaplingSpendingKey :: BS.ByteString -> BS.ByteString +-- | Attempts to obtain a sapling SpendingKey using a HDSeed +genSaplingSpendingKey :: Seed -> Maybe SaplingSpendingKey genSaplingSpendingKey seed = do - let res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed ) - res + if BS.length res == 196 + then Just res + else Nothing + where + res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed) + -- -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey -- | and a Diversifier Index -genSaplingPaymentAddress :: BS.ByteString -> BS.ByteString -genSaplingPaymentAddress extspk = do - let pmtaddress = withPureBorshVarBuffer (rustWrapperPaymentAddress extspk ) - pmtaddress +genSaplingPaymentAddress :: SaplingSpendingKey -> Int -> Maybe SaplingReceiver +genSaplingPaymentAddress extspk i = + if BS.length res == 43 + then Just res + else Nothing + where + res = + withPureBorshVarBuffer + (rustWrapperSaplingPaymentAddress extspk (fromIntegral i)) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index f86f368..41e41e8 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -346,16 +346,6 @@ data DecodedNote = DecodedNote deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote --- } Type to represent parameters to call rust zcash library -data SaplingSKeyParams = SaplingSKeyParams - { hdseed :: BS.ByteString -- ^ seed required for sappling spending key generation - , coin_type :: Word32 -- ^ coin_type - , account_id :: Word32 -- ^ account id - } deriving stock (Eq, Prelude.Show, GHC.Generic) - deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) - deriving anyclass (Data.Structured.Show) - deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingSKeyParams - -- * Helpers -- | Helper function to turn a hex-encoded string to bytestring decodeHexText :: String -> BS.ByteString diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 3e392e2..f6f1ceb 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -37,8 +37,8 @@ import Foreign.Marshal.Array (allocaArray, peekArray) import Foreign.Ptr (Ptr) import Data.Word --- | +-- | -- | Decode the given bytestring using Bech32 decodeBech32 :: BS.ByteString -> RawData decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode @@ -88,13 +88,3 @@ makeZebraCall host port m params = do setRequestHost (E.encodeUtf8 host) $ setRequestMethod "POST" defaultRequest httpJSON myRequest - - --- + Misc functions --- Convert an array of Word8 to a ByteString -word8ArrayToByteString :: [Word8] -> BS.ByteString -word8ArrayToByteString = BS.pack - --- Convert [Word8] to String -word8ToString :: [Word8] -> String -word8ToString = map (toEnum . fromEnum) diff --git a/test/Spec.hs b/test/Spec.hs index 164329d..b7a38ff 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -41,18 +41,21 @@ import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) import ZcashHaskell.Orchard import ZcashHaskell.Sapling ( decodeSaplingOutput + , genSaplingPaymentAddress + , genSaplingSpendingKey , getShieldedOutputs , isValidSaplingViewingKey , isValidShieldedAddress , matchSaplingAddress - , genSaplingSpendingKey - , genSaplingPaymentAddress ) -import ZcashHaskell.Transparent +import ZcashHaskell.Transparent --(encodeTransparent) + import ZcashHaskell.Types - ( BlockResponse(..) + ( AccountId + , BlockResponse(..) , CoinType(..) + , CoinType , DecodedNote(..) , OrchardAction(..) , Phrase(..) @@ -62,19 +65,16 @@ import ZcashHaskell.Types , UnifiedAddress(..) , UnifiedFullViewingKey(..) , decodeHexText - , AccountId - , CoinType , getValue ) import ZcashHaskell.Utils -import Foreign.C.Types import Data.Word +import Foreign.C.Types import Haskoin.Crypto.Keys.Extended -m2bs :: Maybe BS.ByteString -> BS.ByteString -m2bs x = fromMaybe "" x - +m2bs :: Maybe BS.ByteString -> BS.ByteString +m2bs x = fromMaybe "" x main :: IO () main = do @@ -483,83 +483,251 @@ main = do msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" describe "Transparent Private and Publicc Key Generation" $ do it "Obtain a transparent extended private key from HDSeed" $ do - let hdseed = [206, 61, 120, 38, - 206, 40, 201, 62, - 83, 175, 151, 131, - 218, 141, 206, 254, - 28, 244, 172, 213, - 128, 248, 156, 45, - 204, 44, 169, 3, - 162, 188, 16, 173, - 192, 164, 96, 148, - 91, 52, 244, 83, - 149, 169, 82, 196, - 199, 53, 177, 170, - 1, 6, 0, 120, - 170, 2, 238, 219, - 241, 243, 172, 178, - 104, 81, 159, 144 - ] :: [Word8] - let xtpvk = genTransparentPrvKey (word8ArrayToByteString hdseed) - let testpvk = XPrvKey 0 "0000000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" - xtpvk `shouldBe` testpvk --- describe "Obtain transparent public key from private key" $ do - it "Obtain a transparent extended public key from private key" $ do - let testpvk = XPrvKey 0 "0000000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" - let testpbk = XPubKey 0 "00000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8" - let xtpubkIO = genTransparentPubKey testpvk - xtpubk <- xtpubkIO + let hdseed = + [ 206 + , 61 + , 120 + , 38 + , 206 + , 40 + , 201 + , 62 + , 83 + , 175 + , 151 + , 131 + , 218 + , 141 + , 206 + , 254 + , 28 + , 244 + , 172 + , 213 + , 128 + , 248 + , 156 + , 45 + , 204 + , 44 + , 169 + , 3 + , 162 + , 188 + , 16 + , 173 + , 192 + , 164 + , 96 + , 148 + , 91 + , 52 + , 244 + , 83 + , 149 + , 169 + , 82 + , 196 + , 199 + , 53 + , 177 + , 170 + , 1 + , 6 + , 0 + , 120 + , 170 + , 2 + , 238 + , 219 + , 241 + , 243 + , 172 + , 178 + , 104 + , 81 + , 159 + , 144 + ] :: [Word8] + let xtpvk = genTransparentPrvKey (BS.pack hdseed) + let testpvk = + XPrvKey + 0 + "0000000000" + 0 + "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" + "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" + xtpvk `shouldBe` testpvk + it "Obtain a transparent extended public key from private key" $ do + let testpvk = + XPrvKey + 0 + "0000000000" + 0 + "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" + "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" + let testpbk = + XPubKey + 0 + "00000000" + 0 + "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" + "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8" + let xtpubkIO = genTransparentPubKey testpvk + xtpubk <- xtpubkIO ---print $ show xtpubk - xtpubk `shouldBe` testpbk + xtpubk `shouldBe` testpbk describe "Sapling SpendingKey test" $ do - it "Call function with parameters" $ do - let hdseed = [206, 61, 120, 38, - 206, 40, 201, 62, - 83, 175, 151, 131, - 218, 141, 206, 254, - 28, 244, 172, 213, - 128, 248, 156, 45, - 204, 44, 169, 3, - 162, 188, 16, 173, - 192, 164, 96, 148, - 91, 52, 244, 83, - 149, 169, 82, 196, - 199, 53, 177, 170, - 1, 6, 0, 120, - 170, 2, 238, 219, - 241, 243, 172, 178, - 104, 81, 159, 144 - ] :: [Word8] - let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) - let msgArr = BS.unpack msg - if (length msgArr) == 169 - then True - else False + let hdseed = + BS.pack $ + [ 206 + , 61 + , 120 + , 38 + , 206 + , 40 + , 201 + , 62 + , 83 + , 175 + , 151 + , 131 + , 218 + , 141 + , 206 + , 254 + , 28 + , 244 + , 172 + , 213 + , 128 + , 248 + , 156 + , 45 + , 204 + , 44 + , 169 + , 3 + , 162 + , 188 + , 16 + , 173 + , 192 + , 164 + , 96 + , 148 + , 91 + , 52 + , 244 + , 83 + , 149 + , 169 + , 82 + , 196 + , 199 + , 53 + , 177 + , 170 + , 1 + , 6 + , 0 + , 120 + , 170 + , 2 + , 238 + , 219 + , 241 + , 243 + , 172 + , 178 + , 104 + , 81 + , 159 + , 144 + ] + --xit "Call function with parameters" $ do + --let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) + --let msgArr = BS.unpack msg + --if (length msgArr) == 169 + --then True + --else False + it "Generate Sapling spending key" $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + genSaplingSpendingKey <$> s `shouldNotBe` Nothing describe "Sapling Payment Address generation test" $ do - it "Call genSaplingPaymentAddress" $ do - let hdseed1 = [206, 61, 120, 38, - 206, 40, 201, 62, - 83, 175, 151, 131, - 218, 141, 206, 254, - 28, 244, 172, 213, - 128, 248, 156, 45, - 204, 44, 169, 3, - 162, 188, 16, 173, - 192, 164, 96, 148, - 91, 52, 244, 83, - 149, 169, 82, 196, - 199, 53, 177, 170, - 1, 6, 0, 120, - 170, 2, 238, 219, - 241, 243, 172, 178, - 104, 81, 159, 144 - ] :: [Word8] - let msg1 = genSaplingSpendingKey (word8ArrayToByteString hdseed1) - let pmtaddress = genSaplingPaymentAddress msg1 --(word8ArrayToByteString hdseed1) - let msgArr = BS.unpack pmtaddress - if (length msgArr) == 43 - then True - else False + it "Call genSaplingPaymentAddress" $ do + let hdseed1 = + [ 206 + , 61 + , 120 + , 38 + , 206 + , 40 + , 201 + , 62 + , 83 + , 175 + , 151 + , 131 + , 218 + , 141 + , 206 + , 254 + , 28 + , 244 + , 172 + , 213 + , 128 + , 248 + , 156 + , 45 + , 204 + , 44 + , 169 + , 3 + , 162 + , 188 + , 16 + , 173 + , 192 + , 164 + , 96 + , 148 + , 91 + , 52 + , 244 + , 83 + , 149 + , 169 + , 82 + , 196 + , 199 + , 53 + , 177 + , 170 + , 1 + , 6 + , 0 + , 120 + , 170 + , 2 + , 238 + , 219 + , 241 + , 243 + , 172 + , 178 + , 104 + , 81 + , 159 + , 144 + ] :: [Word8] + p <- generateWalletSeedPhrase + let s = getWalletSeed p + genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing + prop "Sapling receivers are valid" $ + forAll genSapArgs $ \(i) -> prop_SaplingReceiver i -- | Properties prop_PhraseLength :: Int -> Property @@ -590,6 +758,14 @@ prop_OrchardReceiver c i j = let sk = genOrchardSpendingKey (fromMaybe "" s) c i return $ genOrchardReceiver j (fromMaybe "" sk) =/= Nothing +prop_SaplingReceiver :: Int -> Property +prop_SaplingReceiver i = + ioProperty $ do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + let sk = genSaplingSpendingKey (fromMaybe "" s) + return $ genSaplingPaymentAddress (fromMaybe "" sk) i =/= Nothing + -- | Generators genOrcArgs :: Gen (CoinType, Int, Int) genOrcArgs = do @@ -597,4 +773,7 @@ genOrcArgs = do j <- arbitrarySizedNatural c <- elements [MainNetCoin, TestNetCoin, RegTestNetCoin] return (c, i, j) + +genSapArgs :: Gen Int +genSapArgs = choose (1, 50) -- | Arbitrary instances diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index ec49d03..0bee561 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.4.0 +version: 0.4.4.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 4443ffeec558a2bd0e6734cb0ffc20265d6b9045 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 10 Mar 2024 10:20:10 -0500 Subject: [PATCH 054/149] Add signature for internal Sapling address --- src/ZcashHaskell/Sapling.hs | 7 +- src/ZcashHaskell/Types.hs | 3 + test/Spec.hs | 141 +----------------------------------- 3 files changed, 9 insertions(+), 142 deletions(-) diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 5db9f06..0a25661 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -40,6 +40,7 @@ import ZcashHaskell.Types , DecodedNote(..) , RawData(..) , RawTxResponse(..) + , SaplingInternalReceiver , SaplingReceiver , SaplingSpendingKey(..) , Seed(..) @@ -93,7 +94,6 @@ instance FromJSON RawTxResponse where a <- o' .: "actions" pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b --- -- | Attempts to obtain a sapling SpendingKey using a HDSeed genSaplingSpendingKey :: Seed -> Maybe SaplingSpendingKey genSaplingSpendingKey seed = do @@ -103,7 +103,6 @@ genSaplingSpendingKey seed = do where res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed) --- -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index genSaplingPaymentAddress :: SaplingSpendingKey -> Int -> Maybe SaplingReceiver genSaplingPaymentAddress extspk i = @@ -114,3 +113,7 @@ genSaplingPaymentAddress extspk i = res = withPureBorshVarBuffer (rustWrapperSaplingPaymentAddress extspk (fromIntegral i)) + +-- | Generate an internal Sapling address +genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingInternalReceiver +genSaplingInternalAddress sk = undefined diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 41e41e8..7c14558 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -54,6 +54,9 @@ type OrchardSpendingKey = BS.ByteString -- | A Sapling receiver type SaplingReceiver = BS.ByteString +-- | A Sapling internal receiver +type SaplingInternalReceiver = BS.ByteString + -- | An Orchard receiver type OrchardReceiver = BS.ByteString diff --git a/test/Spec.hs b/test/Spec.hs index b7a38ff..e6d6bc2 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -578,156 +578,17 @@ main = do ---print $ show xtpubk xtpubk `shouldBe` testpbk describe "Sapling SpendingKey test" $ do - let hdseed = - BS.pack $ - [ 206 - , 61 - , 120 - , 38 - , 206 - , 40 - , 201 - , 62 - , 83 - , 175 - , 151 - , 131 - , 218 - , 141 - , 206 - , 254 - , 28 - , 244 - , 172 - , 213 - , 128 - , 248 - , 156 - , 45 - , 204 - , 44 - , 169 - , 3 - , 162 - , 188 - , 16 - , 173 - , 192 - , 164 - , 96 - , 148 - , 91 - , 52 - , 244 - , 83 - , 149 - , 169 - , 82 - , 196 - , 199 - , 53 - , 177 - , 170 - , 1 - , 6 - , 0 - , 120 - , 170 - , 2 - , 238 - , 219 - , 241 - , 243 - , 172 - , 178 - , 104 - , 81 - , 159 - , 144 - ] - --xit "Call function with parameters" $ do - --let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed) - --let msgArr = BS.unpack msg - --if (length msgArr) == 169 - --then True - --else False it "Generate Sapling spending key" $ do p <- generateWalletSeedPhrase let s = getWalletSeed p genSaplingSpendingKey <$> s `shouldNotBe` Nothing describe "Sapling Payment Address generation test" $ do it "Call genSaplingPaymentAddress" $ do - let hdseed1 = - [ 206 - , 61 - , 120 - , 38 - , 206 - , 40 - , 201 - , 62 - , 83 - , 175 - , 151 - , 131 - , 218 - , 141 - , 206 - , 254 - , 28 - , 244 - , 172 - , 213 - , 128 - , 248 - , 156 - , 45 - , 204 - , 44 - , 169 - , 3 - , 162 - , 188 - , 16 - , 173 - , 192 - , 164 - , 96 - , 148 - , 91 - , 52 - , 244 - , 83 - , 149 - , 169 - , 82 - , 196 - , 199 - , 53 - , 177 - , 170 - , 1 - , 6 - , 0 - , 120 - , 170 - , 2 - , 238 - , 219 - , 241 - , 243 - , 172 - , 178 - , 104 - , 81 - , 159 - , 144 - ] :: [Word8] p <- generateWalletSeedPhrase let s = getWalletSeed p genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing prop "Sapling receivers are valid" $ - forAll genSapArgs $ \(i) -> prop_SaplingReceiver i + forAll genSapArgs $ \i -> prop_SaplingReceiver i -- | Properties prop_PhraseLength :: Int -> Property From 46efe9cd2195376b9d68fd9181411f5b93c70e75 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Sun, 10 Mar 2024 22:19:51 -0400 Subject: [PATCH 055/149] Commit first version of genSaplingInternalAddress function --- librustzcash-wrapper/src/lib.rs | 35 +++++- src/C/Zcash.chs | 7 ++ src/ZcashHaskell/Sapling.hs | 6 +- test/Spec.hs | 188 +++++++++++++++++++++++++++++++- 4 files changed, 231 insertions(+), 5 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 5eea232..3d2754a 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -59,7 +59,8 @@ use zcash_address::{ use zcash_client_backend::keys::sapling::{ ExtendedFullViewingKey, - ExtendedSpendingKey + ExtendedSpendingKey, + DiversifiableFullViewingKey }; use zcash_primitives::zip32::{ AccountId, DiversifierIndex }; @@ -632,7 +633,7 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey( out_len: &mut usize ){ let seed: Vec = marshall_from_haskell_var(iseed, iseed_len, RW); - if ( seed.len() != 64 ) { + if seed.len() != 64 { // invalid seed length marshall_to_haskell_var(&vec![0], out, out_len, RW); } else { @@ -669,6 +670,36 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress( } } +#[no_mangle] +pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress( + extspk: *const u8, + extspk_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + println!("Entering ChangeAddress generation...."); + let vexspk: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); + let vexspkp = &vexspk; + let extspku8 : &[u8] = &vexspkp; + println!("Received ExtendedSpendingKey in Bytes...\n{:?}\n",extspku8); + let extspk = match ExtendedSpendingKey::from_bytes(&extspku8) { + Ok( k ) => k, + Err( e ) => { + // error recovering ExtendedSpendingKey + println!("\n>>>> Error generating ExtendedSpendingKey"); + marshall_to_haskell_var(&vec![0], out, out_len, RW); + return + } + }; + println!("ExtendedSpendingKey -> {:?}",extspk); + let dfvk = extspk.to_diversifiable_full_viewing_key(); + let ( divIx, cPmtAddress ) = dfvk.change_address(); + println!("\nDiversifierIndex > {:?}\n\nChange Payment Address -> {:?}\n ", + divIx, cPmtAddress); + println!("Change Payment Address in bytes : \n{:?}",cPmtAddress.to_bytes()); + marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW); +} + #[no_mangle] pub extern "C" fn rust_wrapper_derive_orchard_spending_key( seed: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 1e32f93..e68404b 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -149,6 +149,13 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_sapling_chgpaymentaddress as rustWrapperSaplingChgPaymentAddress + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_derive_orchard_spending_key as rustWrapperGenOrchardSpendKey { toBorshVar* `BS.ByteString'& , `Word32' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 0a25661..64aa7a6 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -22,6 +22,7 @@ import C.Zcash , rustWrapperSaplingCheck , rustWrapperSaplingNoteDecode , rustWrapperSaplingPaymentAddress + , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingSpendingkey , rustWrapperSaplingVkDecode , rustWrapperTxParse @@ -115,5 +116,6 @@ genSaplingPaymentAddress extspk i = (rustWrapperSaplingPaymentAddress extspk (fromIntegral i)) -- | Generate an internal Sapling address -genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingInternalReceiver -genSaplingInternalAddress sk = undefined +genSaplingInternalAddress :: SaplingSpendingKey -> BS.ByteString -- SaplingInternalReceiver +genSaplingInternalAddress sk = withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress sk) + \ No newline at end of file diff --git a/test/Spec.hs b/test/Spec.hs index e6d6bc2..e00893b 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -42,6 +42,7 @@ import ZcashHaskell.Orchard import ZcashHaskell.Sapling ( decodeSaplingOutput , genSaplingPaymentAddress + , genSaplingInternalAddress , genSaplingSpendingKey , getShieldedOutputs , isValidSaplingViewingKey @@ -481,7 +482,7 @@ main = do Nothing -> "Bad UA" Just u -> maybe "No transparent" encodeTransparent $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" - describe "Transparent Private and Publicc Key Generation" $ do + describe "Transparent Private and Public Key Generation" $ do it "Obtain a transparent extended private key from HDSeed" $ do let hdseed = [ 206 @@ -589,6 +590,191 @@ main = do genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing prop "Sapling receivers are valid" $ forAll genSapArgs $ \i -> prop_SaplingReceiver i + describe "Sapling Change Payment Address generation test" $ do + it "Call genSaplingInternalAddress" $ do + let sk = [ 3 + , 183 + , 26 + , 151 + , 89 + , 0 + , 0 + , 0 + , 128 + , 199 + , 189 + , 33 + , 169 + , 114 + , 194 + , 50 + , 0 + , 139 + , 140 + , 162 + , 100 + , 100 + , 35 + , 58 + , 226 + , 6 + , 47 + , 232 + , 34 + , 214 + , 11 + , 173 + , 142 + , 40 + , 45 + , 163 + , 190 + , 207 + , 49 + , 130 + , 158 + , 113 + , 232 + , 251 + , 79 + , 98 + , 77 + , 195 + , 196 + , 40 + , 42 + , 113 + , 133 + , 35 + , 211 + , 68 + , 146 + , 104 + , 5 + , 56 + , 244 + , 162 + , 55 + , 239 + , 55 + , 112 + , 37 + , 38 + , 189 + , 183 + , 121 + , 201 + , 1 + , 60 + , 158 + , 151 + , 141 + , 123 + , 250 + , 95 + , 169 + , 123 + , 208 + , 56 + , 103 + , 74 + , 85 + , 49 + , 152 + , 207 + , 245 + , 216 + , 58 + , 37 + , 0 + , 127 + , 186 + , 245 + , 234 + , 47 + , 68 + , 11 + , 78 + , 76 + , 12 + , 171 + , 37 + , 63 + , 172 + , 90 + , 111 + , 94 + , 88 + , 152 + , 211 + , 53 + , 243 + , 142 + , 16 + , 195 + , 142 + , 50 + , 14 + , 13 + , 32 + , 91 + , 5 + , 82 + , 182 + , 121 + , 136 + , 109 + , 125 + , 165 + , 125 + , 123 + , 226 + , 54 + , 60 + , 34 + , 62 + , 111 + , 167 + , 88 + , 254 + , 113 + , 204 + , 47 + , 181 + , 97 + , 18 + , 220 + , 46 + , 51 + , 160 + , 62 + , 16 + , 199 + , 143 + , 184 + , 200 + , 209 + , 124 + , 154 + , 175 + , 29 + , 216 + , 48 + , 201] :: [Word8] + let cAdr = [31, 232, 31, 17, 195, -- 196 + 178, 208, 227, 206, + 199, 105, 55, 147, + 23, 151, 206, 117, + 59, 249, 162, 218, + 140, 189, 17, 60, + 116, 106, 56, 64, + 203, 152, 52, 155, + 133, 179, 118, 47, + 161, 70, 155, 21, + 22, 41] :: [Word8] + let bscAdr = BS.pack cAdr + let ca = genSaplingInternalAddress (BS.pack sk) + ca `shouldBe` bscAdr -- | Properties prop_PhraseLength :: Int -> Property From db92dd7de7d6f4bc3eb697cf10c0e09e0c57bb98 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 11 Mar 2024 15:23:29 -0500 Subject: [PATCH 056/149] Implement Sapling child key derivation --- librustzcash-wrapper/src/lib.rs | 18 +++---- src/C/Zcash.chs | 1 + src/ZcashHaskell/Sapling.hs | 10 ++-- test/Spec.hs | 88 +++++++++++++++++---------------- zcash-haskell.cabal | 2 +- 5 files changed, 62 insertions(+), 57 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 5eea232..c2b2607 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -26,6 +26,7 @@ use zip32; use zcash_primitives::{ zip32::{ Scope as SaplingScope, + ChildIndex, sapling_find_address, sapling::DiversifierKey }, @@ -628,21 +629,20 @@ pub extern "C" fn rust_wrapper_recover_seed( pub extern "C" fn rust_wrapper_sapling_spendingkey( iseed: *const u8, iseed_len: usize, + ix: u32, out: *mut u8, out_len: &mut usize ){ let seed: Vec = marshall_from_haskell_var(iseed, iseed_len, RW); - if ( seed.len() != 64 ) { - // invalid seed length - marshall_to_haskell_var(&vec![0], out, out_len, RW); - } else { - // Obtain the ExtendedSpendingKey using the seed - // Returns a byte array (169 bytes) - let su8 = &seed; - let seedu8 : &[u8] = &su8; - let extsk: ExtendedSpendingKey = ExtendedSpendingKey::master(&seedu8); + let su8 = &seed; + let seedu8 : &[u8] = &su8; + let extsk: ExtendedSpendingKey = ExtendedSpendingKey::master(&seedu8); + if ix == 0 { let extsk_bytes = extsk.to_bytes().to_vec(); marshall_to_haskell_var(&extsk_bytes, out, out_len, RW); + } else { + let child_sk = extsk.derive_child(ChildIndex::from_index(ix)); + marshall_to_haskell_var(&child_sk.to_bytes().to_vec(), out, out_len, RW); } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 1e32f93..fc60de0 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -136,6 +136,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey { toBorshVar* `BS.ByteString'& + , `Word32' , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 0a25661..d4eaa71 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -95,13 +95,15 @@ instance FromJSON RawTxResponse where pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b -- | Attempts to obtain a sapling SpendingKey using a HDSeed -genSaplingSpendingKey :: Seed -> Maybe SaplingSpendingKey -genSaplingSpendingKey seed = do - if BS.length res == 196 +genSaplingSpendingKey :: Seed -> Int -> Maybe SaplingSpendingKey +genSaplingSpendingKey seed i = do + if BS.length res == 169 then Just res else Nothing where - res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed) + res = + withPureBorshVarBuffer + (rustWrapperSaplingSpendingkey seed (fromIntegral i)) -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index genSaplingPaymentAddress :: SaplingSpendingKey -> Int -> Maybe SaplingReceiver diff --git a/test/Spec.hs b/test/Spec.hs index e6d6bc2..0762410 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -20,6 +20,7 @@ {-# LANGUAGE TypeSynonymInstances #-} import C.Zcash (rustWrapperUADecode) +import Control.Exception (throwIO) import Control.Monad.IO.Class (liftIO) import Data.Aeson import Data.Bool (Bool(True)) @@ -61,6 +62,7 @@ import ZcashHaskell.Types , Phrase(..) , RawData(..) , RawTxResponse(..) + , Seed(..) , ShieldedOutput(..) , UnifiedAddress(..) , UnifiedFullViewingKey(..) @@ -466,12 +468,18 @@ main = do msg `shouldBe` "Hello World!\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL" describe "Wallet seed phrase" $ do - prop "Generated phrases are valid" prop_PhraseLength - prop "Derived seeds are valid" prop_SeedLength - prop "Orchard spending keys are valid" $ - forAll genOrcArgs $ \(c, i, _) -> prop_OrchardSpendingKey c i - prop "Orchard receivers are valid" $ - forAll genOrcArgs $ \(c, i, j) -> prop_OrchardReceiver c i j + prop "Generated phrases are valid" $ again prop_PhraseLength + prop "Derived seeds are valid" $ again prop_SeedLength + before getSeed $ + describe "Optimized spending key tests" $ do + it "Sapling spending keys are valid" $ \s -> + property $ prop_SaplingSpendingKey s + it "Sapling receivers are valid" $ \s -> + property $ prop_SaplingReceiver s + it "Orchard spending keys are valid" $ \s -> + property $ prop_OrchardSpendingKey s + it "Orchard receivers are valid" $ \s -> + property $ prop_OrchardReceiver s describe "Address tests" $ do it "Encode transparent" $ do let ua = @@ -481,7 +489,7 @@ main = do Nothing -> "Bad UA" Just u -> maybe "No transparent" encodeTransparent $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" - describe "Transparent Private and Publicc Key Generation" $ do + describe "Transparent Private and Public Key Generation" $ do it "Obtain a transparent extended private key from HDSeed" $ do let hdseed = [ 206 @@ -577,55 +585,38 @@ main = do xtpubk <- xtpubkIO ---print $ show xtpubk xtpubk `shouldBe` testpbk - describe "Sapling SpendingKey test" $ do - it "Generate Sapling spending key" $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - genSaplingSpendingKey <$> s `shouldNotBe` Nothing - describe "Sapling Payment Address generation test" $ do - it "Call genSaplingPaymentAddress" $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing - prop "Sapling receivers are valid" $ - forAll genSapArgs $ \i -> prop_SaplingReceiver i -- | Properties -prop_PhraseLength :: Int -> Property -prop_PhraseLength i = +prop_PhraseLength :: Property +prop_PhraseLength = ioProperty $ do p <- generateWalletSeedPhrase return $ BS.length p >= 95 -prop_SeedLength :: Int -> Property -prop_SeedLength i = +prop_SeedLength :: Property +prop_SeedLength = ioProperty $ do p <- generateWalletSeedPhrase let s = getWalletSeed p return $ maybe 0 BS.length s === 64 -prop_OrchardSpendingKey :: CoinType -> Int -> Property -prop_OrchardSpendingKey c i = - ioProperty $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - return $ genOrchardSpendingKey (fromMaybe "" s) c i =/= Nothing +prop_OrchardSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property +prop_OrchardSpendingKey s c (NonNegative i) = + genOrchardSpendingKey s c i =/= Nothing -prop_OrchardReceiver :: CoinType -> Int -> Int -> Property -prop_OrchardReceiver c i j = - ioProperty $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - let sk = genOrchardSpendingKey (fromMaybe "" s) c i - return $ genOrchardReceiver j (fromMaybe "" sk) =/= Nothing +prop_OrchardReceiver :: + Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property +prop_OrchardReceiver s c (NonNegative i) (NonNegative j) = + genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= Nothing -prop_SaplingReceiver :: Int -> Property -prop_SaplingReceiver i = - ioProperty $ do - p <- generateWalletSeedPhrase - let s = getWalletSeed p - let sk = genSaplingSpendingKey (fromMaybe "" s) - return $ genSaplingPaymentAddress (fromMaybe "" sk) i =/= Nothing +prop_SaplingSpendingKey :: Seed -> NonNegative Int -> Property +prop_SaplingSpendingKey s (NonNegative i) = + genSaplingSpendingKey s i =/= Nothing + +prop_SaplingReceiver :: Seed -> NonNegative Int -> NonNegative Int -> Property +prop_SaplingReceiver s (NonNegative i) (NonNegative j) = + genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s j) i =/= + Nothing -- | Generators genOrcArgs :: Gen (CoinType, Int, Int) @@ -637,4 +628,15 @@ genOrcArgs = do genSapArgs :: Gen Int genSapArgs = choose (1, 50) + +getSeed :: IO Seed +getSeed = do + p <- generateWalletSeedPhrase + let s = getWalletSeed p + case s of + Nothing -> throwIO $ userError "Couldn't generate seed" + Just s' -> return s' + -- | Arbitrary instances +instance Arbitrary CoinType where + arbitrary = elements [MainNetCoin, TestNetCoin, RegTestNetCoin] diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 0bee561..19e047d 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.4.1 +version: 0.4.4.2 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From ddaabcc5cd08d04be1d0e9020fb051cdee7ac32c Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 11 Mar 2024 15:48:27 -0500 Subject: [PATCH 057/149] Add test to check that Sapling receivers are unique --- test/Spec.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/Spec.hs b/test/Spec.hs index 0762410..6e815b3 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -476,6 +476,8 @@ main = do property $ prop_SaplingSpendingKey s it "Sapling receivers are valid" $ \s -> property $ prop_SaplingReceiver s + it "Sapling receivers are not the same" $ \s -> + property $ prop_SaplingRecRepeated s it "Orchard spending keys are valid" $ \s -> property $ prop_OrchardSpendingKey s it "Orchard receivers are valid" $ \s -> @@ -618,6 +620,11 @@ prop_SaplingReceiver s (NonNegative i) (NonNegative j) = genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s j) i =/= Nothing +prop_SaplingRecRepeated :: Seed -> NonNegative Int -> Property +prop_SaplingRecRepeated s (NonNegative i) = + genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s 1) i =/= + genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s 1) (i + 1) + -- | Generators genOrcArgs :: Gen (CoinType, Int, Int) genOrcArgs = do From 5db66dcf39810d88cd785fc8b6e6e91f57b4335b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 12 Mar 2024 12:22:05 -0500 Subject: [PATCH 058/149] Adjust indexing of Sapling payment addresses --- src/ZcashHaskell/Sapling.hs | 4 ++-- test/Spec.hs | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index d4eaa71..fd898f0 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -103,7 +103,7 @@ genSaplingSpendingKey seed i = do where res = withPureBorshVarBuffer - (rustWrapperSaplingSpendingkey seed (fromIntegral i)) + (rustWrapperSaplingSpendingkey seed (fromIntegral (i + 2 ^ 31))) -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index genSaplingPaymentAddress :: SaplingSpendingKey -> Int -> Maybe SaplingReceiver @@ -114,7 +114,7 @@ genSaplingPaymentAddress extspk i = where res = withPureBorshVarBuffer - (rustWrapperSaplingPaymentAddress extspk (fromIntegral i)) + (rustWrapperSaplingPaymentAddress extspk (fromIntegral (i * 111))) -- | Generate an internal Sapling address genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingInternalReceiver diff --git a/test/Spec.hs b/test/Spec.hs index 6e815b3..65470b2 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -476,12 +476,14 @@ main = do property $ prop_SaplingSpendingKey s it "Sapling receivers are valid" $ \s -> property $ prop_SaplingReceiver s - it "Sapling receivers are not the same" $ \s -> + it "Sapling receivers are distinct" $ \s -> property $ prop_SaplingRecRepeated s it "Orchard spending keys are valid" $ \s -> property $ prop_OrchardSpendingKey s it "Orchard receivers are valid" $ \s -> property $ prop_OrchardReceiver s + it "Orchard receivers are distinct" $ \s -> + property $ prop_OrchardRecRepeated s describe "Address tests" $ do it "Encode transparent" $ do let ua = @@ -625,6 +627,12 @@ prop_SaplingRecRepeated s (NonNegative i) = genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s 1) i =/= genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s 1) (i + 1) +prop_OrchardRecRepeated :: + Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property +prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) = + genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= + genOrchardReceiver (j + 1) (fromMaybe "" $ genOrchardSpendingKey s c i) + -- | Generators genOrcArgs :: Gen (CoinType, Int, Int) genOrcArgs = do From 5b6f5fa8e2bfb2a8cede369431ba432b879bc9f1 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 12 Mar 2024 16:02:35 -0500 Subject: [PATCH 059/149] Upgrade `HexString` --- cabal.project | 2 +- cabal.project.freeze | 2 +- zcash-haskell.cabal | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cabal.project b/cabal.project index a64c126..989ae5a 100644 --- a/cabal.project +++ b/cabal.project @@ -10,4 +10,4 @@ source-repository-package source-repository-package type: git location: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git - tag: fd1ddce73c0ad18a2a4509a299c6e93f8c6c383d + tag: 39d8da7b11a80269454c2f134a5c834e0f3cb9a7 diff --git a/cabal.project.freeze b/cabal.project.freeze index 49f5987..5cf3ed7 100644 --- a/cabal.project.freeze +++ b/cabal.project.freeze @@ -99,7 +99,7 @@ constraints: any.Cabal ==3.8.1.0, hashable +integer-gmp -random-initial-seed, any.haskell-lexer ==1.1.1, any.haskoin-core ==1.0.4, - any.hexstring ==0.12.0, + any.hexstring ==0.12.1.0, any.hourglass ==0.2.12, any.hsc2hs ==0.68.10, hsc2hs -in-ghc-tree, diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 19e047d..584e7ba 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -51,7 +51,7 @@ library , cryptonite , foreign-rust , generics-sop - , hexstring >=0.12 + , hexstring >=0.12.1 , http-conduit , memory , text @@ -72,7 +72,7 @@ test-suite zcash-haskell-test , base >=4.7 && <5 , bytestring , haskoin-core - , hexstring + , hexstring >= 0.12.1 , hspec , QuickCheck , quickcheck-transformer From 4f20160c36c60f3df5a3d24c8e3711320e8cead2 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 12 Mar 2024 16:03:35 -0500 Subject: [PATCH 060/149] Enable Unified Address generation tests --- librustzcash-wrapper/src/lib.rs | 35 +++-- src/ZcashHaskell/Orchard.hs | 9 +- src/ZcashHaskell/Sapling.hs | 6 +- src/ZcashHaskell/Transparent.hs | 39 ++++-- src/ZcashHaskell/Types.hs | 34 ++--- test/Spec.hs | 241 +++++++++++++++++++------------- 6 files changed, 214 insertions(+), 150 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index c2b2607..fa8e042 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -641,7 +641,7 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey( let extsk_bytes = extsk.to_bytes().to_vec(); marshall_to_haskell_var(&extsk_bytes, out, out_len, RW); } else { - let child_sk = extsk.derive_child(ChildIndex::from_index(ix)); + let child_sk = extsk.derive_child(ChildIndex::from_index(ix + (1 << 31))); marshall_to_haskell_var(&child_sk.to_bytes().to_vec(), out, out_len, RW); } } @@ -655,16 +655,29 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress( out_len: &mut usize ){ let extspk: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); - let expsk = ExpandedSpendingKey::from_spending_key(&extspk); - let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk); - let dk = DiversifierKey::master(&extspk); - let result = sapling_find_address(&fvk, &dk, DiversifierIndex::from(div_ix)); - match result { - Some((_d, p_address)) => { - marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW); - }, - None => { - marshall_to_haskell_var(&vec![0], out, out_len, RW); + if div_ix == 0 { + let sp_key = ExtendedSpendingKey::from_bytes(&extspk); + match sp_key { + Ok(sp_key_x) => { + let (def_div, def_address) = sp_key_x.default_address(); + marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW); + }, + Err(_e) => { + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } + } + } else { + let expsk = ExpandedSpendingKey::from_spending_key(&extspk); + let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk); + let dk = DiversifierKey::master(&extspk); + let result = sapling_find_address(&fvk, &dk, DiversifierIndex::from(div_ix)); + match result { + Some((_d, p_address)) => { + marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW); + }, + None => { + marshall_to_haskell_var(&vec![0], out, out_len, RW); + } } } } diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 22a82c2..26c9fa1 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -27,6 +27,7 @@ import C.Zcash ) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C +import Data.HexString (fromRawBytes, toBytes) import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word @@ -83,9 +84,9 @@ isValidUnifiedAddress str = then Just (raw_s x) else Nothing) (if not (BS.null (raw_t x)) - then Just $ TransparentAddress P2PKH whichNet (raw_t x) + then Just $ TransparentAddress P2PKH (fromRawBytes $ raw_t x) else if not (BS.null (raw_to x)) - then Just $ TransparentAddress P2SH whichNet (raw_to x) + then Just $ TransparentAddress P2SH (fromRawBytes $ raw_to x) else Nothing) -- | Encode a 'UnifiedAddress' per [ZIP-316](https://zips.z.cash/zip-0316) @@ -102,8 +103,8 @@ encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b Nothing -> BS.empty Just t -> case ta_type t of - P2SH -> packReceiver 0x01 $ Just $ ta_bytes t - P2PKH -> packReceiver 0x00 $ Just $ ta_bytes t + P2SH -> packReceiver 0x01 $ Just $ toBytes $ ta_bytes t + P2PKH -> packReceiver 0x00 $ Just $ toBytes $ ta_bytes t sReceiver = packReceiver 0x02 $ s_rec ua oReceiver = packReceiver 0x03 $ o_rec ua padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index fd898f0..b9fac6b 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -103,11 +103,11 @@ genSaplingSpendingKey seed i = do where res = withPureBorshVarBuffer - (rustWrapperSaplingSpendingkey seed (fromIntegral (i + 2 ^ 31))) + (rustWrapperSaplingSpendingkey seed (fromIntegral i)) -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index -genSaplingPaymentAddress :: SaplingSpendingKey -> Int -> Maybe SaplingReceiver -genSaplingPaymentAddress extspk i = +genSaplingPaymentAddress :: Int -> SaplingSpendingKey -> Maybe SaplingReceiver +genSaplingPaymentAddress i extspk = if BS.length res == 43 then Just res else Nothing diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 210ead9..8c2bf89 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -15,13 +15,13 @@ -- module ZcashHaskell.Transparent where +import Control.Exception (throwIO) import Crypto.Hash import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58) import qualified Data.Text as T import qualified Data.Text.Encoding as E -import Data.Word import ZcashHaskell.Types ( TransparentAddress(..) , TransparentType(..) @@ -29,13 +29,17 @@ import ZcashHaskell.Types , getTransparentPrefix ) -import Haskoin.Crypto.Keys.Extended -import Data.Word import Crypto.Secp256k1 +import Data.HexString +import Data.Word +import Haskoin.Address (Address(..)) +import qualified Haskoin.Crypto.Hash as H +import Haskoin.Crypto.Keys.Extended -encodeTransparent :: TransparentAddress -> T.Text -encodeTransparent t = - encodeTransparent' (getTransparentPrefix (ta_net t) (ta_type t)) $ ta_bytes t +encodeTransparent :: ZcashNet -> TransparentAddress -> T.Text +encodeTransparent zNet t = + encodeTransparent' (getTransparentPrefix zNet (ta_type t)) $ + toBytes $ ta_bytes t where encodeTransparent' :: (Word8, Word8) -> BS.ByteString -> T.Text encodeTransparent' (a, b) h = @@ -47,17 +51,22 @@ encodeTransparent t = checksum = sha256 $ sha256 digest -- | Attempts to generate an Extended Private Key from a known HDSeed. -genTransparentPrvKey :: - BS.ByteString -> XPrvKey +genTransparentPrvKey :: BS.ByteString -> XPrvKey genTransparentPrvKey hdseed = do - makeXPrvKey hdseed + makeXPrvKey hdseed -- | Attempts to obtain an Extended Public Key from a known Extended Private Key -genTransparentPubKey :: - XPrvKey -> IO XPubKey +genTransparentPubKey :: XPrvKey -> IO XPubKey genTransparentPubKey xpvk = do - ioCtx <- createContext - let xpubk = deriveXPubKey ioCtx xpvk - return xpubk - + ioCtx <- createContext + let xpubk = deriveXPubKey ioCtx xpvk + return xpubk +genTransparentReceiver :: XPubKey -> IO TransparentAddress +genTransparentReceiver xpubk = do + ioCtx <- createContext + let x = xPubAddr ioCtx xpubk + case x of + PubKeyAddress k -> return $ TransparentAddress P2PKH $ fromBinary k + ScriptAddress j -> return $ TransparentAddress P2SH $ fromBinary j + _anyOtherKind -> throwIO $ userError "Unsupported transparent address type" diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 7c14558..ad6bc13 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -36,6 +36,7 @@ import qualified Data.Text.Encoding as E import Data.Word import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP +import Haskoin.Address (Address) -- * General -- @@ -45,21 +46,6 @@ type Seed = C.ByteString -- | A mnemonic phrase used to derive seeds type Phrase = BS.ByteString --- | A spending key for Sapling -type SaplingSpendingKey = BS.ByteString - --- | A spending key for Orchard -type OrchardSpendingKey = BS.ByteString - --- | A Sapling receiver -type SaplingReceiver = BS.ByteString - --- | A Sapling internal receiver -type SaplingInternalReceiver = BS.ByteString - --- | An Orchard receiver -type OrchardReceiver = BS.ByteString - -- | Type to represent data after Bech32 decoding data RawData = RawData { hrp :: !BS.ByteString -- ^ Human-readable part of the Bech32 encoding @@ -250,11 +236,19 @@ data TransparentType -- | Type to represent a transparent Zcash addresses data TransparentAddress = TransparentAddress { ta_type :: !TransparentType - , ta_net :: !ZcashNet - , ta_bytes :: !BS.ByteString + , ta_bytes :: !HexString } deriving (Eq, Prelude.Show, Read) -- * Sapling +-- | A spending key for Sapling +type SaplingSpendingKey = BS.ByteString + +-- | A Sapling receiver +type SaplingReceiver = BS.ByteString + +-- | A Sapling internal receiver +type SaplingInternalReceiver = BS.ByteString + -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ShieldedOutput = ShieldedOutput { s_cv :: !HexString -- ^ Value commitment to the input note @@ -280,6 +274,12 @@ instance FromJSON ShieldedOutput where pure $ ShieldedOutput cv cmu ephKey encText outText p -- * Orchard +-- | A spending key for Orchard +type OrchardSpendingKey = BS.ByteString + +-- | An Orchard receiver +type OrchardReceiver = BS.ByteString + -- | Type to represent a Unified Address data UnifiedAddress = UnifiedAddress { ua_net :: !ZcashNet diff --git a/test/Spec.hs b/test/Spec.hs index 65470b2..37fc16e 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -66,6 +66,7 @@ import ZcashHaskell.Types , ShieldedOutput(..) , UnifiedAddress(..) , UnifiedFullViewingKey(..) + , ZcashNet(..) , decodeHexText , getValue ) @@ -470,6 +471,102 @@ main = do describe "Wallet seed phrase" $ do prop "Generated phrases are valid" $ again prop_PhraseLength prop "Derived seeds are valid" $ again prop_SeedLength + describe "Transparent Private and Public Key Generation" $ do + it "Obtain a transparent extended private key from HDSeed" $ do + let hdseed = + [ 206 + , 61 + , 120 + , 38 + , 206 + , 40 + , 201 + , 62 + , 83 + , 175 + , 151 + , 131 + , 218 + , 141 + , 206 + , 254 + , 28 + , 244 + , 172 + , 213 + , 128 + , 248 + , 156 + , 45 + , 204 + , 44 + , 169 + , 3 + , 162 + , 188 + , 16 + , 173 + , 192 + , 164 + , 96 + , 148 + , 91 + , 52 + , 244 + , 83 + , 149 + , 169 + , 82 + , 196 + , 199 + , 53 + , 177 + , 170 + , 1 + , 6 + , 0 + , 120 + , 170 + , 2 + , 238 + , 219 + , 241 + , 243 + , 172 + , 178 + , 104 + , 81 + , 159 + , 144 + ] :: [Word8] + let xtpvk = genTransparentPrvKey (BS.pack hdseed) + let testpvk = + XPrvKey + 0 + "0000000000" + 0 + "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" + "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" + xtpvk `shouldBe` testpvk + it "Obtain a transparent extended public key from private key" $ do + let testpvk = + XPrvKey + 0 + "0000000000" + 0 + "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" + "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" + let testpbk = + XPubKey + 0 + "00000000" + 0 + "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" + "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8" + let xtpubkIO = genTransparentPubKey testpvk + xtpubk <- xtpubkIO + ---print $ show xtpubk + xtpubk `shouldBe` testpbk before getSeed $ describe "Optimized spending key tests" $ do it "Sapling spending keys are valid" $ \s -> @@ -491,104 +588,48 @@ main = do let msg = case isValidUnifiedAddress ua of Nothing -> "Bad UA" - Just u -> maybe "No transparent" encodeTransparent $ t_rec u + Just u -> + maybe "No transparent" (encodeTransparent (ua_net u)) $ + t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" - describe "Transparent Private and Public Key Generation" $ do - it "Obtain a transparent extended private key from HDSeed" $ do - let hdseed = - [ 206 - , 61 - , 120 - , 38 - , 206 - , 40 - , 201 - , 62 - , 83 - , 175 - , 151 - , 131 - , 218 - , 141 - , 206 - , 254 - , 28 - , 244 - , 172 - , 213 - , 128 - , 248 - , 156 - , 45 - , 204 - , 44 - , 169 - , 3 - , 162 - , 188 - , 16 - , 173 - , 192 - , 164 - , 96 - , 148 - , 91 - , 52 - , 244 - , 83 - , 149 - , 169 - , 82 - , 196 - , 199 - , 53 - , 177 - , 170 - , 1 - , 6 - , 0 - , 120 - , 170 - , 2 - , 238 - , 219 - , 241 - , 243 - , 172 - , 178 - , 104 - , 81 - , 159 - , 144 - ] :: [Word8] - let xtpvk = genTransparentPrvKey (BS.pack hdseed) - let testpvk = - XPrvKey - 0 - "0000000000" - 0 - "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" - "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" - xtpvk `shouldBe` testpvk - it "Obtain a transparent extended public key from private key" $ do - let testpvk = - XPrvKey - 0 - "0000000000" - 0 - "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" - "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" - let testpbk = - XPubKey - 0 - "00000000" - 0 - "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" - "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8" - let xtpubkIO = genTransparentPubKey testpvk - xtpubk <- xtpubkIO - ---print $ show xtpubk - xtpubk `shouldBe` testpbk + it "Recover UA from YWallet" $ + ioProperty $ do + let p = + "security expect junk hour people bind law hub between topic wink cliff spirit scissors auction idle figure option wide useful swift prison cushion round" + let targetUA = + isValidUnifiedAddress + "u1qsylqauvnhw8tsfe3cldcsj3mjrfqzgaf3mt8yzlkjuvsf5wzj223yvrt8q66qukfqcc80x3z0mk6ym6pm2f0hukzkp6t4wj78h85t6kfr2u9mqsfhdd73g3sc7ezy2ut3rtq5jmejatwv4xqqd6l8tt9fycer8kdw0gz6e607nkssqsc7kd7nk2yfz2hpvpqhdg39wxalpjzhe34j7" + let s = getWalletSeed p + case s of + Nothing -> return $ expectationFailure "Failed to generate seed" + Just s' -> do + let oK = genOrchardSpendingKey s' MainNetCoin 0 + let sK = genSaplingSpendingKey s' 0 + let tK = genTransparentPrvKey s' + let oR = genOrchardReceiver 0 =<< oK + let sR = genSaplingPaymentAddress 0 =<< sK + tR <- genTransparentReceiver =<< genTransparentPubKey tK + let newUA = UnifiedAddress MainNet oR sR $ Just tR + return $ Just newUA `shouldBe` targetUA + it "Recover UA from Zingo" $ + ioProperty $ do + let p = + "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest" + let targetUA = + isValidUnifiedAddress + "u1trd8cvc6265ywwj4mmvuznsye5ghe2dhhn3zy8kcuyg4vx3svskw9r2dedp5hu6m740vylkqc34t4w9eqkl9fyu5uyzn3af72jg235440ke6tu5cf994eq85n97x69x9824hqejmwz3d8qqthtesrd6gerjupdymldhl9xccejjwfj0dhh9mt4rw4kytp325twlutsxd20rfqhzxu3m" + let s = getWalletSeed p + case s of + Nothing -> return $ expectationFailure "Failed to generate seed" + Just s' -> do + let oK = genOrchardSpendingKey s' MainNetCoin 0 + let sK = genSaplingSpendingKey s' 0 + let tK = genTransparentPrvKey s' + let oR = genOrchardReceiver 0 =<< oK + let sR = genSaplingPaymentAddress 0 =<< sK + tR <- genTransparentReceiver =<< genTransparentPubKey tK + let newUA = UnifiedAddress MainNet oR sR $ Just tR + return $ Just newUA `shouldBe` targetUA -- | Properties prop_PhraseLength :: Property @@ -619,13 +660,13 @@ prop_SaplingSpendingKey s (NonNegative i) = prop_SaplingReceiver :: Seed -> NonNegative Int -> NonNegative Int -> Property prop_SaplingReceiver s (NonNegative i) (NonNegative j) = - genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s j) i =/= + genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s j) =/= Nothing prop_SaplingRecRepeated :: Seed -> NonNegative Int -> Property prop_SaplingRecRepeated s (NonNegative i) = - genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s 1) i =/= - genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s 1) (i + 1) + genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s 1) =/= + genSaplingPaymentAddress (i + 1) (fromMaybe "" $ genSaplingSpendingKey s 1) prop_OrchardRecRepeated :: Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property From c5feb10fbcbb91344ec5d8416f4941083e41cfb9 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 13 Mar 2024 12:50:39 -0500 Subject: [PATCH 061/149] Update Sapling spending key with coin type --- librustzcash-wrapper/src/lib.rs | 24 +++++++++--------------- src/C/Zcash.chs | 1 + src/ZcashHaskell/Sapling.hs | 10 +++++++--- test/Spec.hs | 25 +++++++++++++------------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index fa8e042..1921f73 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -59,11 +59,12 @@ use zcash_address::{ }; use zcash_client_backend::keys::sapling::{ + spending_key, ExtendedFullViewingKey, ExtendedSpendingKey }; -use zcash_primitives::zip32::{ AccountId, DiversifierIndex }; +use zcash_primitives::zip32::DiversifierIndex; use orchard::{ Action, @@ -627,23 +628,16 @@ pub extern "C" fn rust_wrapper_recover_seed( #[no_mangle] pub extern "C" fn rust_wrapper_sapling_spendingkey( - iseed: *const u8, - iseed_len: usize, - ix: u32, + seed: *const u8, + seed_len: usize, + coin_type: u32, + acc_id: u32, out: *mut u8, out_len: &mut usize ){ - let seed: Vec = marshall_from_haskell_var(iseed, iseed_len, RW); - let su8 = &seed; - let seedu8 : &[u8] = &su8; - let extsk: ExtendedSpendingKey = ExtendedSpendingKey::master(&seedu8); - if ix == 0 { - let extsk_bytes = extsk.to_bytes().to_vec(); - marshall_to_haskell_var(&extsk_bytes, out, out_len, RW); - } else { - let child_sk = extsk.derive_child(ChildIndex::from_index(ix + (1 << 31))); - marshall_to_haskell_var(&child_sk.to_bytes().to_vec(), out, out_len, RW); - } + let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); + let sk = spending_key(&s, coin_type, zcash_primitives::zip32::AccountId::try_from(acc_id).unwrap()); + marshall_to_haskell_var(&sk.to_bytes().to_vec(), out, out_len, RW); } #[no_mangle] diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index fc60de0..94204db 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -137,6 +137,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey { toBorshVar* `BS.ByteString'& , `Word32' + , `Word32' , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index b9fac6b..381ee51 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -46,6 +46,7 @@ import ZcashHaskell.Types , Seed(..) , ShieldedOutput(..) , decodeHexText + , getValue ) import ZcashHaskell.Utils (decodeBech32) @@ -95,15 +96,18 @@ instance FromJSON RawTxResponse where pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b -- | Attempts to obtain a sapling SpendingKey using a HDSeed -genSaplingSpendingKey :: Seed -> Int -> Maybe SaplingSpendingKey -genSaplingSpendingKey seed i = do +genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey +genSaplingSpendingKey seed c i = do if BS.length res == 169 then Just res else Nothing where res = withPureBorshVarBuffer - (rustWrapperSaplingSpendingkey seed (fromIntegral i)) + (rustWrapperSaplingSpendingkey + seed + (fromIntegral $ getValue c) + (fromIntegral i)) -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index genSaplingPaymentAddress :: Int -> SaplingSpendingKey -> Maybe SaplingReceiver diff --git a/test/Spec.hs b/test/Spec.hs index 37fc16e..c80f264 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -604,7 +604,7 @@ main = do Nothing -> return $ expectationFailure "Failed to generate seed" Just s' -> do let oK = genOrchardSpendingKey s' MainNetCoin 0 - let sK = genSaplingSpendingKey s' 0 + let sK = genSaplingSpendingKey s' MainNetCoin 0 let tK = genTransparentPrvKey s' let oR = genOrchardReceiver 0 =<< oK let sR = genSaplingPaymentAddress 0 =<< sK @@ -623,7 +623,7 @@ main = do Nothing -> return $ expectationFailure "Failed to generate seed" Just s' -> do let oK = genOrchardSpendingKey s' MainNetCoin 0 - let sK = genSaplingSpendingKey s' 0 + let sK = genSaplingSpendingKey s' MainNetCoin 0 let tK = genTransparentPrvKey s' let oR = genOrchardReceiver 0 =<< oK let sR = genSaplingPaymentAddress 0 =<< sK @@ -654,19 +654,20 @@ prop_OrchardReceiver :: prop_OrchardReceiver s c (NonNegative i) (NonNegative j) = genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= Nothing -prop_SaplingSpendingKey :: Seed -> NonNegative Int -> Property -prop_SaplingSpendingKey s (NonNegative i) = - genSaplingSpendingKey s i =/= Nothing +prop_SaplingSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property +prop_SaplingSpendingKey s c (NonNegative i) = + genSaplingSpendingKey s c i =/= Nothing -prop_SaplingReceiver :: Seed -> NonNegative Int -> NonNegative Int -> Property -prop_SaplingReceiver s (NonNegative i) (NonNegative j) = - genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s j) =/= +prop_SaplingReceiver :: + Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property +prop_SaplingReceiver s c (NonNegative i) (NonNegative j) = + genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s c j) =/= Nothing -prop_SaplingRecRepeated :: Seed -> NonNegative Int -> Property -prop_SaplingRecRepeated s (NonNegative i) = - genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s 1) =/= - genSaplingPaymentAddress (i + 1) (fromMaybe "" $ genSaplingSpendingKey s 1) +prop_SaplingRecRepeated :: Seed -> CoinType -> NonNegative Int -> Property +prop_SaplingRecRepeated s c (NonNegative i) = + genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s c 1) =/= + genSaplingPaymentAddress (i + 1) (fromMaybe "" $ genSaplingSpendingKey s c 1) prop_OrchardRecRepeated :: Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property From 6e1048cd4dc5be11f0e5ba60815e5d246e867060 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 13 Mar 2024 14:12:28 -0500 Subject: [PATCH 062/149] Adjust Transparent generators to use indices --- src/ZcashHaskell/Transparent.hs | 26 +++---- test/Spec.hs | 126 ++++++-------------------------- 2 files changed, 37 insertions(+), 115 deletions(-) diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 8c2bf89..944948e 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -23,7 +23,9 @@ import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58) import qualified Data.Text as T import qualified Data.Text.Encoding as E import ZcashHaskell.Types - ( TransparentAddress(..) + ( AccountId + , Seed + , TransparentAddress(..) , TransparentType(..) , ZcashNet(..) , getTransparentPrefix @@ -51,21 +53,19 @@ encodeTransparent zNet t = checksum = sha256 $ sha256 digest -- | Attempts to generate an Extended Private Key from a known HDSeed. -genTransparentPrvKey :: BS.ByteString -> XPrvKey -genTransparentPrvKey hdseed = do - makeXPrvKey hdseed - --- | Attempts to obtain an Extended Public Key from a known Extended Private Key -genTransparentPubKey :: XPrvKey -> IO XPubKey -genTransparentPubKey xpvk = do +genTransparentPrvKey :: Seed -> AccountId -> IO XPrvKey +genTransparentPrvKey hdseed i = do + let prvKey = makeXPrvKey hdseed ioCtx <- createContext - let xpubk = deriveXPubKey ioCtx xpvk - return xpubk + return $ hardSubKey ioCtx prvKey (fromIntegral i) -genTransparentReceiver :: XPubKey -> IO TransparentAddress -genTransparentReceiver xpubk = do +-- | Generate a transparent receiver +genTransparentReceiver :: Int -> XPrvKey -> IO TransparentAddress +genTransparentReceiver i xprvk = do ioCtx <- createContext - let x = xPubAddr ioCtx xpubk + let rootPubKey = deriveXPubKey ioCtx xprvk + let childPubKey = pubSubKey ioCtx rootPubKey (fromIntegral i) + let x = xPubAddr ioCtx childPubKey case x of PubKeyAddress k -> return $ TransparentAddress P2PKH $ fromBinary k ScriptAddress j -> return $ TransparentAddress P2SH $ fromBinary j diff --git a/test/Spec.hs b/test/Spec.hs index c80f264..8354c52 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -50,8 +50,6 @@ import ZcashHaskell.Sapling , matchSaplingAddress ) import ZcashHaskell.Transparent - --(encodeTransparent) - import ZcashHaskell.Types ( AccountId , BlockResponse(..) @@ -64,6 +62,8 @@ import ZcashHaskell.Types , RawTxResponse(..) , Seed(..) , ShieldedOutput(..) + , TransparentAddress(..) + , TransparentType(..) , UnifiedAddress(..) , UnifiedFullViewingKey(..) , ZcashNet(..) @@ -471,104 +471,12 @@ main = do describe "Wallet seed phrase" $ do prop "Generated phrases are valid" $ again prop_PhraseLength prop "Derived seeds are valid" $ again prop_SeedLength - describe "Transparent Private and Public Key Generation" $ do - it "Obtain a transparent extended private key from HDSeed" $ do - let hdseed = - [ 206 - , 61 - , 120 - , 38 - , 206 - , 40 - , 201 - , 62 - , 83 - , 175 - , 151 - , 131 - , 218 - , 141 - , 206 - , 254 - , 28 - , 244 - , 172 - , 213 - , 128 - , 248 - , 156 - , 45 - , 204 - , 44 - , 169 - , 3 - , 162 - , 188 - , 16 - , 173 - , 192 - , 164 - , 96 - , 148 - , 91 - , 52 - , 244 - , 83 - , 149 - , 169 - , 82 - , 196 - , 199 - , 53 - , 177 - , 170 - , 1 - , 6 - , 0 - , 120 - , 170 - , 2 - , 238 - , 219 - , 241 - , 243 - , 172 - , 178 - , 104 - , 81 - , 159 - , 144 - ] :: [Word8] - let xtpvk = genTransparentPrvKey (BS.pack hdseed) - let testpvk = - XPrvKey - 0 - "0000000000" - 0 - "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" - "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" - xtpvk `shouldBe` testpvk - it "Obtain a transparent extended public key from private key" $ do - let testpvk = - XPrvKey - 0 - "0000000000" - 0 - "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" - "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943" - let testpbk = - XPubKey - 0 - "00000000" - 0 - "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" - "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8" - let xtpubkIO = genTransparentPubKey testpvk - xtpubk <- xtpubkIO - ---print $ show xtpubk - xtpubk `shouldBe` testpbk before getSeed $ describe "Optimized spending key tests" $ do + it "Transparent spending keys are valid" $ \s -> + property $ prop_TransparentSpendingKey s + it "Transparent receivers are valid" $ \s -> + property $ prop_TransparentReceiver s it "Sapling spending keys are valid" $ \s -> property $ prop_SaplingSpendingKey s it "Sapling receivers are valid" $ \s -> @@ -605,10 +513,10 @@ main = do Just s' -> do let oK = genOrchardSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0 - let tK = genTransparentPrvKey s' + let tK = genTransparentPrvKey s' 0 let oR = genOrchardReceiver 0 =<< oK let sR = genSaplingPaymentAddress 0 =<< sK - tR <- genTransparentReceiver =<< genTransparentPubKey tK + tR <- genTransparentReceiver 0 =<< tK let newUA = UnifiedAddress MainNet oR sR $ Just tR return $ Just newUA `shouldBe` targetUA it "Recover UA from Zingo" $ @@ -624,10 +532,10 @@ main = do Just s' -> do let oK = genOrchardSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0 - let tK = genTransparentPrvKey s' + let tK = genTransparentPrvKey s' 0 let oR = genOrchardReceiver 0 =<< oK let sR = genSaplingPaymentAddress 0 =<< sK - tR <- genTransparentReceiver =<< genTransparentPubKey tK + tR <- genTransparentReceiver 0 =<< tK let newUA = UnifiedAddress MainNet oR sR $ Just tR return $ Just newUA `shouldBe` targetUA @@ -675,6 +583,20 @@ prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) = genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= genOrchardReceiver (j + 1) (fromMaybe "" $ genOrchardSpendingKey s c i) +prop_TransparentSpendingKey :: Seed -> NonNegative Int -> Property +prop_TransparentSpendingKey s (NonNegative i) = + ioProperty $ do + k <- genTransparentPrvKey s i + return $ xPrvChild k == fromIntegral i + +prop_TransparentReceiver :: + Seed -> NonNegative Int -> NonNegative Int -> Property +prop_TransparentReceiver s (NonNegative i) (NonNegative j) = + ioProperty $ do + k <- genTransparentPrvKey s i + r <- genTransparentReceiver j k + return $ ta_type r == P2PKH + -- | Generators genOrcArgs :: Gen (CoinType, Int, Int) genOrcArgs = do From 8a293f4e79cd1398c2cf22740be783bd77cf4713 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 11:12:31 -0500 Subject: [PATCH 063/149] Enable for internal/external Orchard receivers --- librustzcash-wrapper/src/lib.rs | 6 +++++- src/C/Zcash.chs | 1 + src/ZcashHaskell/Orchard.hs | 9 +++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 1921f73..9d09942 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -702,13 +702,17 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver( spend_key: *const u8, spend_key_len: usize, add_id: u32, + scope: bool, out: *mut u8, out_len: &mut usize ){ let sk_in: Vec = marshall_from_haskell_var(spend_key, spend_key_len, RW); let sk = SpendingKey::from_bytes(sk_in[0..32].try_into().unwrap()).unwrap(); let fvk = FullViewingKey::from(&sk); - let o_rec = fvk.address_at(add_id, Scope::External); + let sc = if scope { + Scope::External + } else {Scope::Internal}; + let o_rec = fvk.address_at(add_id, sc); marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 94204db..0f93729 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -163,6 +163,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_derive_orchard_receiver as rustWrapperGenOrchardReceiver { toBorshVar* `BS.ByteString'& , `Word32' + , `Bool' , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 26c9fa1..5dd000f 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -46,20 +46,21 @@ genOrchardSpendingKey s coinType accountId = k = withPureBorshVarBuffer $ rustWrapperGenOrchardSpendKey - s + (getBytes s) (getValue coinType) (fromIntegral accountId) -- | Derives an Orchard receiver for the given spending key and index -genOrchardReceiver :: Int -> OrchardSpendingKey -> Maybe OrchardReceiver -genOrchardReceiver i osk = +genOrchardReceiver :: + Int -> Scope -> OrchardSpendingKey -> Maybe OrchardReceiver +genOrchardReceiver i scope osk = if BS.length k /= 43 then Nothing else Just k where k = withPureBorshVarBuffer $ - rustWrapperGenOrchardReceiver osk (fromIntegral i) + rustWrapperGenOrchardReceiver osk (fromIntegral i) (scope == External) -- | Checks if given bytestring is a valid encoded unified address isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress From 23472ee1c4d15d64c0736f7ea1c5446bed93ea60 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 11:13:10 -0500 Subject: [PATCH 064/149] Improve type safety for Seed and Phrase --- src/ZcashHaskell/Keys.hs | 4 +-- src/ZcashHaskell/Transparent.hs | 5 +-- src/ZcashHaskell/Types.hs | 39 ++++++++++++++++++++-- test/Spec.hs | 59 +++++++++++++++++++++------------ 4 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/ZcashHaskell/Keys.hs b/src/ZcashHaskell/Keys.hs index a3b20b3..30484e7 100644 --- a/src/ZcashHaskell/Keys.hs +++ b/src/ZcashHaskell/Keys.hs @@ -21,7 +21,7 @@ import Foreign.Rust.Marshall.Variable ( withBorshVarBuffer , withPureBorshVarBuffer ) -import ZcashHaskell.Types (Phrase, Seed) +import ZcashHaskell.Types (Phrase, Seed(..), ToBytes(..)) -- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses. generateWalletSeedPhrase :: IO Phrase @@ -30,7 +30,7 @@ generateWalletSeedPhrase = withBorshVarBuffer rustWrapperGenSeedPhrase -- | Derive a cryptographic seed from the given seed phrase. getWalletSeed :: Phrase -> Maybe Seed getWalletSeed p = - if BS.length result > 0 + if BS.length (getBytes result) > 0 then Just result else Nothing where diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 944948e..7cc1dce 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -24,7 +24,8 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as E import ZcashHaskell.Types ( AccountId - , Seed + , Seed(..) + , ToBytes(..) , TransparentAddress(..) , TransparentType(..) , ZcashNet(..) @@ -55,7 +56,7 @@ encodeTransparent zNet t = -- | Attempts to generate an Extended Private Key from a known HDSeed. genTransparentPrvKey :: Seed -> AccountId -> IO XPrvKey genTransparentPrvKey hdseed i = do - let prvKey = makeXPrvKey hdseed + let prvKey = makeXPrvKey $ getBytes hdseed ioCtx <- createContext return $ hardSubKey ioCtx prvKey (fromIntegral i) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index ad6bc13..40be29b 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -15,6 +15,7 @@ -- {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE GeneralisedNewtypeDeriving #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE UndecidableInstances #-} @@ -41,10 +42,32 @@ import Haskoin.Address (Address) -- * General -- -- | A seed for generating private keys -type Seed = C.ByteString +newtype Seed = + Seed C.ByteString + deriving stock (Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Seed + +instance ToBytes Seed where + getBytes (Seed x) = x -- | A mnemonic phrase used to derive seeds -type Phrase = BS.ByteString +newtype Phrase = + Phrase BS.ByteString + deriving stock (Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Phrase + +instance ToBytes Phrase where + getBytes (Phrase x) = x + +-- | Scope for addresses/receivers +data Scope + = External + | Internal + deriving (Eq, Prelude.Show, Read) -- | Type to represent data after Bech32 decoding data RawData = RawData @@ -241,7 +264,12 @@ data TransparentAddress = TransparentAddress -- * Sapling -- | A spending key for Sapling -type SaplingSpendingKey = BS.ByteString +newtype SaplingSpendingKey = + SaplingSpendingKey BS.ByteString + deriving stock (Eq, Prelude.Show, Read) + +instance ToBytes SaplingSpendingKey where + getBytes (SaplingSpendingKey s) = s -- | A Sapling receiver type SaplingReceiver = BS.ByteString @@ -349,6 +377,11 @@ data DecodedNote = DecodedNote deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote +-- * Classes +-- | Class to represent types with a bytestring representation +class ToBytes a where + getBytes :: a -> BS.ByteString + -- * Helpers -- | Helper function to turn a hex-encoded string to bytestring decodeHexText :: String -> BS.ByteString diff --git a/test/Spec.hs b/test/Spec.hs index 8354c52..ff3a2c3 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -60,8 +60,11 @@ import ZcashHaskell.Types , Phrase(..) , RawData(..) , RawTxResponse(..) + , SaplingSpendingKey(..) + , Scope(..) , Seed(..) , ShieldedOutput(..) + , ToBytes(..) , TransparentAddress(..) , TransparentType(..) , UnifiedAddress(..) @@ -314,7 +317,7 @@ main = do describe "Seeds" $ do it "generate seed phrase" $ do s <- generateWalletSeedPhrase - BS.length s `shouldNotBe` 0 + BS.length (getBytes s) `shouldNotBe` 0 it "get seed from phrase" $ do s <- generateWalletSeedPhrase let x = getWalletSeed s @@ -468,11 +471,11 @@ main = do let msg = maybe "" a_memo decryptedNote2 msg `shouldBe` "Hello World!\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL" - describe "Wallet seed phrase" $ do + describe "Wallet seed phrase:" $ do prop "Generated phrases are valid" $ again prop_PhraseLength prop "Derived seeds are valid" $ again prop_SeedLength before getSeed $ - describe "Optimized spending key tests" $ do + describe "Optimized spending key tests:" $ do it "Transparent spending keys are valid" $ \s -> property $ prop_TransparentSpendingKey s it "Transparent receivers are valid" $ \s -> @@ -489,7 +492,7 @@ main = do property $ prop_OrchardReceiver s it "Orchard receivers are distinct" $ \s -> property $ prop_OrchardRecRepeated s - describe "Address tests" $ do + describe "Address tests:" $ do it "Encode transparent" $ do let ua = "u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x" @@ -500,10 +503,11 @@ main = do maybe "No transparent" (encodeTransparent (ua_net u)) $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" - it "Recover UA from YWallet" $ + it "Recover UA from YWallet:" $ ioProperty $ do let p = - "security expect junk hour people bind law hub between topic wink cliff spirit scissors auction idle figure option wide useful swift prison cushion round" + Phrase + "security expect junk hour people bind law hub between topic wink cliff spirit scissors auction idle figure option wide useful swift prison cushion round" let targetUA = isValidUnifiedAddress "u1qsylqauvnhw8tsfe3cldcsj3mjrfqzgaf3mt8yzlkjuvsf5wzj223yvrt8q66qukfqcc80x3z0mk6ym6pm2f0hukzkp6t4wj78h85t6kfr2u9mqsfhdd73g3sc7ezy2ut3rtq5jmejatwv4xqqd6l8tt9fycer8kdw0gz6e607nkssqsc7kd7nk2yfz2hpvpqhdg39wxalpjzhe34j7" @@ -514,15 +518,16 @@ main = do let oK = genOrchardSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0 let tK = genTransparentPrvKey s' 0 - let oR = genOrchardReceiver 0 =<< oK + let oR = genOrchardReceiver 0 External =<< oK let sR = genSaplingPaymentAddress 0 =<< sK tR <- genTransparentReceiver 0 =<< tK let newUA = UnifiedAddress MainNet oR sR $ Just tR return $ Just newUA `shouldBe` targetUA - it "Recover UA from Zingo" $ + it "Recover UA from Zingo:" $ ioProperty $ do let p = - "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest" + Phrase + "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest" let targetUA = isValidUnifiedAddress "u1trd8cvc6265ywwj4mmvuznsye5ghe2dhhn3zy8kcuyg4vx3svskw9r2dedp5hu6m740vylkqc34t4w9eqkl9fyu5uyzn3af72jg235440ke6tu5cf994eq85n97x69x9824hqejmwz3d8qqthtesrd6gerjupdymldhl9xccejjwfj0dhh9mt4rw4kytp325twlutsxd20rfqhzxu3m" @@ -533,7 +538,7 @@ main = do let oK = genOrchardSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0 let tK = genTransparentPrvKey s' 0 - let oR = genOrchardReceiver 0 =<< oK + let oR = genOrchardReceiver 0 External =<< oK let sR = genSaplingPaymentAddress 0 =<< sK tR <- genTransparentReceiver 0 =<< tK let newUA = UnifiedAddress MainNet oR sR $ Just tR @@ -544,23 +549,24 @@ prop_PhraseLength :: Property prop_PhraseLength = ioProperty $ do p <- generateWalletSeedPhrase - return $ BS.length p >= 95 + return $ BS.length (getBytes p) >= 95 prop_SeedLength :: Property prop_SeedLength = ioProperty $ do p <- generateWalletSeedPhrase let s = getWalletSeed p - return $ maybe 0 BS.length s === 64 + return $ maybe 0 (BS.length . getBytes) s === 64 prop_OrchardSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property prop_OrchardSpendingKey s c (NonNegative i) = genOrchardSpendingKey s c i =/= Nothing prop_OrchardReceiver :: - Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property -prop_OrchardReceiver s c (NonNegative i) (NonNegative j) = - genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= Nothing + Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property +prop_OrchardReceiver s c (NonNegative i) (NonNegative j) scope = + genOrchardReceiver j scope (fromMaybe "" $ genOrchardSpendingKey s c i) =/= + Nothing prop_SaplingSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property prop_SaplingSpendingKey s c (NonNegative i) = @@ -569,19 +575,25 @@ prop_SaplingSpendingKey s c (NonNegative i) = prop_SaplingReceiver :: Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property prop_SaplingReceiver s c (NonNegative i) (NonNegative j) = - genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s c j) =/= + genSaplingPaymentAddress + i + (fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c j) =/= Nothing prop_SaplingRecRepeated :: Seed -> CoinType -> NonNegative Int -> Property prop_SaplingRecRepeated s c (NonNegative i) = - genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s c 1) =/= - genSaplingPaymentAddress (i + 1) (fromMaybe "" $ genSaplingSpendingKey s c 1) + genSaplingPaymentAddress + i + (fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c 1) =/= + genSaplingPaymentAddress + (i + 1) + (fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c 1) prop_OrchardRecRepeated :: - Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property -prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) = - genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= - genOrchardReceiver (j + 1) (fromMaybe "" $ genOrchardSpendingKey s c i) + Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property +prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) scope = + genOrchardReceiver j scope (fromMaybe "" $ genOrchardSpendingKey s c i) =/= + genOrchardReceiver (j + 1) scope (fromMaybe "" $ genOrchardSpendingKey s c i) prop_TransparentSpendingKey :: Seed -> NonNegative Int -> Property prop_TransparentSpendingKey s (NonNegative i) = @@ -619,3 +631,6 @@ getSeed = do -- | Arbitrary instances instance Arbitrary CoinType where arbitrary = elements [MainNetCoin, TestNetCoin, RegTestNetCoin] + +instance Arbitrary Scope where + arbitrary = elements [External, Internal] From d2619123c0ea4476939dc6a14796c992d6be6e50 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 11:30:54 -0500 Subject: [PATCH 065/149] Improve type safety for Sapling --- src/ZcashHaskell/Orchard.hs | 4 ++-- src/ZcashHaskell/Sapling.hs | 13 ++++++++----- src/ZcashHaskell/Types.hs | 7 ++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 5dd000f..af68603 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -82,7 +82,7 @@ isValidUnifiedAddress str = then Just (raw_o x) else Nothing) (if BS.length (raw_s x) == 43 - then Just (raw_s x) + then Just (SaplingReceiver $ raw_s x) else Nothing) (if not (BS.null (raw_t x)) then Just $ TransparentAddress P2PKH (fromRawBytes $ raw_t x) @@ -106,7 +106,7 @@ encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b case ta_type t of P2SH -> packReceiver 0x01 $ Just $ toBytes $ ta_bytes t P2PKH -> packReceiver 0x00 $ Just $ toBytes $ ta_bytes t - sReceiver = packReceiver 0x02 $ s_rec ua + sReceiver = packReceiver 0x02 $ getBytes <$> s_rec ua oReceiver = packReceiver 0x03 $ o_rec ua padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 381ee51..d788195 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -41,10 +41,11 @@ import ZcashHaskell.Types , RawData(..) , RawTxResponse(..) , SaplingInternalReceiver - , SaplingReceiver + , SaplingReceiver(..) , SaplingSpendingKey(..) , Seed(..) , ShieldedOutput(..) + , ToBytes(..) , decodeHexText , getValue ) @@ -99,13 +100,13 @@ instance FromJSON RawTxResponse where genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey genSaplingSpendingKey seed c i = do if BS.length res == 169 - then Just res + then Just $ SaplingSpendingKey res else Nothing where res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey - seed + (getBytes seed) (fromIntegral $ getValue c) (fromIntegral i)) @@ -113,12 +114,14 @@ genSaplingSpendingKey seed c i = do genSaplingPaymentAddress :: Int -> SaplingSpendingKey -> Maybe SaplingReceiver genSaplingPaymentAddress i extspk = if BS.length res == 43 - then Just res + then Just $ SaplingReceiver res else Nothing where res = withPureBorshVarBuffer - (rustWrapperSaplingPaymentAddress extspk (fromIntegral (i * 111))) + (rustWrapperSaplingPaymentAddress + (getBytes extspk) + (fromIntegral (i * 111))) -- | Generate an internal Sapling address genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingInternalReceiver diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 40be29b..af12cb0 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -272,7 +272,12 @@ instance ToBytes SaplingSpendingKey where getBytes (SaplingSpendingKey s) = s -- | A Sapling receiver -type SaplingReceiver = BS.ByteString +newtype SaplingReceiver = + SaplingReceiver BS.ByteString + deriving stock (Eq, Prelude.Show, Read) + +instance ToBytes SaplingReceiver where + getBytes (SaplingReceiver s) = s -- | A Sapling internal receiver type SaplingInternalReceiver = BS.ByteString From 4cc4c379602f07b7ddd5a168c44aa18773c4c013 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 12:35:13 -0500 Subject: [PATCH 066/149] Improve type safety for Orchard --- src/ZcashHaskell/Orchard.hs | 15 +++++++++------ src/ZcashHaskell/Sapling.hs | 3 +-- src/ZcashHaskell/Types.hs | 17 ++++++++++++----- test/Spec.hs | 16 +++++++++++++--- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index af68603..3021234 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -41,7 +41,7 @@ genOrchardSpendingKey :: genOrchardSpendingKey s coinType accountId = if BS.length k /= 32 then Nothing - else Just k + else Just $ OrchardSpendingKey k where k = withPureBorshVarBuffer $ @@ -56,11 +56,14 @@ genOrchardReceiver :: genOrchardReceiver i scope osk = if BS.length k /= 43 then Nothing - else Just k + else Just $ OrchardReceiver k where k = withPureBorshVarBuffer $ - rustWrapperGenOrchardReceiver osk (fromIntegral i) (scope == External) + rustWrapperGenOrchardReceiver + (getBytes osk) + (fromIntegral i) + (scope == External) -- | Checks if given bytestring is a valid encoded unified address isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress @@ -79,10 +82,10 @@ isValidUnifiedAddress str = UnifiedAddress whichNet (if BS.length (raw_o x) == 43 - then Just (raw_o x) + then Just $ OrchardReceiver (raw_o x) else Nothing) (if BS.length (raw_s x) == 43 - then Just (SaplingReceiver $ raw_s x) + then Just $ SaplingReceiver (raw_s x) else Nothing) (if not (BS.null (raw_t x)) then Just $ TransparentAddress P2PKH (fromRawBytes $ raw_t x) @@ -107,7 +110,7 @@ encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b P2SH -> packReceiver 0x01 $ Just $ toBytes $ ta_bytes t P2PKH -> packReceiver 0x00 $ Just $ toBytes $ ta_bytes t sReceiver = packReceiver 0x02 $ getBytes <$> s_rec ua - oReceiver = packReceiver 0x03 $ o_rec ua + oReceiver = packReceiver 0x03 $ getBytes <$> o_rec ua padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString packReceiver typeCode receiver' = diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index d788195..d1904ad 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -40,7 +40,6 @@ import ZcashHaskell.Types , DecodedNote(..) , RawData(..) , RawTxResponse(..) - , SaplingInternalReceiver , SaplingReceiver(..) , SaplingSpendingKey(..) , Seed(..) @@ -124,5 +123,5 @@ genSaplingPaymentAddress i extspk = (fromIntegral (i * 111))) -- | Generate an internal Sapling address -genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingInternalReceiver +genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver genSaplingInternalAddress sk = undefined diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index af12cb0..7a16ff7 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -279,9 +279,6 @@ newtype SaplingReceiver = instance ToBytes SaplingReceiver where getBytes (SaplingReceiver s) = s --- | A Sapling internal receiver -type SaplingInternalReceiver = BS.ByteString - -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ShieldedOutput = ShieldedOutput { s_cv :: !HexString -- ^ Value commitment to the input note @@ -308,10 +305,20 @@ instance FromJSON ShieldedOutput where -- * Orchard -- | A spending key for Orchard -type OrchardSpendingKey = BS.ByteString +newtype OrchardSpendingKey = + OrchardSpendingKey BS.ByteString + deriving stock (Eq, Prelude.Show, Read) + +instance ToBytes OrchardSpendingKey where + getBytes (OrchardSpendingKey o) = o -- | An Orchard receiver -type OrchardReceiver = BS.ByteString +newtype OrchardReceiver = + OrchardReceiver BS.ByteString + deriving stock (Eq, Prelude.Show, Read) + +instance ToBytes OrchardReceiver where + getBytes (OrchardReceiver o) = o -- | Type to represent a Unified Address data UnifiedAddress = UnifiedAddress diff --git a/test/Spec.hs b/test/Spec.hs index ff3a2c3..23e67a8 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -57,6 +57,7 @@ import ZcashHaskell.Types , CoinType , DecodedNote(..) , OrchardAction(..) + , OrchardSpendingKey(..) , Phrase(..) , RawData(..) , RawTxResponse(..) @@ -565,7 +566,10 @@ prop_OrchardSpendingKey s c (NonNegative i) = prop_OrchardReceiver :: Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property prop_OrchardReceiver s c (NonNegative i) (NonNegative j) scope = - genOrchardReceiver j scope (fromMaybe "" $ genOrchardSpendingKey s c i) =/= + genOrchardReceiver + j + scope + (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) =/= Nothing prop_SaplingSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property @@ -592,8 +596,14 @@ prop_SaplingRecRepeated s c (NonNegative i) = prop_OrchardRecRepeated :: Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) scope = - genOrchardReceiver j scope (fromMaybe "" $ genOrchardSpendingKey s c i) =/= - genOrchardReceiver (j + 1) scope (fromMaybe "" $ genOrchardSpendingKey s c i) + genOrchardReceiver + j + scope + (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) =/= + genOrchardReceiver + (j + 1) + scope + (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) prop_TransparentSpendingKey :: Seed -> NonNegative Int -> Property prop_TransparentSpendingKey s (NonNegative i) = From 46a28128fdc8b2e9841f9ae33c5a1ec6581c3033 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 12:39:31 -0500 Subject: [PATCH 067/149] Bump version --- zcash-haskell.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 584e7ba..d7d33d9 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.4.4.2 +version: 0.5.0.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From d0ba74c7a1513743f91c6abd5a142000503ed32a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 13:02:02 -0500 Subject: [PATCH 068/149] Fix #34 --- src/ZcashHaskell/Types.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 7a16ff7..7d72f94 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -44,7 +44,7 @@ import Haskoin.Address (Address) -- | A seed for generating private keys newtype Seed = Seed C.ByteString - deriving stock (Prelude.Show, GHC.Generic) + deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Seed @@ -55,7 +55,7 @@ instance ToBytes Seed where -- | A mnemonic phrase used to derive seeds newtype Phrase = Phrase BS.ByteString - deriving stock (Prelude.Show, GHC.Generic) + deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Phrase From c93809489b99af2ce33b4b8d6bf671ff15dec2a3 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 14 Mar 2024 13:12:18 -0500 Subject: [PATCH 069/149] Add `Read` instance to `Phrase` --- src/ZcashHaskell/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 7d72f94..2e6cd5c 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -55,7 +55,7 @@ instance ToBytes Seed where -- | A mnemonic phrase used to derive seeds newtype Phrase = Phrase BS.ByteString - deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Phrase From 2c58d1260a98a2cb1624c4af36bfd41766a34376 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Thu, 14 Mar 2024 17:44:18 -0400 Subject: [PATCH 070/149] Transparent address fix --- src/ZcashHaskell/Sapling.hs | 2 +- src/ZcashHaskell/Transparent.hs | 24 +++++++++++++++++++----- test/Spec.hs | 20 ++++++++------------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index f93c635..3029b5b 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -124,7 +124,7 @@ genSaplingPaymentAddress i extspk = -- | Generate an internal Sapling address genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingInternalReceiver genSaplingInternalAddress sk = - if BS.length res <> 0 + if (BS.length res) > 0 then Just res else Nothing where diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 944948e..fed2ae7 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -28,15 +28,17 @@ import ZcashHaskell.Types , TransparentAddress(..) , TransparentType(..) , ZcashNet(..) + , CoinType(..) , getTransparentPrefix + , getValue ) import Crypto.Secp256k1 +import Haskoin.Crypto.Keys.Extended import Data.HexString import Data.Word import Haskoin.Address (Address(..)) import qualified Haskoin.Crypto.Hash as H -import Haskoin.Crypto.Keys.Extended encodeTransparent :: ZcashNet -> TransparentAddress -> T.Text encodeTransparent zNet t = @@ -53,11 +55,23 @@ encodeTransparent zNet t = checksum = sha256 $ sha256 digest -- | Attempts to generate an Extended Private Key from a known HDSeed. -genTransparentPrvKey :: Seed -> AccountId -> IO XPrvKey -genTransparentPrvKey hdseed i = do - let prvKey = makeXPrvKey hdseed +genTransparentPrvKey :: Seed -> CoinType -> AccountId -> IO XPrvKey +genTransparentPrvKey hdseed ctype accid = do + let coin = getValue ctype ioCtx <- createContext - return $ hardSubKey ioCtx prvKey (fromIntegral i) + let path = Deriv :| 44 :| 133 :| coin :/ 0 :/ 0 :: DerivPath + let prvKey = makeXPrvKey hdseed + return $ derivePath ioCtx path prvKey + +genTransparentPubKey :: XPrvKey -> IO XPubKey +genTransparentPubKey xPrvKey = do + ioCtx <- createContext + return $ deriveXPubKey ioCtx xPrvKey + +genTransparentPubAddress :: XPubKey -> IO Address +genTransparentPubAddress xPubKey = do + ioCtx <- createContext + return $ xPubAddr ioCtx xPubKey -- | Generate a transparent receiver genTransparentReceiver :: Int -> XPrvKey -> IO TransparentAddress diff --git a/test/Spec.hs b/test/Spec.hs index 24990da..06684a2 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -45,7 +45,6 @@ import ZcashHaskell.Sapling , genSaplingPaymentAddress , genSaplingInternalAddress , genSaplingSpendingKey - , genSaplingMasterSpendingKey , getShieldedOutputs , isValidSaplingViewingKey , isValidShieldedAddress @@ -502,7 +501,6 @@ main = do maybe "No transparent" (encodeTransparent (ua_net u)) $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" -<<<<<<< HEAD describe "Transparent Private and Public Key Generation" $ do it "Obtain a transparent extended private key from HDSeed" $ do let hdseed = @@ -571,7 +569,7 @@ main = do , 159 , 144 ] :: [Word8] - let xtpvk = genTransparentPrvKey (BS.pack hdseed) + xtpvk <- genTransparentPrvKey (BS.pack hdseed) MainNetCoin 0 let testpvk = XPrvKey 0 @@ -608,9 +606,9 @@ main = do it "Call genSaplingPaymentAddress" $ do p <- generateWalletSeedPhrase let s = getWalletSeed p - genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing - prop "Sapling receivers are valid" $ - forAll genSapArgs $ \i -> prop_SaplingReceiver i + genSaplingPaymentAddress 0 (fromMaybe "" s) `shouldNotBe` Nothing +-- prop "Sapling receivers are valid" $ +-- forAll genSapArgs $ \s -> prop_SaplingReceiver s describe "Sapling Change Payment Address generation test" $ do it "Call genSaplingInternalAddress" $ do let sk = [ 3 @@ -796,7 +794,6 @@ main = do let bscAdr = BS.pack cAdr let ca = genSaplingInternalAddress (BS.pack sk) (fromMaybe "" ca) `shouldBe` bscAdr -======= it "Recover UA from YWallet" $ ioProperty $ do let p = @@ -810,7 +807,7 @@ main = do Just s' -> do let oK = genOrchardSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0 - let tK = genTransparentPrvKey s' 0 + let tK = genTransparentPrvKey s' MainNetCoin 0 let oR = genOrchardReceiver 0 =<< oK let sR = genSaplingPaymentAddress 0 =<< sK tR <- genTransparentReceiver 0 =<< tK @@ -829,14 +826,13 @@ main = do Just s' -> do let oK = genOrchardSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0 - let tK = genTransparentPrvKey s' 0 + let tK = genTransparentPrvKey s' MainNetCoin 0 let oR = genOrchardReceiver 0 =<< oK let sR = genSaplingPaymentAddress 0 =<< sK tR <- genTransparentReceiver 0 =<< tK let newUA = UnifiedAddress MainNet oR sR $ Just tR return $ Just newUA `shouldBe` targetUA ->>>>>>> origin/dev040 -- | Properties prop_PhraseLength :: Property prop_PhraseLength = @@ -884,14 +880,14 @@ prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) = prop_TransparentSpendingKey :: Seed -> NonNegative Int -> Property prop_TransparentSpendingKey s (NonNegative i) = ioProperty $ do - k <- genTransparentPrvKey s i + k <- genTransparentPrvKey s MainNetCoin 0 return $ xPrvChild k == fromIntegral i prop_TransparentReceiver :: Seed -> NonNegative Int -> NonNegative Int -> Property prop_TransparentReceiver s (NonNegative i) (NonNegative j) = ioProperty $ do - k <- genTransparentPrvKey s i + k <- genTransparentPrvKey s MainCoinNet i r <- genTransparentReceiver j k return $ ta_type r == P2PKH From 0a98246855c13ba13605453a9c21283aecb56e64 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Thu, 14 Mar 2024 17:47:09 -0400 Subject: [PATCH 071/149] Transparent fix 2 --- src/ZcashHaskell/Transparent.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index fed2ae7..6c59edd 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -59,7 +59,7 @@ genTransparentPrvKey :: Seed -> CoinType -> AccountId -> IO XPrvKey genTransparentPrvKey hdseed ctype accid = do let coin = getValue ctype ioCtx <- createContext - let path = Deriv :| 44 :| 133 :| coin :/ 0 :/ 0 :: DerivPath + let path = Deriv :| 44 :| coin :| AccounId :/ 0 :/ 0 :: DerivPath let prvKey = makeXPrvKey hdseed return $ derivePath ioCtx path prvKey From c7c4d664dc2a9180805c35e5d08a61fc75d2c1ff Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 15 Mar 2024 08:15:08 -0500 Subject: [PATCH 072/149] Remove duplicate import --- test/Spec.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Spec.hs b/test/Spec.hs index 23e67a8..47fdda0 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -54,7 +54,6 @@ import ZcashHaskell.Types ( AccountId , BlockResponse(..) , CoinType(..) - , CoinType , DecodedNote(..) , OrchardAction(..) , OrchardSpendingKey(..) From d1b0436af2509d772d5127bd720df6072c023724 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 15 Mar 2024 10:26:06 -0500 Subject: [PATCH 073/149] Add documentation for new functions --- CHANGELOG.md | 1 + src/ZcashHaskell/Orchard.hs | 10 ++++++++-- src/ZcashHaskell/Transparent.hs | 28 +++++++++++++++------------- src/ZcashHaskell/Types.hs | 4 ++-- zcash-haskell.cabal | 2 +- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe73f0a..31c81c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Types for Spending Keys and Receivers for Sapling and Orchard - Function to generate an Orchard receiver - Function to generate a Sapling receiver +- Function to generate a Transparent receiver ### Changed diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 3021234..a810114 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -37,7 +37,10 @@ import ZcashHaskell.Utils (encodeBech32m, f4Jumble) -- | Derives an Orchard spending key for the given seed and account ID genOrchardSpendingKey :: - Seed -> CoinType -> AccountId -> Maybe OrchardSpendingKey + Seed -- ^ The cryptographic seed for the wallet + -> CoinType -- ^ The coin type constant + -> AccountId -- ^ The index of the account to be used + -> Maybe OrchardSpendingKey genOrchardSpendingKey s coinType accountId = if BS.length k /= 32 then Nothing @@ -52,7 +55,10 @@ genOrchardSpendingKey s coinType accountId = -- | Derives an Orchard receiver for the given spending key and index genOrchardReceiver :: - Int -> Scope -> OrchardSpendingKey -> Maybe OrchardReceiver + Int -- ^ The index of the address to be created + -> Scope -- ^ `External` for wallet addresses, `Internal` for change addresses + -> OrchardSpendingKey -- ^ The spending key + -> Maybe OrchardReceiver genOrchardReceiver i scope osk = if BS.length k /= 43 then Nothing diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 52bc469..690fa4c 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -41,7 +41,11 @@ import ZcashHaskell.Types , getValue ) -encodeTransparent :: ZcashNet -> TransparentAddress -> T.Text +-- | Encodes a `TransparentAddress` into the human-readable format per the Zcash Protocol section 5.6.1.1 +encodeTransparent :: + ZcashNet -- ^ The network, `MainNet` or `TestNet` + -> TransparentAddress -- ^ The address to encode + -> T.Text encodeTransparent zNet t = encodeTransparent' (getTransparentPrefix zNet (ta_type t)) $ toBytes $ ta_bytes t @@ -56,7 +60,11 @@ encodeTransparent zNet t = checksum = sha256 $ sha256 digest -- | Generate an Extended Private Key from a known HDSeed. -genTransparentPrvKey :: Seed -> CoinType -> AccountId -> IO XPrvKey +genTransparentPrvKey :: + Seed -- ^ The cryptographic seed of the wallet + -> CoinType -- ^ The coin type constant to be used + -> AccountId -- ^ The index of the account to be used + -> IO XPrvKey genTransparentPrvKey hdseed ctype accid = do let coin = getValue ctype ioCtx <- createContext @@ -64,18 +72,12 @@ genTransparentPrvKey hdseed ctype accid = do let prvKey = makeXPrvKey $ getBytes hdseed return $ derivePath ioCtx path prvKey -genTransparentPubKey :: XPrvKey -> IO XPubKey -genTransparentPubKey xPrvKey = do - ioCtx <- createContext - return $ deriveXPubKey ioCtx xPrvKey - -genTransparentPubAddress :: XPubKey -> IO Address -genTransparentPubAddress xPubKey = do - ioCtx <- createContext - return $ xPubAddr ioCtx xPubKey - -- | Generate a transparent receiver -genTransparentReceiver :: Int -> Scope -> XPrvKey -> IO TransparentAddress +genTransparentReceiver :: + Int -- ^ The index of the address to be created + -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses + -> XPrvKey -- ^ The transparent private key + -> IO TransparentAddress genTransparentReceiver i scope xprvk = do ioCtx <- createContext let s = diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 2e6cd5c..12a27a2 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -65,8 +65,8 @@ instance ToBytes Phrase where -- | Scope for addresses/receivers data Scope - = External - | Internal + = External -- ^ Addresses used publically to receive payments + | Internal -- ^ Addresses used internally by wallets for change and shielding deriving (Eq, Prelude.Show, Read) -- | Type to represent data after Bech32 decoding diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index d7d33d9..646d810 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.0.0 +version: 0.5.0.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From bc1ee2430a5a0d2dad6811b68fbf34b10b815dfe Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 15 Mar 2024 12:26:28 -0500 Subject: [PATCH 074/149] Improve dependency on Haskoin for downstream --- src/ZcashHaskell/Transparent.hs | 3 ++- src/ZcashHaskell/Types.hs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 690fa4c..140bdbb 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -35,6 +35,7 @@ import ZcashHaskell.Types , Seed(..) , ToBytes(..) , TransparentAddress(..) + , TransparentSpendingKey(..) , TransparentType(..) , ZcashNet(..) , getTransparentPrefix @@ -64,7 +65,7 @@ genTransparentPrvKey :: Seed -- ^ The cryptographic seed of the wallet -> CoinType -- ^ The coin type constant to be used -> AccountId -- ^ The index of the account to be used - -> IO XPrvKey + -> IO TransparentSpendingKey genTransparentPrvKey hdseed ctype accid = do let coin = getValue ctype ioCtx <- createContext diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 12a27a2..091d661 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -38,6 +38,7 @@ import Data.Word import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP import Haskoin.Address (Address) +import Haskoin.Crypto.Keys.Extended (XPrvKey) -- * General -- @@ -256,6 +257,9 @@ data TransparentType | P2PKH deriving (Eq, Prelude.Show, Read) +-- | Type for transparent spending key +type TransparentSpendingKey = XPrvKey + -- | Type to represent a transparent Zcash addresses data TransparentAddress = TransparentAddress { ta_type :: !TransparentType From e7050f03c0423a3f0682aa2d26bbb06683630d67 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 20 Mar 2024 11:13:02 -0500 Subject: [PATCH 075/149] Upgrade Zebra call --- CHANGELOG.md | 7 ++++++- src/ZcashHaskell/Sapling.hs | 2 +- src/ZcashHaskell/Types.hs | 6 +++--- src/ZcashHaskell/Utils.hs | 20 +++++++++++++++++--- zcash-haskell.cabal | 3 ++- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c81c2..ad3ee7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.1.0] -## [Unreleased] +### Changed + +- Modified the `makeZebraCall` function to handle errors explicitly + +## [0.5.0.1] ### Added diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index c744516..fb1c459 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -126,7 +126,7 @@ genSaplingPaymentAddress i extspk = -- | Generate an internal Sapling address genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver genSaplingInternalAddress sk = - if BS.length res > 0 + if BS.length res == 43 then Just $ SaplingReceiver res else Nothing where diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 091d661..32f4e57 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -44,7 +44,7 @@ import Haskoin.Crypto.Keys.Extended (XPrvKey) -- -- | A seed for generating private keys newtype Seed = - Seed C.ByteString + Seed BS.ByteString deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -55,7 +55,7 @@ instance ToBytes Seed where -- | A mnemonic phrase used to derive seeds newtype Phrase = - Phrase BS.ByteString + Phrase C.ByteString deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -191,7 +191,7 @@ data BlockResponse = BlockResponse { bl_confirmations :: !Integer -- ^ Block confirmations , bl_height :: !Integer -- ^ Block height , bl_time :: !Integer -- ^ Block time - , bl_txs :: ![T.Text] -- ^ List of transaction IDs in the block + , bl_txs :: ![HexString] -- ^ List of transaction IDs in the block } deriving (Prelude.Show, Eq) instance FromJSON BlockResponse where diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index f6f1ceb..702e453 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -23,12 +23,14 @@ import C.Zcash , rustWrapperF4Jumble , rustWrapperF4UnJumble ) +import Control.Exception (try) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable +import Network.HTTP.Client (HttpException(..)) import Network.HTTP.Simple import ZcashHaskell.Types @@ -74,12 +76,12 @@ makeZcashCall username password m p = do -- | Make a Zebra RPC call makeZebraCall :: - (MonadIO m, FromJSON a) + FromJSON a => T.Text -- ^ Hostname for `zebrad` -> Int -- ^ Port for `zebrad` -> T.Text -- ^ RPC method to call -> [Data.Aeson.Value] -- ^ List of parameters - -> m (Response a) + -> IO (Either String a) makeZebraCall host port m params = do let payload = RpcCall "2.0" "zh" m params let myRequest = @@ -87,4 +89,16 @@ makeZebraCall host port m params = do setRequestPort port $ setRequestHost (E.encodeUtf8 host) $ setRequestMethod "POST" defaultRequest - httpJSON myRequest + r <- + try $ httpJSON myRequest :: FromJSON a1 => + IO (Either HttpException (Response (RpcResponse a1))) + case r of + Left ex -> return $ Left $ show ex + Right res -> do + let zebraResp = getResponseBody res + case err zebraResp of + Just zErr -> return $ Left $ T.unpack $ emessage zErr + Nothing -> + case result zebraResp of + Nothing -> return $ Left "Empty response from Zebra" + Just zR -> return $ Right zR diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 646d810..05ac71b 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.0.1 +version: 0.5.1.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -53,6 +53,7 @@ library , generics-sop , hexstring >=0.12.1 , http-conduit + , http-client , memory , text , haskoin-core From 1af152dc310dce0952e8e3aecbcc3abc2180a6cf Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 20 Mar 2024 16:15:30 +0000 Subject: [PATCH 076/149] Upgrade Zebra call (#39) Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/39 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- CHANGELOG.md | 7 ++++++- src/ZcashHaskell/Sapling.hs | 2 +- src/ZcashHaskell/Types.hs | 6 +++--- src/ZcashHaskell/Utils.hs | 20 +++++++++++++++++--- zcash-haskell.cabal | 3 ++- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c81c2..ad3ee7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.1.0] -## [Unreleased] +### Changed + +- Modified the `makeZebraCall` function to handle errors explicitly + +## [0.5.0.1] ### Added diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index c744516..fb1c459 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -126,7 +126,7 @@ genSaplingPaymentAddress i extspk = -- | Generate an internal Sapling address genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver genSaplingInternalAddress sk = - if BS.length res > 0 + if BS.length res == 43 then Just $ SaplingReceiver res else Nothing where diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 091d661..32f4e57 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -44,7 +44,7 @@ import Haskoin.Crypto.Keys.Extended (XPrvKey) -- -- | A seed for generating private keys newtype Seed = - Seed C.ByteString + Seed BS.ByteString deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -55,7 +55,7 @@ instance ToBytes Seed where -- | A mnemonic phrase used to derive seeds newtype Phrase = - Phrase BS.ByteString + Phrase C.ByteString deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -191,7 +191,7 @@ data BlockResponse = BlockResponse { bl_confirmations :: !Integer -- ^ Block confirmations , bl_height :: !Integer -- ^ Block height , bl_time :: !Integer -- ^ Block time - , bl_txs :: ![T.Text] -- ^ List of transaction IDs in the block + , bl_txs :: ![HexString] -- ^ List of transaction IDs in the block } deriving (Prelude.Show, Eq) instance FromJSON BlockResponse where diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index f6f1ceb..702e453 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -23,12 +23,14 @@ import C.Zcash , rustWrapperF4Jumble , rustWrapperF4UnJumble ) +import Control.Exception (try) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable +import Network.HTTP.Client (HttpException(..)) import Network.HTTP.Simple import ZcashHaskell.Types @@ -74,12 +76,12 @@ makeZcashCall username password m p = do -- | Make a Zebra RPC call makeZebraCall :: - (MonadIO m, FromJSON a) + FromJSON a => T.Text -- ^ Hostname for `zebrad` -> Int -- ^ Port for `zebrad` -> T.Text -- ^ RPC method to call -> [Data.Aeson.Value] -- ^ List of parameters - -> m (Response a) + -> IO (Either String a) makeZebraCall host port m params = do let payload = RpcCall "2.0" "zh" m params let myRequest = @@ -87,4 +89,16 @@ makeZebraCall host port m params = do setRequestPort port $ setRequestHost (E.encodeUtf8 host) $ setRequestMethod "POST" defaultRequest - httpJSON myRequest + r <- + try $ httpJSON myRequest :: FromJSON a1 => + IO (Either HttpException (Response (RpcResponse a1))) + case r of + Left ex -> return $ Left $ show ex + Right res -> do + let zebraResp = getResponseBody res + case err zebraResp of + Just zErr -> return $ Left $ T.unpack $ emessage zErr + Nothing -> + case result zebraResp of + Nothing -> return $ Left "Empty response from Zebra" + Just zR -> return $ Right zR diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 646d810..05ac71b 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.0.1 +version: 0.5.1.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -53,6 +53,7 @@ library , generics-sop , hexstring >=0.12.1 , http-conduit + , http-client , memory , text , haskoin-core From 517b736c9a67a9f54546cce04f589b0fdfa9c8d0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 20 Mar 2024 14:16:12 -0500 Subject: [PATCH 077/149] Allow for missing `result` in RPC response --- CHANGELOG.md | 1 + src/ZcashHaskell/Types.hs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad3ee7b..fef5e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Modified the `makeZebraCall` function to handle errors explicitly +- Modified the RPC response to handle missing `result` field ## [0.5.0.1] diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 32f4e57..182ddc1 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -168,7 +168,7 @@ instance (FromJSON r) => FromJSON (RpcResponse r) where withObject "RpcResponse" $ \obj -> do e <- obj .:? "error" i <- obj .: "id" - r <- obj .: "result" + r <- obj .:? "result" pure $ MakeRpcResponse e i r -- | A type to model the errors from the Zcash RPC From 3af235377b2a0f3bd83f4523c5b25fe62001be62 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 20 Mar 2024 19:33:36 +0000 Subject: [PATCH 078/149] Adapt response handling to Zebra (#40) This PR allows for the Zebra behavior of not having a `result` field in the RPC response on error conditions. Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/40 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- CHANGELOG.md | 1 + src/ZcashHaskell/Types.hs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad3ee7b..fef5e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Modified the `makeZebraCall` function to handle errors explicitly +- Modified the RPC response to handle missing `result` field ## [0.5.0.1] diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 32f4e57..182ddc1 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -168,7 +168,7 @@ instance (FromJSON r) => FromJSON (RpcResponse r) where withObject "RpcResponse" $ \obj -> do e <- obj .:? "error" i <- obj .: "id" - r <- obj .: "result" + r <- obj .:? "result" pure $ MakeRpcResponse e i r -- | A type to model the errors from the Zcash RPC From 69bce58345bfb9b0bf2a30d1cae0b834a769d66f Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 21 Mar 2024 12:52:45 -0500 Subject: [PATCH 079/149] Improve exception handling of Zebra calls --- src/ZcashHaskell/Utils.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 702e453..39296e9 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -23,7 +23,7 @@ import C.Zcash , rustWrapperF4Jumble , rustWrapperF4UnJumble ) -import Control.Exception (try) +import Control.Exception (SomeException(..), try) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS @@ -91,7 +91,7 @@ makeZebraCall host port m params = do setRequestMethod "POST" defaultRequest r <- try $ httpJSON myRequest :: FromJSON a1 => - IO (Either HttpException (Response (RpcResponse a1))) + IO (Either SomeException (Response (RpcResponse a1))) case r of Left ex -> return $ Left $ show ex Right res -> do From d7752e98667f930095d65536dff20c08fc91abaa Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 21 Mar 2024 18:26:13 +0000 Subject: [PATCH 080/149] Improve response handling for Zebra RPC (#41) This PR includes changes to handle differences between the `zebrad` and `zcashd` RPC. Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/41 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- src/ZcashHaskell/Utils.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 702e453..39296e9 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -23,7 +23,7 @@ import C.Zcash , rustWrapperF4Jumble , rustWrapperF4UnJumble ) -import Control.Exception (try) +import Control.Exception (SomeException(..), try) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS @@ -91,7 +91,7 @@ makeZebraCall host port m params = do setRequestMethod "POST" defaultRequest r <- try $ httpJSON myRequest :: FromJSON a1 => - IO (Either HttpException (Response (RpcResponse a1))) + IO (Either SomeException (Response (RpcResponse a1))) case r of Left ex -> return $ Left $ show ex Right res -> do From 9c8a851eadb860ee71092c5af3c77945109bf58f Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 21 Mar 2024 15:12:22 -0500 Subject: [PATCH 081/149] Implement Sapling spends --- CHANGELOG.md | 4 ++++ src/ZcashHaskell/Sapling.hs | 6 ++++-- src/ZcashHaskell/Types.hs | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fef5e74..999e019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.5.1.0] +### Added + +- Functionality to capture Sapling Spends + ### Changed - Modified the `makeZebraCall` function to handle errors explicitly diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index fb1c459..2dc7f49 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -90,11 +90,13 @@ instance FromJSON RawTxResponse where ht <- obj .: "height" c <- obj .: "confirmations" b <- obj .: "blocktime" + sSpend <- obj .: "vShieldedSpend" case o of - Nothing -> pure $ RawTxResponse i h (getShieldedOutputs h) [] ht c b + Nothing -> + pure $ RawTxResponse i h sSpend (getShieldedOutputs h) [] ht c b Just o' -> do a <- o' .: "actions" - pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b + pure $ RawTxResponse i h sSpend (getShieldedOutputs h) a ht c b -- | Attempts to obtain a sapling SpendingKey using a HDSeed genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 182ddc1..e7f68b3 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -207,6 +207,7 @@ instance FromJSON BlockResponse where data RawTxResponse = RawTxResponse { rt_id :: !HexString , rt_hex :: !HexString + , rt_shieldedSpends :: ![ShieldedSpend] , rt_shieldedOutputs :: ![BS.ByteString] , rt_orchardActions :: ![OrchardAction] , rt_blockheight :: !Integer @@ -283,6 +284,30 @@ newtype SaplingReceiver = instance ToBytes SaplingReceiver where getBytes (SaplingReceiver s) = s +-- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method +data ShieldedSpend = ShieldedSpend + { sp_cv :: !HexString + , sp_anchor :: !HexString + , sp_nullifier :: !HexString + , sp_rk :: !HexString + , sp_proof :: !HexString + , sp_auth :: !HexString + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedSpend + +instance FromJSON ShieldedSpend where + parseJSON = + withObject "ShieldedSpend" $ \obj -> do + cv <- obj .: "cv" + anchor <- obj .: "anchor" + nullifier <- obj .: "nullifier" + rk <- obj .: "rk" + p <- obj .: "proof" + sig <- obj .: "spendAuthSig" + pure $ ShieldedSpend cv anchor nullifier rk p sig + -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ShieldedOutput = ShieldedOutput { s_cv :: !HexString -- ^ Value commitment to the input note From aa10e09595549ad3dd07a08ba459cac5a59350ae Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 21 Mar 2024 20:15:49 +0000 Subject: [PATCH 082/149] Implement Sapling spends (#42) This PR add support for Sapling spends in the communication with Zebra. Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/42 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- CHANGELOG.md | 4 ++++ src/ZcashHaskell/Sapling.hs | 6 ++++-- src/ZcashHaskell/Types.hs | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fef5e74..999e019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.5.1.0] +### Added + +- Functionality to capture Sapling Spends + ### Changed - Modified the `makeZebraCall` function to handle errors explicitly diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index fb1c459..2dc7f49 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -90,11 +90,13 @@ instance FromJSON RawTxResponse where ht <- obj .: "height" c <- obj .: "confirmations" b <- obj .: "blocktime" + sSpend <- obj .: "vShieldedSpend" case o of - Nothing -> pure $ RawTxResponse i h (getShieldedOutputs h) [] ht c b + Nothing -> + pure $ RawTxResponse i h sSpend (getShieldedOutputs h) [] ht c b Just o' -> do a <- o' .: "actions" - pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b + pure $ RawTxResponse i h sSpend (getShieldedOutputs h) a ht c b -- | Attempts to obtain a sapling SpendingKey using a HDSeed genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 182ddc1..e7f68b3 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -207,6 +207,7 @@ instance FromJSON BlockResponse where data RawTxResponse = RawTxResponse { rt_id :: !HexString , rt_hex :: !HexString + , rt_shieldedSpends :: ![ShieldedSpend] , rt_shieldedOutputs :: ![BS.ByteString] , rt_orchardActions :: ![OrchardAction] , rt_blockheight :: !Integer @@ -283,6 +284,30 @@ newtype SaplingReceiver = instance ToBytes SaplingReceiver where getBytes (SaplingReceiver s) = s +-- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method +data ShieldedSpend = ShieldedSpend + { sp_cv :: !HexString + , sp_anchor :: !HexString + , sp_nullifier :: !HexString + , sp_rk :: !HexString + , sp_proof :: !HexString + , sp_auth :: !HexString + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedSpend + +instance FromJSON ShieldedSpend where + parseJSON = + withObject "ShieldedSpend" $ \obj -> do + cv <- obj .: "cv" + anchor <- obj .: "anchor" + nullifier <- obj .: "nullifier" + rk <- obj .: "rk" + p <- obj .: "proof" + sig <- obj .: "spendAuthSig" + pure $ ShieldedSpend cv anchor nullifier rk p sig + -- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ShieldedOutput = ShieldedOutput { s_cv :: !HexString -- ^ Value commitment to the input note From 5b6ce3f29b718cb7ff63f768ab67201cfda48677 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 21 Mar 2024 19:27:09 -0500 Subject: [PATCH 083/149] Account for missing block time field --- src/ZcashHaskell/Types.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index e7f68b3..612410e 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -7,7 +7,7 @@ -- Copyright : 2022-2024 Vergara Technologies -- License : MIT -- --- Maintainer : pitmut@vergara.tech +-- Maintainer : pitmutt@vergara.tech -- Stability : experimental -- Portability : unknown -- @@ -31,6 +31,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C import Data.HexString import Data.Int +import Data.Maybe (fromMaybe) import Data.Structured import qualified Data.Text as T import qualified Data.Text.Encoding as E @@ -199,9 +200,9 @@ instance FromJSON BlockResponse where withObject "BlockResponse" $ \obj -> do c <- obj .: "confirmations" h <- obj .: "height" - t <- obj .: "time" + t <- obj .:? "time" txs <- obj .: "tx" - pure $ BlockResponse c h t txs + pure $ BlockResponse c h (fromMaybe 0 t) txs -- | Type to represent response from the `zcashd` RPC `getrawtransaction` data RawTxResponse = RawTxResponse From f0995441628381fee14ae1c655c3c4f8d96162e5 Mon Sep 17 00:00:00 2001 From: pitmutt Date: Fri, 22 Mar 2024 18:04:04 +0000 Subject: [PATCH 084/149] Account for missing response fields in Zebra response (#43) This PR adds code to account for the missing `time` field in the Zebra response for blocks and raw transactions. Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/43 Co-authored-by: pitmutt Co-committed-by: pitmutt --- src/ZcashHaskell/Types.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index e7f68b3..612410e 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -7,7 +7,7 @@ -- Copyright : 2022-2024 Vergara Technologies -- License : MIT -- --- Maintainer : pitmut@vergara.tech +-- Maintainer : pitmutt@vergara.tech -- Stability : experimental -- Portability : unknown -- @@ -31,6 +31,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C import Data.HexString import Data.Int +import Data.Maybe (fromMaybe) import Data.Structured import qualified Data.Text as T import qualified Data.Text.Encoding as E @@ -199,9 +200,9 @@ instance FromJSON BlockResponse where withObject "BlockResponse" $ \obj -> do c <- obj .: "confirmations" h <- obj .: "height" - t <- obj .: "time" + t <- obj .:? "time" txs <- obj .: "tx" - pure $ BlockResponse c h t txs + pure $ BlockResponse c h (fromMaybe 0 t) txs -- | Type to represent response from the `zcashd` RPC `getrawtransaction` data RawTxResponse = RawTxResponse From b0df0480c5f5e71ba30d23927cae8f6a06b976f9 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 26 Mar 2024 09:50:49 -0500 Subject: [PATCH 085/149] Implement Transaction hex deserialization --- librustzcash-wrapper/src/lib.rs | 112 +++++++++++++++++++++++++++++++- src/C/Zcash.chs | 7 ++ src/ZcashHaskell/Types.hs | 101 +++++++++++++++++++++++----- src/ZcashHaskell/Utils.hs | 19 ++++-- test/Spec.hs | 7 ++ zebrablock.json | 20 ++++++ zebrahexblock.json | 4 ++ zebratx.json | 8 +++ 8 files changed, 253 insertions(+), 25 deletions(-) create mode 100644 zebrablock.json create mode 100644 zebrahexblock.json create mode 100644 zebratx.json diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 2451d27..d6ee532 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -31,9 +31,17 @@ use zcash_primitives::{ sapling::DiversifierKey }, zip339::{Count, Mnemonic}, - transaction::components::sapling::{ - GrothProofBytes, - OutputDescription + transaction::components::{ + transparent::{ + Bundle as TransparentBundle, + TxIn, + TxOut, + Authorized + }, + sapling::{ + GrothProofBytes, + OutputDescription + } }, sapling::{ PaymentAddress, @@ -220,6 +228,81 @@ impl Hua { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Htx { + txid: Vec, + locktime: u32, + expiry: u32, + t_bundle: HTBundle +} + +impl ToHaskell for Htx { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HTBundle { + vin: Vec, + vout: Vec, + coinbase: bool +} + +impl ToHaskell for HTBundle { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +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()} + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HTxIn { + outpoint: u32, + script: Vec, + sequence: u32 +} + +impl ToHaskell for HTxIn { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl HTxIn { + pub fn pack(t: &TxIn) -> HTxIn { + return HTxIn { outpoint: t.prevout.n(), script: t.script_sig.0.clone(), sequence: t.sequence} + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HTxOut { + amt: i64, + script: Vec +} + +impl ToHaskell for HTxOut { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl HTxOut { + pub fn pack(t: &TxOut) -> HTxOut { + return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() } + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, @@ -555,6 +638,29 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt( } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_tx_read( + tx: *const u8, + tx_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW); + let mut tx_reader = Cursor::new(tx_input); + 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); + }, + 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} }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_tx_parse( tx: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 19df3c3..7bf023f 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -123,6 +123,13 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_tx_read as rustWrapperTxRead + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer RawZebraTx'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase { getVarBuffer `Buffer Phrase'& } -> `()' #} diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 612410e..d22a8a7 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -16,6 +16,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GeneralisedNewtypeDeriving #-} +{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE UndecidableInstances #-} @@ -40,6 +41,7 @@ import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP import Haskoin.Address (Address) import Haskoin.Crypto.Keys.Extended (XPrvKey) +import qualified Haskoin.Transaction.Common as H -- * General -- @@ -89,6 +91,19 @@ data ZcashNet type AccountId = Int +-- | Function to get the Base58 prefix for encoding a 'TransparentAddress' +getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) +getTransparentPrefix n t = + case t of + P2SH -> + case n of + MainNet -> (0x1c, 0xbd) + _ -> (0x1c, 0xba) + P2PKH -> + case n of + MainNet -> (0x1c, 0xb8) + _ -> (0x1d, 0x25) + -- ** Constants -- | Type for coin types on the different networks data CoinType @@ -104,7 +119,23 @@ getValue c = TestNetCoin -> 1 RegTestNetCoin -> 1 --- | Constants for Sapling Human-readable part +-- | A Zcash transaction +data Transaction = Transaction + { tx_id :: !HexString + , tx_height :: !Int + , tx_conf :: !Int + , tx_expiry :: !Int + , tx_transpBundle :: !(Maybe TransparentBundle) + } deriving (Prelude.Show, Eq, Read) + +-- | The transparent portion of a Zcash transaction +data TransparentBundle = TransparentBundle + { tb_vin :: ![H.TxIn] + , tb_vout :: ![H.TxOut] + , tb_coinbase :: !Bool + } deriving (Eq, Prelude.Show, Read) + +-- *** Constants for Sapling Human-readable part sapExtSpendingKeyHrp = "secret-extended-key-main" :: String sapExtFullViewingKeyHrp = "zxviews" :: String @@ -117,7 +148,7 @@ sapTestExtFullViewingKeyHrp = "zxviewtestsapling" :: String sapTestPaymentAddressHrp = "ztestsapling" :: String --- | Constants for Unified Human-readable part +-- *** Constants for Unified Human-readable part uniPaymentAddressHrp = "u" :: T.Text uniFullViewingKeyHrp = "uview" :: T.Text @@ -130,19 +161,6 @@ uniTestFullViewingKeyHrp = "uviewtest" :: T.Text uniTestIncomingViewingKeyHrp = "uivktest" :: T.Text --- | Function to get the Base58 prefix for encoding a 'TransparentAddress' -getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) -getTransparentPrefix n t = - case t of - P2SH -> - case n of - MainNet -> (0x1c, 0xbd) - _ -> (0x1c, 0xba) - P2PKH -> - case n of - MainNet -> (0x1c, 0xb8) - _ -> (0x1d, 0x25) - -- * RPC -- | A type to model Zcash RPC calls data RpcCall = RpcCall @@ -217,6 +235,41 @@ data RawTxResponse = RawTxResponse } deriving (Prelude.Show, Eq, Read) -- ** `zebrad` +data ZebraTxResponse = ZebraTxResponse + { ztr_blockheight :: !Int + , ztr_conf :: !Int + , ztr_hex :: !HexString + } deriving (Prelude.Show, Eq, Read) + +instance FromJSON ZebraTxResponse where + parseJSON = + withObject "ZebraTxResponse" $ \obj -> do + hex <- obj .: "hex" + height <- obj .: "height" + c <- obj .: "confirmations" + pure $ ZebraTxResponse height c hex + +-- | Type to represent a raw deserialized Zebra transaction +data RawZebraTx = RawZebraTx + { zt_id :: !HexString + , zt_locktime :: !Word32 + , zt_expiry :: !Word32 + , zt_tBundle :: !RawTBundle + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawZebraTx + +-- | Type for a raw deserialized Zebra transparent bundle +data RawTBundle = RawTBundle + { ztb_vin :: ![RawTxIn] + , ztb_vout :: ![RawTxOut] + , ztb_coinbase :: !Bool + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle + -- | Type for the response from the `zebrad` RPC method `getinfo` data ZebraGetInfo = ZebraGetInfo { zgi_build :: !T.Text @@ -268,6 +321,24 @@ data TransparentAddress = TransparentAddress , ta_bytes :: !HexString } deriving (Eq, Prelude.Show, Read) +-- | Wrapper types for transparent elements +data RawTxIn = RawTxIn + { rti_outpoint :: !Word32 + , rti_script :: !BS.ByteString + , rti_seq :: !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 RawTxIn + +data RawTxOut = RawTxOut + { rto_amt :: !Word64 + , rto_script :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxOut + -- * Sapling -- | A spending key for Sapling newtype SaplingSpendingKey = diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 39296e9..1407c79 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -22,11 +22,13 @@ import C.Zcash , rustWrapperBech32Encode , rustWrapperF4Jumble , rustWrapperF4UnJumble + , rustWrapperTxRead ) import Control.Exception (SomeException(..), try) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS +import Data.HexString (HexString(..)) import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable @@ -34,13 +36,7 @@ import Network.HTTP.Client (HttpException(..)) import Network.HTTP.Simple import ZcashHaskell.Types -import Foreign.C.Types -import Foreign.Marshal.Array (allocaArray, peekArray) -import Foreign.Ptr (Ptr) - -import Data.Word - --- | +-- * Utility functions -- | Decode the given bytestring using Bech32 decodeBech32 :: BS.ByteString -> RawData decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode @@ -57,6 +53,7 @@ f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble f4UnJumble :: BS.ByteString -> BS.ByteString f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble +-- * Node interaction -- | Make a Zcash RPC call makeZcashCall :: (MonadIO m, FromJSON a) @@ -102,3 +99,11 @@ makeZebraCall host port m params = do case result zebraResp of Nothing -> return $ Left "Empty response from Zebra" Just zR -> return $ Right zR + +readZebraTransaction :: HexString -> Maybe RawZebraTx +readZebraTransaction hex = + if BS.length (hexBytes $ zt_id rawTx) < 1 + then Nothing + else Just rawTx + where + rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex diff --git a/test/Spec.hs b/test/Spec.hs index 97c4a0c..f81c71a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -71,6 +71,7 @@ import ZcashHaskell.Types , UnifiedAddress(..) , UnifiedFullViewingKey(..) , ZcashNet(..) + , ZebraTxResponse(..) , decodeHexText , getValue ) @@ -765,6 +766,12 @@ main = do let bscAdr = SaplingReceiver $ BS.pack cAdr let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk) fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr + describe "Zebra response processing: " $ do + it "Raw transaction" $ do + let h = + hexString + "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" + readZebraTransaction h `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property diff --git a/zebrablock.json b/zebrablock.json new file mode 100644 index 0000000..5665f92 --- /dev/null +++ b/zebrablock.json @@ -0,0 +1,20 @@ +{ + "result": { + "hash": "0041ee9cb0e256a73c92bb72d830143c402ea350152f56f19f74d23cf51418fb", + "confirmations": 3583, + "height": 2767099, + "tx": [ + "d169ec3eda57dc750edfc1aa6b8ffb4ed2065780bfd5964de34b529503ec372f", + "987fcdb9bd37cbb5b205a8336de60d043f7028bebaa372828d81f3da296c7ef9" + ], + "trees": { + "sapling": { + "size": 129349 + }, + "orchard": { + "size": 39382 + } + } + }, + "id": 123 +} diff --git a/zebrahexblock.json b/zebrahexblock.json new file mode 100644 index 0000000..19d6b90 --- /dev/null +++ b/zebrahexblock.json @@ -0,0 +1,4 @@ +{ + "result": "040000006deacb36066fbbf99f00436c5a384e885c0cecff393b922ccfb22ed81b9e3600722039ae03cfdbee344a18774168795a936a720db144408a9abd92c55eb4a276f1b96e93c15bc2943a1f2483e466c456d2ea22ad82eea3d5788dbb5448bcb30c33d0fd658002451f6c005c8fa5b5d0cb7a24d326de3fd34422e22718a0c5e23972800292ed830000fd4005022b23ffe50b150fe854961e44e5d752975857fbc5026bc06a7550448e858e71dc7d927b44a04a325df027973f510de1ebb56327d7ed086b7419ff71db198940760dcede94a775fd67f515836ec4caa7d17bdc6e0ec8924c9dc74945496e673d104b85d35df91ec33942c84b7404579023b24d78b30bcde7563740f3a10a159918daafa18d955b0bb74c94782195ff5195936b21e6ec853a8ef269b21ac325675445b1815dfab18904ad6e2ad19ec8a753d1f0fd3b9171e0ea29f274bb261b028508d984fcdb9ed3df6e77ba3eb038d606d517b7461ebee25b57de47b2f2dde11c7504bacd271f2f44e405f79faaf99962145aa537b2b1f5f4f55ea30657f48ae51f0aff8f1bc1669311607cf941717e561b5e7e44bd48adc9aafba49d947deabe07e475c52e3eb13d485e60790db1a04540b4dfe992578b9758ed42ad8bee87a60b71cc88357f2d654a368cad751455057ed219766206094c899100ffc2c1b306409931d608f99a4ba850403eeaf092ee8b47b24dfad9fe18520d3ecaa98d7cf915f270629ab3fa8214f439cd0a160df22be1d99d7a979bbbd1fcb2dfd8ccfafa1207691013f2a524e1613150e0c679ac5950cb08211defa839fc537d5d941c6141e9d724ff468b9759ec7eb825132a2a78f5b9c413ee9674b44dd25f8bb65d1e839a24b36ee0eca01129935e73437e4a08edc8a2ff97d10752867f0e46cbc0ba2ae7459a7f9fbf41da3ea7be2618caa57728aae1d378f2a408f9569da556d0fa630c183d94dd542052dbc9b52ef75585ca16cf5793240f61b15dca17bead2cc524856b36c12a74c7187480090e8c01f6c79c3cafe4e50eac4c89f6991df7ff040913e3f70058aaa123f015a86b433b8a621b555f4d1abacf785c477257cc3f43ba13220e29ef653ab6be34ce223455e9bb5d775a19200267035efe861ce55503545cc85db40f59cbb4f049154983509e19877d181c3ffeff7c8fc24f1ea3f22e2513de60f340a8c7d019b76175055bc87f94dfb6381a5319e237ab5d269e1fdbe7a9e38d7fcadbf32528e1f295f5fafd1bde5b09a24c0d04d2a401e8efd352ac51b24da627ed62c85c5fefdd1019b0f9ffd86a2c30ed03d2b1b3181e261f7b05accb95a8b1109b72dd1759b7158a1b7a2c913b0fd6b5ad1dba25740139070648641739985e9e82036e9941c15580832dc4258f425c44296cc7b0938910b29cbdfcdc48df4b753744297fc656397ff504c50ea77f0cf229eb25849621e417275bb58f0b773a173d69bdf1339438b5cd5a44871436b8f56e37b059ae06ae61517e2e2857d5b422846f15d440d46fe8be4217836c545d56cfa75d46539cd0b71311e40abe30c21766ca99310bb5bab07061d69b250fc99f855c7b1218275dbe69d2d1b0aba51815126e08f68553d99de9078aad6a732b16618578127604aa0c565b2a5f0f1f15f9916ee72ffb43ace3a615bf74c461ac150fc5fa3537c78537d3aec997a1b426df2db27ad4b2dad1d34cabe61c16a9b9118ec017b650c4fc1669ecb725330c1849136e503d719b081157704377b8c778cf79031e1d25d98f4d3eb537aa564191404132231db19a64102a4c37395a86c6d557715379e39b6ec3bd7e5e4611ceeb45198e577f3efd92dea57f694ccbb20b0b44082a22cb61c758c063c862156ba6093aeed8986e1f0afd3a26f46022d515af226bdac94fb0f61f57708c2285c39c8df04a65cbdbb3fa06a53482131ed1bd8a66ed9fcdb7a995a37de76a67d75932deb989fdf565084b10954caca8919dc125483d722a02207838ccb94bb9df367e23ae37efe8c55d23bdf9cdb31d5defbc0fe25fd6dd1bf53bf30112c3babb9161d42cd5a58330cc4a44c999b2bfc701a45e7ec3b5ef493c9e1887020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603fb382a011cffffffff0468b6e60e000000001976a91401cb2de6a35b807a6e75d7fbfcc8107b2b9d39f488ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000fb382a0000000000000000000000000400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02", + "id": 123 +} diff --git a/zebratx.json b/zebratx.json new file mode 100644 index 0000000..83f6b4b --- /dev/null +++ b/zebratx.json @@ -0,0 +1,8 @@ +{ + "result": { + "hex": "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02", + "height": 2767099, + "confirmations": 3582 + }, + "id": 123 +} From 4e86a2f5a48c45c88c3b4f15cedebc16931bb5e6 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 26 Mar 2024 14:56:10 +0000 Subject: [PATCH 086/149] Zebra Raw Tx deserialization (#44) Implements features to deserialize the hex representation of of a Zebra Transaction Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/44 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- librustzcash-wrapper/src/lib.rs | 112 +++++++++++++++++++++++++++++++- src/C/Zcash.chs | 7 ++ src/ZcashHaskell/Types.hs | 101 +++++++++++++++++++++++----- src/ZcashHaskell/Utils.hs | 19 ++++-- test/Spec.hs | 7 ++ zebrablock.json | 20 ++++++ zebrahexblock.json | 4 ++ zebratx.json | 8 +++ 8 files changed, 253 insertions(+), 25 deletions(-) create mode 100644 zebrablock.json create mode 100644 zebrahexblock.json create mode 100644 zebratx.json diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 2451d27..d6ee532 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -31,9 +31,17 @@ use zcash_primitives::{ sapling::DiversifierKey }, zip339::{Count, Mnemonic}, - transaction::components::sapling::{ - GrothProofBytes, - OutputDescription + transaction::components::{ + transparent::{ + Bundle as TransparentBundle, + TxIn, + TxOut, + Authorized + }, + sapling::{ + GrothProofBytes, + OutputDescription + } }, sapling::{ PaymentAddress, @@ -220,6 +228,81 @@ impl Hua { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Htx { + txid: Vec, + locktime: u32, + expiry: u32, + t_bundle: HTBundle +} + +impl ToHaskell for Htx { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HTBundle { + vin: Vec, + vout: Vec, + coinbase: bool +} + +impl ToHaskell for HTBundle { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +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()} + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HTxIn { + outpoint: u32, + script: Vec, + sequence: u32 +} + +impl ToHaskell for HTxIn { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl HTxIn { + pub fn pack(t: &TxIn) -> HTxIn { + return HTxIn { outpoint: t.prevout.n(), script: t.script_sig.0.clone(), sequence: t.sequence} + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HTxOut { + amt: i64, + script: Vec +} + +impl ToHaskell for HTxOut { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl HTxOut { + pub fn pack(t: &TxOut) -> HTxOut { + return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() } + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, @@ -555,6 +638,29 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt( } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_tx_read( + tx: *const u8, + tx_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW); + let mut tx_reader = Cursor::new(tx_input); + 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); + }, + 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} }; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_tx_parse( tx: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 19df3c3..7bf023f 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -123,6 +123,13 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_tx_read as rustWrapperTxRead + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer RawZebraTx'& + } + -> `()' +#} + {# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase { getVarBuffer `Buffer Phrase'& } -> `()' #} diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 612410e..d22a8a7 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -16,6 +16,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GeneralisedNewtypeDeriving #-} +{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE UndecidableInstances #-} @@ -40,6 +41,7 @@ import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP import Haskoin.Address (Address) import Haskoin.Crypto.Keys.Extended (XPrvKey) +import qualified Haskoin.Transaction.Common as H -- * General -- @@ -89,6 +91,19 @@ data ZcashNet type AccountId = Int +-- | Function to get the Base58 prefix for encoding a 'TransparentAddress' +getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) +getTransparentPrefix n t = + case t of + P2SH -> + case n of + MainNet -> (0x1c, 0xbd) + _ -> (0x1c, 0xba) + P2PKH -> + case n of + MainNet -> (0x1c, 0xb8) + _ -> (0x1d, 0x25) + -- ** Constants -- | Type for coin types on the different networks data CoinType @@ -104,7 +119,23 @@ getValue c = TestNetCoin -> 1 RegTestNetCoin -> 1 --- | Constants for Sapling Human-readable part +-- | A Zcash transaction +data Transaction = Transaction + { tx_id :: !HexString + , tx_height :: !Int + , tx_conf :: !Int + , tx_expiry :: !Int + , tx_transpBundle :: !(Maybe TransparentBundle) + } deriving (Prelude.Show, Eq, Read) + +-- | The transparent portion of a Zcash transaction +data TransparentBundle = TransparentBundle + { tb_vin :: ![H.TxIn] + , tb_vout :: ![H.TxOut] + , tb_coinbase :: !Bool + } deriving (Eq, Prelude.Show, Read) + +-- *** Constants for Sapling Human-readable part sapExtSpendingKeyHrp = "secret-extended-key-main" :: String sapExtFullViewingKeyHrp = "zxviews" :: String @@ -117,7 +148,7 @@ sapTestExtFullViewingKeyHrp = "zxviewtestsapling" :: String sapTestPaymentAddressHrp = "ztestsapling" :: String --- | Constants for Unified Human-readable part +-- *** Constants for Unified Human-readable part uniPaymentAddressHrp = "u" :: T.Text uniFullViewingKeyHrp = "uview" :: T.Text @@ -130,19 +161,6 @@ uniTestFullViewingKeyHrp = "uviewtest" :: T.Text uniTestIncomingViewingKeyHrp = "uivktest" :: T.Text --- | Function to get the Base58 prefix for encoding a 'TransparentAddress' -getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) -getTransparentPrefix n t = - case t of - P2SH -> - case n of - MainNet -> (0x1c, 0xbd) - _ -> (0x1c, 0xba) - P2PKH -> - case n of - MainNet -> (0x1c, 0xb8) - _ -> (0x1d, 0x25) - -- * RPC -- | A type to model Zcash RPC calls data RpcCall = RpcCall @@ -217,6 +235,41 @@ data RawTxResponse = RawTxResponse } deriving (Prelude.Show, Eq, Read) -- ** `zebrad` +data ZebraTxResponse = ZebraTxResponse + { ztr_blockheight :: !Int + , ztr_conf :: !Int + , ztr_hex :: !HexString + } deriving (Prelude.Show, Eq, Read) + +instance FromJSON ZebraTxResponse where + parseJSON = + withObject "ZebraTxResponse" $ \obj -> do + hex <- obj .: "hex" + height <- obj .: "height" + c <- obj .: "confirmations" + pure $ ZebraTxResponse height c hex + +-- | Type to represent a raw deserialized Zebra transaction +data RawZebraTx = RawZebraTx + { zt_id :: !HexString + , zt_locktime :: !Word32 + , zt_expiry :: !Word32 + , zt_tBundle :: !RawTBundle + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawZebraTx + +-- | Type for a raw deserialized Zebra transparent bundle +data RawTBundle = RawTBundle + { ztb_vin :: ![RawTxIn] + , ztb_vout :: ![RawTxOut] + , ztb_coinbase :: !Bool + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle + -- | Type for the response from the `zebrad` RPC method `getinfo` data ZebraGetInfo = ZebraGetInfo { zgi_build :: !T.Text @@ -268,6 +321,24 @@ data TransparentAddress = TransparentAddress , ta_bytes :: !HexString } deriving (Eq, Prelude.Show, Read) +-- | Wrapper types for transparent elements +data RawTxIn = RawTxIn + { rti_outpoint :: !Word32 + , rti_script :: !BS.ByteString + , rti_seq :: !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 RawTxIn + +data RawTxOut = RawTxOut + { rto_amt :: !Word64 + , rto_script :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxOut + -- * Sapling -- | A spending key for Sapling newtype SaplingSpendingKey = diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 39296e9..1407c79 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -22,11 +22,13 @@ import C.Zcash , rustWrapperBech32Encode , rustWrapperF4Jumble , rustWrapperF4UnJumble + , rustWrapperTxRead ) import Control.Exception (SomeException(..), try) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString as BS +import Data.HexString (HexString(..)) import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable @@ -34,13 +36,7 @@ import Network.HTTP.Client (HttpException(..)) import Network.HTTP.Simple import ZcashHaskell.Types -import Foreign.C.Types -import Foreign.Marshal.Array (allocaArray, peekArray) -import Foreign.Ptr (Ptr) - -import Data.Word - --- | +-- * Utility functions -- | Decode the given bytestring using Bech32 decodeBech32 :: BS.ByteString -> RawData decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode @@ -57,6 +53,7 @@ f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble f4UnJumble :: BS.ByteString -> BS.ByteString f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble +-- * Node interaction -- | Make a Zcash RPC call makeZcashCall :: (MonadIO m, FromJSON a) @@ -102,3 +99,11 @@ makeZebraCall host port m params = do case result zebraResp of Nothing -> return $ Left "Empty response from Zebra" Just zR -> return $ Right zR + +readZebraTransaction :: HexString -> Maybe RawZebraTx +readZebraTransaction hex = + if BS.length (hexBytes $ zt_id rawTx) < 1 + then Nothing + else Just rawTx + where + rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex diff --git a/test/Spec.hs b/test/Spec.hs index 97c4a0c..f81c71a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -71,6 +71,7 @@ import ZcashHaskell.Types , UnifiedAddress(..) , UnifiedFullViewingKey(..) , ZcashNet(..) + , ZebraTxResponse(..) , decodeHexText , getValue ) @@ -765,6 +766,12 @@ main = do let bscAdr = SaplingReceiver $ BS.pack cAdr let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk) fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr + describe "Zebra response processing: " $ do + it "Raw transaction" $ do + let h = + hexString + "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" + readZebraTransaction h `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property diff --git a/zebrablock.json b/zebrablock.json new file mode 100644 index 0000000..5665f92 --- /dev/null +++ b/zebrablock.json @@ -0,0 +1,20 @@ +{ + "result": { + "hash": "0041ee9cb0e256a73c92bb72d830143c402ea350152f56f19f74d23cf51418fb", + "confirmations": 3583, + "height": 2767099, + "tx": [ + "d169ec3eda57dc750edfc1aa6b8ffb4ed2065780bfd5964de34b529503ec372f", + "987fcdb9bd37cbb5b205a8336de60d043f7028bebaa372828d81f3da296c7ef9" + ], + "trees": { + "sapling": { + "size": 129349 + }, + "orchard": { + "size": 39382 + } + } + }, + "id": 123 +} diff --git a/zebrahexblock.json b/zebrahexblock.json new file mode 100644 index 0000000..19d6b90 --- /dev/null +++ b/zebrahexblock.json @@ -0,0 +1,4 @@ +{ + "result": "040000006deacb36066fbbf99f00436c5a384e885c0cecff393b922ccfb22ed81b9e3600722039ae03cfdbee344a18774168795a936a720db144408a9abd92c55eb4a276f1b96e93c15bc2943a1f2483e466c456d2ea22ad82eea3d5788dbb5448bcb30c33d0fd658002451f6c005c8fa5b5d0cb7a24d326de3fd34422e22718a0c5e23972800292ed830000fd4005022b23ffe50b150fe854961e44e5d752975857fbc5026bc06a7550448e858e71dc7d927b44a04a325df027973f510de1ebb56327d7ed086b7419ff71db198940760dcede94a775fd67f515836ec4caa7d17bdc6e0ec8924c9dc74945496e673d104b85d35df91ec33942c84b7404579023b24d78b30bcde7563740f3a10a159918daafa18d955b0bb74c94782195ff5195936b21e6ec853a8ef269b21ac325675445b1815dfab18904ad6e2ad19ec8a753d1f0fd3b9171e0ea29f274bb261b028508d984fcdb9ed3df6e77ba3eb038d606d517b7461ebee25b57de47b2f2dde11c7504bacd271f2f44e405f79faaf99962145aa537b2b1f5f4f55ea30657f48ae51f0aff8f1bc1669311607cf941717e561b5e7e44bd48adc9aafba49d947deabe07e475c52e3eb13d485e60790db1a04540b4dfe992578b9758ed42ad8bee87a60b71cc88357f2d654a368cad751455057ed219766206094c899100ffc2c1b306409931d608f99a4ba850403eeaf092ee8b47b24dfad9fe18520d3ecaa98d7cf915f270629ab3fa8214f439cd0a160df22be1d99d7a979bbbd1fcb2dfd8ccfafa1207691013f2a524e1613150e0c679ac5950cb08211defa839fc537d5d941c6141e9d724ff468b9759ec7eb825132a2a78f5b9c413ee9674b44dd25f8bb65d1e839a24b36ee0eca01129935e73437e4a08edc8a2ff97d10752867f0e46cbc0ba2ae7459a7f9fbf41da3ea7be2618caa57728aae1d378f2a408f9569da556d0fa630c183d94dd542052dbc9b52ef75585ca16cf5793240f61b15dca17bead2cc524856b36c12a74c7187480090e8c01f6c79c3cafe4e50eac4c89f6991df7ff040913e3f70058aaa123f015a86b433b8a621b555f4d1abacf785c477257cc3f43ba13220e29ef653ab6be34ce223455e9bb5d775a19200267035efe861ce55503545cc85db40f59cbb4f049154983509e19877d181c3ffeff7c8fc24f1ea3f22e2513de60f340a8c7d019b76175055bc87f94dfb6381a5319e237ab5d269e1fdbe7a9e38d7fcadbf32528e1f295f5fafd1bde5b09a24c0d04d2a401e8efd352ac51b24da627ed62c85c5fefdd1019b0f9ffd86a2c30ed03d2b1b3181e261f7b05accb95a8b1109b72dd1759b7158a1b7a2c913b0fd6b5ad1dba25740139070648641739985e9e82036e9941c15580832dc4258f425c44296cc7b0938910b29cbdfcdc48df4b753744297fc656397ff504c50ea77f0cf229eb25849621e417275bb58f0b773a173d69bdf1339438b5cd5a44871436b8f56e37b059ae06ae61517e2e2857d5b422846f15d440d46fe8be4217836c545d56cfa75d46539cd0b71311e40abe30c21766ca99310bb5bab07061d69b250fc99f855c7b1218275dbe69d2d1b0aba51815126e08f68553d99de9078aad6a732b16618578127604aa0c565b2a5f0f1f15f9916ee72ffb43ace3a615bf74c461ac150fc5fa3537c78537d3aec997a1b426df2db27ad4b2dad1d34cabe61c16a9b9118ec017b650c4fc1669ecb725330c1849136e503d719b081157704377b8c778cf79031e1d25d98f4d3eb537aa564191404132231db19a64102a4c37395a86c6d557715379e39b6ec3bd7e5e4611ceeb45198e577f3efd92dea57f694ccbb20b0b44082a22cb61c758c063c862156ba6093aeed8986e1f0afd3a26f46022d515af226bdac94fb0f61f57708c2285c39c8df04a65cbdbb3fa06a53482131ed1bd8a66ed9fcdb7a995a37de76a67d75932deb989fdf565084b10954caca8919dc125483d722a02207838ccb94bb9df367e23ae37efe8c55d23bdf9cdb31d5defbc0fe25fd6dd1bf53bf30112c3babb9161d42cd5a58330cc4a44c999b2bfc701a45e7ec3b5ef493c9e1887020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603fb382a011cffffffff0468b6e60e000000001976a91401cb2de6a35b807a6e75d7fbfcc8107b2b9d39f488ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000fb382a0000000000000000000000000400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02", + "id": 123 +} diff --git a/zebratx.json b/zebratx.json new file mode 100644 index 0000000..83f6b4b --- /dev/null +++ b/zebratx.json @@ -0,0 +1,8 @@ +{ + "result": { + "hex": "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02", + "height": 2767099, + "confirmations": 3582 + }, + "id": 123 +} From f593fefd7f80620137b99209b27fce22edeecccb Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 26 Mar 2024 15:37:04 -0500 Subject: [PATCH 087/149] Add types for low-level transparent components --- 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 From bb9d336dc326ad9d35eb8c29dbee9e3acbb29341 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 26 Mar 2024 20:39:31 +0000 Subject: [PATCH 088/149] 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 From 0fb02b2514ca21af8a65533bdf12fdcc42a1d3b0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 27 Mar 2024 09:00:00 -0500 Subject: [PATCH 089/149] Correct parsing of OutPoint --- src/ZcashHaskell/Types.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 91ef4a3..c4be9ed 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -31,9 +31,10 @@ 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 qualified Data.ByteString.Short as BS (ShortByteString, toShort) import Data.HexString import Data.Int -import Data.Maybe (fromMaybe) +import Data.Maybe (fromJust, fromMaybe) import Data.Structured import qualified Data.Text as T import qualified Data.Text.Encoding as E @@ -41,6 +42,7 @@ import Data.Word import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP import Haskoin.Address (Address) +import qualified Haskoin.Crypto.Hash as H (Hash256(..)) import Haskoin.Crypto.Keys.Extended (XPrvKey) import qualified Haskoin.Transaction.Common as H @@ -151,7 +153,9 @@ fromRawTxIn :: RawTxIn -> H.TxIn fromRawTxIn t = H.TxIn (H.OutPoint - (read $ US.toString $ C.fromStrict $ rop_hash $ rti_outpoint t) + ((fromJust . + H.hexToTxHash . E.decodeUtf8Lenient . rop_hash . rti_outpoint) + t) (rop_n $ rti_outpoint t)) (rti_script t) (rti_seq t) From 52950885c146b3cefc6da61a18156764751d4f5b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 27 Mar 2024 13:22:34 -0500 Subject: [PATCH 090/149] Implements parsing of transparent bundles --- CHANGELOG.md | 7 +++++++ src/ZcashHaskell/Types.hs | 20 +++++++++++--------- test/Spec.hs | 26 ++++++++++++++++++++++++-- zcash-haskell.cabal | 3 ++- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 999e019..2f357bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.2.0] + +### Added + +- Functionality to parse transparent bundles from Zebra +- Types for transparent `TxIn`, `TxOut`, `OutPoint` + ## [0.5.1.0] ### Added diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index c4be9ed..ad81d41 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -150,15 +150,17 @@ fromRawTBundle rtb = (ztb_coinbase rtb) fromRawTxIn :: RawTxIn -> H.TxIn -fromRawTxIn t = - H.TxIn - (H.OutPoint - ((fromJust . - H.hexToTxHash . E.decodeUtf8Lenient . rop_hash . rti_outpoint) - t) - (rop_n $ rti_outpoint t)) - (rti_script t) - (rti_seq t) +fromRawTxIn t = H.TxIn op (rti_script t) (rti_seq t) + where + op = + if rop_hash (rti_outpoint t) == + "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL" + then H.nullOutPoint + else H.OutPoint + ((fromJust . + H.hexToTxHash . E.decodeUtf8Lenient . rop_hash . rti_outpoint) + t) + (rop_n $ rti_outpoint t) fromRawTxOut :: RawTxOut -> H.TxOut fromRawTxOut t = H.TxOut (rto_amt t) (rto_script t) diff --git a/test/Spec.hs b/test/Spec.hs index f81c71a..04f5678 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -34,6 +34,7 @@ import qualified Data.Text.Encoding as E import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO import GHC.Float.RealFracMethods (properFractionDoubleInteger) +import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck @@ -59,7 +60,12 @@ import ZcashHaskell.Types , OrchardSpendingKey(..) , Phrase(..) , RawData(..) + , RawOutPoint(..) + , RawTBundle(..) + , RawTxIn(..) + , RawTxOut(..) , RawTxResponse(..) + , RawZebraTx(..) , SaplingReceiver(..) , SaplingSpendingKey(..) , Scope(..) @@ -73,6 +79,7 @@ import ZcashHaskell.Types , ZcashNet(..) , ZebraTxResponse(..) , decodeHexText + , fromRawTBundle , getValue ) import ZcashHaskell.Utils @@ -767,11 +774,26 @@ main = do let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk) fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr describe "Zebra response processing: " $ do - it "Raw transaction" $ do + it "Raw transaction from faucet" $ do let h = hexString "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" - readZebraTransaction h `shouldNotBe` Nothing + let t = readZebraTransaction h + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let tb = zt_tBundle t' + fromRawTBundle tb `shouldNotBe` Nothing + it "Raw transaction" $ do + let h = + hexString + "0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603204c2a010cffffffff0480b2e60e000000001976a914278aff0c0f8734638ce81aaef4ab0afddd36552888ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000204c2a000000000000000000000000" + let t = readZebraTransaction h + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let tb = zt_tBundle t' + fromRawTBundle tb `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 15d27f9..aeeb2ca 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.1.0 +version: 0.5.2.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain @@ -76,6 +76,7 @@ test-suite zcash-haskell-test , haskoin-core , hexstring >= 0.12.1 , hspec + , HUnit , QuickCheck , quickcheck-transformer , text From 7cada685ce4263d4c49e3268475e5b40ea7371b8 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Thu, 28 Mar 2024 15:06:02 -0400 Subject: [PATCH 091/149] getBlockTime function added --- librustzcash-wrapper/Cargo.lock | 969 +++++++++++++++++++++++++++++++- librustzcash-wrapper/Cargo.toml | 3 + librustzcash-wrapper/src/lib.rs | 1 + src/ZcashHaskell/Utils.hs | 39 ++ test/Spec.hs | 4 + 5 files changed, 996 insertions(+), 20 deletions(-) diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 7f59103..485be44 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aead" version = "0.5.2" @@ -58,6 +73,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.21.2" @@ -297,6 +327,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + [[package]] name = "byteorder" version = "1.4.3" @@ -320,9 +356,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -371,6 +407,22 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.6" @@ -474,6 +526,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "equihash" version = "0.2.0" @@ -484,6 +545,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -492,7 +559,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -540,6 +607,36 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fpe" version = "0.6.1" @@ -560,6 +657,45 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -581,6 +717,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "group" version = "0.13.0" @@ -593,6 +735,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "halo2_gadgets" version = "0.3.0" @@ -651,6 +812,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "haskell-ffi" version = "0.1.0" @@ -709,6 +876,112 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "hyper" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "incrementalmerkletree" version = "0.5.0" @@ -728,6 +1001,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + [[package]] name = "inout" version = "0.1.3" @@ -754,9 +1037,15 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.10.5" @@ -766,6 +1055,21 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "jubjub" version = "0.10.0" @@ -791,9 +1095,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.146" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -807,6 +1111,16 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.19" @@ -847,18 +1161,62 @@ dependencies = [ "nonempty", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -921,6 +1279,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -933,6 +1300,50 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "orchard" version = "0.6.0" @@ -1000,6 +1411,29 @@ dependencies = [ "group", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.0", +] + [[package]] name = "password-hash" version = "0.3.2" @@ -1049,14 +1483,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "poly1305" @@ -1257,6 +1723,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "ref-cast" version = "1.0.16" @@ -1292,6 +1767,54 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "reqwest" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e333b1eb9fe677f6893a9efcb0d277a2d3edd83f358a236b657c32301dc6e5f6" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" version = "0.37.20" @@ -1303,7 +1826,16 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", ] [[package]] @@ -1316,6 +1848,8 @@ dependencies = [ "haskell-ffi", "orchard 0.7.1", "proc-macro2", + "reqwest", + "tokio", "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", @@ -1323,6 +1857,21 @@ dependencies = [ "zip32", ] +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1338,6 +1887,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.193" @@ -1358,6 +1930,29 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "serde_json" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1394,6 +1989,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -1434,6 +2063,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -1449,9 +2105,9 @@ dependencies = [ "autocfg", "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1508,6 +2164,60 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.11" @@ -1530,6 +2240,34 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.37" @@ -1537,6 +2275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1562,6 +2301,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.16.0" @@ -1580,6 +2325,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.8" @@ -1605,18 +2356,120 @@ dependencies = [ "subtle", ] +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.0" @@ -1634,7 +2487,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -1643,13 +2505,28 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -1658,42 +2535,94 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 9c8f966..6554f17 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -17,6 +17,9 @@ zcash_primitives = "0.13.0" zcash_client_backend = "0.10.0" zip32 = "0.1.0" proc-macro2 = "1.0.66" +reqwest = "0.12.1" +tokio = { version="1.36.0", features = ["full"] } + [features] capi = [] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 2451d27..4f99a5d 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -66,6 +66,7 @@ use zcash_client_backend::keys::sapling::{ }; use zcash_primitives::zip32::DiversifierIndex; +use zcash_primitives::block::BlockHeader; use orchard::{ Action, diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 39296e9..1f881d9 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -102,3 +102,42 @@ makeZebraCall host port m params = do case result zebraResp of Nothing -> return $ Left "Empty response from Zebra" Just zR -> return $ Right zR + +-- | +hexDigitToInt :: Char -> Int +hexDigitToInt x = case x of + '0' -> 0 + '1' -> 1 + '2' -> 2 + '3' -> 3 + '4' -> 4 + '5' -> 5 + '6' -> 6 + '7' -> 7 + '8' -> 8 + '9' -> 9 + 'a' -> 10 + 'b' -> 11 + 'c' -> 12 + 'd' -> 13 + 'e' -> 14 + 'f' -> 15 + 'A' -> 10 + 'B' -> 11 + 'C' -> 12 + 'D' -> 13 + 'E' -> 14 + 'F' -> 15 + +hexStringToInt :: String -> Int -> Int -> Int +hexStringToInt [] val _ = val +hexStringToInt (x:y:xs) val e = + hexStringToInt xs (val + ((hexDigitToInt x)*16 + (hexDigitToInt y))*256^e) (e+1) + + +getBlockTime :: String -> Int +getBlockTime hex_block = do + let hex_date = take 8 (drop 200 hex_block) + hexStringToInt hex_date 0 0 + + diff --git a/test/Spec.hs b/test/Spec.hs index 97c4a0c..d570070 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -765,6 +765,10 @@ main = do let bscAdr = SaplingReceiver $ BS.pack cAdr let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk) fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr + describe "Call zebra to get Block data" $ do + it "Call genBlockResponse" $ do + let blkdata <- genBlockResponse + return $ Just blkdata `shouldBe` "" -- | Properties prop_PhraseLength :: Property From dbae2150da0d6bfb0a2906b37faefc5890017ede Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 29 Mar 2024 13:54:21 -0500 Subject: [PATCH 092/149] Advance bundle FFI --- librustzcash-wrapper/src/lib.rs | 149 +++++++++++++++++++++++++++++--- 1 file changed, 137 insertions(+), 12 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index e74beb3..f140d0f 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -32,6 +32,7 @@ use zcash_primitives::{ }, zip339::{Count, Mnemonic}, transaction::components::{ + amount::Amount, transparent::{ Bundle as TransparentBundle, TxIn, @@ -41,7 +42,10 @@ use zcash_primitives::{ }, sapling::{ GrothProofBytes, - OutputDescription + OutputDescription, + SpendDescription, + Authorized as SaplingAuthorized, + Bundle as SaplingBundle } }, sapling::{ @@ -77,6 +81,8 @@ use zcash_client_backend::keys::sapling::{ use zcash_primitives::zip32::DiversifierIndex; use orchard::{ + Bundle as OrchardBundle, + bundle::Authorized as OrchardAuthorized, Action, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, @@ -234,7 +240,9 @@ pub struct Htx { txid: Vec, locktime: u32, expiry: u32, - t_bundle: HTBundle + t_bundle: HTBundle, + s_bundle: HSBundle, + o_bundle: HOBundle } impl ToHaskell for Htx { @@ -324,6 +332,100 @@ impl Houtpoint { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HSBundle { + empty: bool, + spends: Vec, + outputs: Vec , + value: i64, + sig: Vec +} + +impl ToHaskell for HSBundle { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl HSBundle { + pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { + let s = Vec::new(); + sb.authorization().binding_sig.write(s); + return HSBundle {empty: true, spends: sb.shielded_spends() , outputs: sb.shielded_outputs() , value: i64::from(sb.value_balance()) , sig: s } + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Hspend { + cv: Vec, + anchor: Vec, + nullifier: Vec, + rk: Vec, + proof: Vec, + authsig: Vec +} + +impl ToHaskell for Hspend { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl Hspend { + pub fn pack(sp: &SpendDescription) -> Hspend { + let rk = Vec::new(); + let authsig = Vec::new(); + sp.rk().write(rk); + sp.spend_auth_sig().write(authsig); + return Hspend {cv: sp.cv().to_bytes().to_vec(), anchor: sp.anchor().to_bytes().to_vec(), nullifier: sp.nullifier().to_vec(), rk, proof: sp.zkproof().to_vec(), authsig } + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HOBundle { + empty: bool, + actions: Vec, + flags: Vec, + value: i64, + anchor: Vec, + auth: Vec +} + +impl ToHaskell for HOBundle { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl HOBundle { + pub fn from_bundle(b: &OrchardBundle) -> HOBundle { + return HOBundle {actions: b.actions(), flags: b.flags(), value: b.value_balance(), anchor: b.anchor(), auth: b.authorization()} + } +} + + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Hflags { + spends: bool, + outputs: bool +} + +impl ToHaskell for Hflags { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + +impl Hflags { + pub fn pack(f: &Flags) -> Hflags { + return Hflags {spends: f.spends_enabled(), outputs: f.outputs_enabled()} + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, @@ -673,19 +775,42 @@ pub extern "C" fn rust_wrapper_tx_read( match parsed_tx { Ok(t) => { 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); + let sb = t.sapling_bundle(); + let ob = t.orchard_bundle(); + let h1 = Htx + { txid: t.txid().as_ref().to_vec() + , locktime: t.lock_time() + , expiry: u32::from(t.expiry_height()) + , t_bundle: match tb { + Some(tb1) => {HTBundle::from_bundle(tb1)}, + None => {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: false}}} + , s_bundle: match sb { + Some(sb1) => {HSBundle::from_bundle(sb1)}, + None => {} } + , o_bundle: match ob { + Some(ob1) => {HOBundle::from_bundle(ob1)}, + None => {} } - } + }; + marshall_to_haskell_var(&h1, out, out_len, RW); }, Err(_e) => { - 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} }; + 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}, + s_bundle: HSBundle + {empty: true, + }, + o_bundle: HOBundle + {empty: true, + } + }; marshall_to_haskell_var(&h0, out, out_len, RW); } } From 58b74a4c9ae491b5c1170265d2a59072b3c5a8b0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 29 Mar 2024 15:51:48 -0500 Subject: [PATCH 093/149] Update `getBlockTime` to use `HexString` --- librustzcash-wrapper/Cargo.lock | 957 +------------------------------- librustzcash-wrapper/Cargo.toml | 2 - src/ZcashHaskell/Utils.hs | 47 +- test/Spec.hs | 9 +- zcash-haskell.cabal | 1 + 5 files changed, 29 insertions(+), 987 deletions(-) diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 485be44..8b0a869 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aead" version = "0.5.2" @@ -73,21 +58,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.21.2" @@ -327,12 +297,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bumpalo" -version = "3.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" - [[package]] name = "byteorder" version = "1.4.3" @@ -407,22 +371,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" version = "0.2.6" @@ -526,15 +474,6 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - [[package]] name = "equihash" version = "0.2.0" @@ -545,12 +484,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "errno" version = "0.3.1" @@ -559,7 +492,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -607,36 +540,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "fpe" version = "0.6.1" @@ -657,45 +560,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -717,12 +581,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - [[package]] name = "group" version = "0.13.0" @@ -735,25 +593,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 2.2.6", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "halo2_gadgets" version = "0.3.0" @@ -812,12 +651,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - [[package]] name = "haskell-ffi" version = "0.1.0" @@ -876,112 +709,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "hyper" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower", - "tower-service", - "tracing", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "incrementalmerkletree" version = "0.5.0" @@ -1001,16 +728,6 @@ dependencies = [ "hashbrown 0.12.3", ] -[[package]] -name = "indexmap" -version = "2.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", -] - [[package]] name = "inout" version = "0.1.3" @@ -1037,15 +754,9 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.48.0", + "windows-sys", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - [[package]] name = "itertools" version = "0.10.5" @@ -1055,21 +766,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "jubjub" version = "0.10.0" @@ -1111,16 +807,6 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.19" @@ -1161,62 +847,18 @@ dependencies = [ "nonempty", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "7.1.3" @@ -1279,15 +921,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.17.1" @@ -1300,50 +933,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.4.2", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "orchard" version = "0.6.0" @@ -1411,29 +1000,6 @@ dependencies = [ "group", ] -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "smallvec", - "windows-targets 0.48.0", -] - [[package]] name = "password-hash" version = "0.3.2" @@ -1483,27 +1049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap 1.9.3", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", + "indexmap", ] [[package]] @@ -1512,18 +1058,6 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - [[package]] name = "poly1305" version = "0.8.0" @@ -1723,15 +1257,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "ref-cast" version = "1.0.16" @@ -1767,54 +1292,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" -[[package]] -name = "reqwest" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e333b1eb9fe677f6893a9efcb0d277a2d3edd83f358a236b657c32301dc6e5f6" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - [[package]] name = "rustix" version = "0.37.20" @@ -1826,16 +1303,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64", + "windows-sys", ] [[package]] @@ -1848,8 +1316,6 @@ dependencies = [ "haskell-ffi", "orchard 0.7.1", "proc-macro2", - "reqwest", - "tokio", "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", @@ -1857,21 +1323,6 @@ dependencies = [ "zip32", ] -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "scopeguard" version = "1.1.0" @@ -1887,29 +1338,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.193" @@ -1930,29 +1358,6 @@ dependencies = [ "syn 2.0.32", ] -[[package]] -name = "serde_json" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "sha2" version = "0.9.9" @@ -1989,40 +1394,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "spin" version = "0.5.2" @@ -2063,33 +1434,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tap" version = "1.0.1" @@ -2105,9 +1449,9 @@ dependencies = [ "autocfg", "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2164,60 +1508,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - [[package]] name = "toml" version = "0.5.11" @@ -2240,34 +1530,6 @@ dependencies = [ "syn 2.0.32", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.37" @@ -2275,7 +1537,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2301,12 +1562,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typenum" version = "1.16.0" @@ -2325,12 +1580,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.8" @@ -2356,120 +1605,18 @@ dependencies = [ "subtle", ] -[[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.32", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "which" version = "4.4.0" @@ -2487,16 +1634,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -2505,28 +1643,13 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" -dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2535,94 +1658,42 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wyz" version = "0.5.1" diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 6554f17..cdacc12 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -17,8 +17,6 @@ zcash_primitives = "0.13.0" zcash_client_backend = "0.10.0" zip32 = "0.1.0" proc-macro2 = "1.0.66" -reqwest = "0.12.1" -tokio = { version="1.36.0", features = ["full"] } [features] diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 1f881d9..bddb18e 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -26,7 +26,11 @@ import C.Zcash import Control.Exception (SomeException(..), try) import Control.Monad.IO.Class import Data.Aeson +import Data.Binary.Get import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as C +import qualified Data.ByteString.Lazy as LBS +import Data.HexString import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable @@ -103,41 +107,8 @@ makeZebraCall host port m params = do Nothing -> return $ Left "Empty response from Zebra" Just zR -> return $ Right zR --- | -hexDigitToInt :: Char -> Int -hexDigitToInt x = case x of - '0' -> 0 - '1' -> 1 - '2' -> 2 - '3' -> 3 - '4' -> 4 - '5' -> 5 - '6' -> 6 - '7' -> 7 - '8' -> 8 - '9' -> 9 - 'a' -> 10 - 'b' -> 11 - 'c' -> 12 - 'd' -> 13 - 'e' -> 14 - 'f' -> 15 - 'A' -> 10 - 'B' -> 11 - 'C' -> 12 - 'D' -> 13 - 'E' -> 14 - 'F' -> 15 - -hexStringToInt :: String -> Int -> Int -> Int -hexStringToInt [] val _ = val -hexStringToInt (x:y:xs) val e = - hexStringToInt xs (val + ((hexDigitToInt x)*16 + (hexDigitToInt y))*256^e) (e+1) - - -getBlockTime :: String -> Int -getBlockTime hex_block = do - let hex_date = take 8 (drop 200 hex_block) - hexStringToInt hex_date 0 0 - - +getBlockTime :: HexString -> Int +getBlockTime hex_block = + fromIntegral $ + runGet getInt32le $ + LBS.fromStrict $ BS.take 4 (BS.drop 100 $ hexBytes hex_block) diff --git a/test/Spec.hs b/test/Spec.hs index d570070..b173c7a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -765,10 +765,11 @@ main = do let bscAdr = SaplingReceiver $ BS.pack cAdr let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk) fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr - describe "Call zebra to get Block data" $ do - it "Call genBlockResponse" $ do - let blkdata <- genBlockResponse - return $ Just blkdata `shouldBe` "" + describe "Zebra parsing:" $ do + it "Read block time" $ do + let blkdata = + "04000000c732575a3e94b7464c84b35935c2fca26e40d37b6f3278bda3d941877b192a0048cb59da8eeaf9f622d4537ec2a27918415f444d0bb143e8091b470a18a438c117fd96323fd1a9c775ed793154de0e8a2023b551431e89b5ce6f30ef047388f0bdbb0266fe81481f2c006046a8be3bca5b8819209d053a88184bb48753171accb807c38f46d00000fd400500b6a304cbc4e6caecc9b3a29ce42d71752bf5396944e28465e29d1bd7e7f3c680db3c4b85a1488d7c4e21a70c1cf50cfe4e7f70a4f1abb638f144afb90dfa69adfc7c981fd8792df19926da695356699ab76213094431bf6491b80572c310ab8e51c27cda39c771371dbf7adc60089ff5f9614577a92ce3a1e452d5d8280e3d970322e1fc5937c53369623d5e58e57672b48c2edc7a86f7d7de62e06668097547b782a01d3794a50f131695f7de3e4bf038b12abffd29b8eba618820e143ba0be57ccaa67f64511460027efb065c6ddff501376744c6b68ca01d884025e7f939521d230b1a46016d66ad3ab2262dbd477b38bbec234a19425b939dc0f1d868035c3ef99eee635c9dfd62235ede6d62f652fec499127a152e7be4f959a9bb7fcca9b335de98d14aee1330fe37f1ba28be26902a1cddbc3f67f2f6317093d3f278c1c0156e5319a0b35360e38719911b8012a8cc34893f05dc28a22787f3e0461836aef602110e621c8b9d78e991ccc0160deb8c8921f6fd13bd00d47a170b9d0b09ffd57e37467400c138823dce7c12027eb9f919b8d5f4072b331a042f6f5ebb814c6a903110cc97161787d1eb2716c8fb98d747abf321a0b079f3b6a1a0329337b4922a0d739886a03f0d475d41005733f26c675dd9694712d6ee07772a5e8f6b5871789ac14159beced65d3b2196725baa4ce04f059a7034ff0d7ce081e94d23e0115b9d8f0d99fa5b4f9771b4feaac881ac80f178c3575cc70858e40f53f1d0406253edba75e2bb6f40eb2ef4ece3df2b8dadbd3ac168b1743a0cfb68ba81ca4c838435002587852e81e0a0c3c77e11218df892ac0c2f343967b127fbb822712ceabc459ded7f3ca3e95ed5ddfe6cdfe5657da1b12ee47eb63af7a839cdbc1f52f51e349dbf2bd7d8529f814108723d97fcfda7563c4fbe3a24118352e4706f9f91406f11acde3a570e10a258fa6a5415f57a26988249a87e57f192d0408b6f56c2ef65d5e37597d144e8fe1014a4f552dc8c213b0a70b2ccff6747f6f1e0d598715d8cb0da37e0331c01da1f9e0505e9d1907628427d5d032a29fa0e14b75289b10c7d3109b590aec2a4debda9eabcbd3163f6df67efd92687d0c492a71d48b2995a658b34e96814e5674a62e3738efe53e0daf4bfe9d1031e45a246bd3b32c6d581b71c0b60ad711570bca3848c38ba5aed74d977a185d199db721c2677d4868e5afaa6cc2c7369898f58f93738681118d529194d41f0cb6103915d2e99422db387fb90046eae764a79dfe8733a7f68439d0f37e773d16ac7f0ba3a8ce0fd6746d038704b2a0a979659a859682581fee0be3fc50eed13e85e6b52f398d16b59f5f738c1eb289b3861d636b961d9220c1f76bf8a8be3ffd1e2472461202285aa1c5b7eac58564aaab0beedb65e1078517e031888aa3cb8843876aaf19311d5150529a10c9d9bce1538f7533aff126e518ef1ebd919f26aa3869ed1ced167cb1de6075897ad2dc461b9156922c4ee387f8e1635e95564d3964e47d39e6e82079e0220df00614ecd4fd31a5f3d13c9191fc65037ad3cd651f055cc795260f21bb0ba8acd7e3b65b5ff81d5a1b187a777c826f96637e00f8147c4bd43753fe9e0cd73564b53a0e9eab27745505ccd2e4a1a217307c66030ca115580ce6ecb9a8b043ca41bd2de15abd13838d130d3cfecd8d0e3bb6c982bf1f9cdc799c3256e2d722d3f13cd7acdb9d75d7e36483b2ae01e5dcb70db824eadd9c3955d79557d612a879fa6da1da479807640e37bf25a22091f1cf6fb5293e60e5616416fb2adb1d9892258e8ac6859978159338d3b6e2063b9d842a24d61d0616506d5088fe76025df35619febd57f92e30699a0df8731bdbad0775922bb12fb1697d9da833010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603204c2a010cffffffff0480b2e60e000000001976a914278aff0c0f8734638ce81aaef4ab0afddd36552888ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000204c2a000000000000000000000000" + getBlockTime (hexString blkdata) `shouldBe` 1711455165 -- | Properties prop_PhraseLength :: Property diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 05ac71b..4fecf64 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -55,6 +55,7 @@ library , http-conduit , http-client , memory + , binary , text , haskoin-core , secp256k1-haskell From 6ad879c1557439a35b694284f4a8cc1326a8c7f5 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 2 Apr 2024 11:23:05 -0500 Subject: [PATCH 094/149] Implements FFI for transaction deserialization --- librustzcash-wrapper/Cargo.lock | 46 ++------------ librustzcash-wrapper/Cargo.toml | 3 +- librustzcash-wrapper/src/lib.rs | 98 ++++++++++++++++++----------- src/ZcashHaskell/Types.hs | 108 +++++++++++++++++++++++++++----- test/Spec.hs | 44 +++++++++---- zcash-haskell.cabal | 2 +- 6 files changed, 190 insertions(+), 111 deletions(-) diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 7f59103..50b15c1 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -961,36 +961,6 @@ dependencies = [ "zcash_note_encryption", ] -[[package]] -name = "orchard" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" -dependencies = [ - "aes", - "bitvec", - "blake2b_simd", - "ff", - "fpe", - "group", - "halo2_gadgets", - "halo2_proofs", - "hex", - "incrementalmerkletree", - "lazy_static", - "memuse", - "nonempty", - "pasta_curves", - "rand", - "reddsa", - "serde", - "subtle", - "tracing", - "zcash_note_encryption", - "zcash_spec", - "zip32", -] - [[package]] name = "pairing" version = "0.23.0" @@ -1314,7 +1284,8 @@ dependencies = [ "borsh 0.10.3", "f4jumble", "haskell-ffi", - "orchard 0.7.1", + "nonempty", + "orchard", "proc-macro2", "zcash_address 0.2.0", "zcash_client_backend", @@ -1743,7 +1714,7 @@ dependencies = [ "incrementalmerkletree", "memuse", "nom", - "orchard 0.6.0", + "orchard", "percent-encoding", "prost", "rayon", @@ -1806,7 +1777,7 @@ dependencies = [ "lazy_static", "memuse", "nonempty", - "orchard 0.6.0", + "orchard", "rand", "rand_core", "sha2 0.10.6", @@ -1816,15 +1787,6 @@ dependencies = [ "zcash_note_encryption", ] -[[package]] -name = "zcash_spec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" -dependencies = [ - "blake2b_simd", -] - [[package]] name = "zeroize" version = "1.6.0" diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 9c8f966..22e1fea 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -11,12 +11,13 @@ f4jumble = "0.1" zcash_address = "0.2.0" borsh = "0.10" bech32 = "0.11" -orchard = "0.7.0" +orchard = "0.6.0" zcash_note_encryption = "0.4.0" zcash_primitives = "0.13.0" zcash_client_backend = "0.10.0" zip32 = "0.1.0" proc-macro2 = "1.0.66" +nonempty = "0.7.0" [features] capi = [] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index f140d0f..eb6bde0 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -10,6 +10,8 @@ use std::{ }, }; +use nonempty::NonEmpty; + use f4jumble; use borsh::{BorshDeserialize, BorshSerialize}; @@ -82,7 +84,10 @@ use zcash_primitives::zip32::DiversifierIndex; use orchard::{ Bundle as OrchardBundle, - bundle::Authorized as OrchardAuthorized, + bundle::{ + Authorized as OrchardAuthorized, + Flags + }, Action, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, @@ -135,12 +140,12 @@ impl ToHaskell for HrawTx { #[derive(BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { - cv: Vec, - cmu: Vec, - eph_key: Vec, - enc_txt: Vec, - out_txt: Vec, - proof: Vec + cv: Hhex, + cmu: Hhex, + eph_key: Hhex, + enc_txt: Hhex, + out_txt: Hhex, + proof: Hhex } impl FromHaskell for HshieldedOutput { @@ -158,9 +163,15 @@ impl ToHaskell for HshieldedOutput { } impl HshieldedOutput { - fn from_object(s: OutputDescription) -> Result { - let o = HshieldedOutput { cv: s.cv().to_bytes().to_vec(), cmu: s.cmu().to_bytes().to_vec(), eph_key: s.ephemeral_key().0.to_vec(), enc_txt: s.enc_ciphertext().to_vec(), out_txt: s.out_ciphertext().to_vec(), proof: s.zkproof().to_vec() }; - Ok(o) + fn from_object(s: &OutputDescription) -> HshieldedOutput { + HshieldedOutput { cv: Hhex{ bytes: s.cv().to_bytes().to_vec()}, cmu: Hhex{ bytes: s.cmu().to_bytes().to_vec()}, eph_key: Hhex{ bytes: s.ephemeral_key().0.to_vec()}, enc_txt: Hhex{ bytes: s.enc_ciphertext().to_vec()}, out_txt: Hhex{ bytes: s.out_ciphertext().to_vec()}, proof: Hhex{ bytes: s.zkproof().to_vec()} } + } + pub fn pack(sp: &[OutputDescription]) -> Vec { + let mut r = Vec::new(); + for s in sp { + r.push(HshieldedOutput::from_object(s)); + } + return r } } @@ -188,6 +199,16 @@ impl FromHaskell for Haction { } } +impl Haction { + pub fn pack(sp: &NonEmpty>>) -> Vec { + let mut r = Vec::new(); + for s in sp { + r.push(Haction {nf: Hhex { bytes: s.nullifier().to_bytes().to_vec()}, rk: Hhex { bytes: <[u8; 32]>::from(s.rk()).to_vec()}, cmx: Hhex{bytes: s.cmx().to_bytes().to_vec()}, eph_key: Hhex{ bytes: s.encrypted_note().epk_bytes.to_vec()}, enc_txt: Hhex {bytes: s.encrypted_note().enc_ciphertext.to_vec()}, out_txt: Hhex {bytes: s.encrypted_note().out_ciphertext.to_vec()}, cv: Hhex {bytes: s.cv_net().to_bytes().to_vec()}, auth: Hhex { bytes: <[u8; 64]>::from(s.authorization()).to_vec()}}); + } + return r + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hnote { note: u64, @@ -350,20 +371,20 @@ impl ToHaskell for HSBundle { impl HSBundle { pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { - let s = Vec::new(); - sb.authorization().binding_sig.write(s); - return HSBundle {empty: true, spends: sb.shielded_spends() , outputs: sb.shielded_outputs() , value: i64::from(sb.value_balance()) , sig: s } + let s = Cursor::new(Vec::new()); + sb.authorization().binding_sig.write(s.clone()).unwrap(); + return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s.into_inner() } } } #[derive(BorshSerialize, BorshDeserialize)] pub struct Hspend { - cv: Vec, - anchor: Vec, - nullifier: Vec, - rk: Vec, - proof: Vec, - authsig: Vec + cv: Hhex, + anchor: Hhex, + nullifier: Hhex, + rk: Hhex, + proof: Hhex, + authsig: Hhex } impl ToHaskell for Hspend { @@ -374,12 +395,16 @@ impl ToHaskell for Hspend { } impl Hspend { - pub fn pack(sp: &SpendDescription) -> Hspend { - let rk = Vec::new(); - let authsig = Vec::new(); - sp.rk().write(rk); - sp.spend_auth_sig().write(authsig); - return Hspend {cv: sp.cv().to_bytes().to_vec(), anchor: sp.anchor().to_bytes().to_vec(), nullifier: sp.nullifier().to_vec(), rk, proof: sp.zkproof().to_vec(), authsig } + pub fn pack(sp: &[SpendDescription]) -> Vec { + let mut r = Vec::new(); + for s in sp { + let rk = Cursor::new(Vec::new()); + let authsig = Cursor::new(Vec::new()); + s.rk().write(rk.clone()).unwrap(); + s.spend_auth_sig().write(authsig.clone()).unwrap(); + r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk.into_inner()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig.into_inner()}}); + } + return r } } @@ -387,10 +412,11 @@ impl Hspend { pub struct HOBundle { empty: bool, actions: Vec, - flags: Vec, + flags: Hflags, value: i64, - anchor: Vec, - auth: Vec + anchor: Hhex, + proof: Hhex, + bindingsig: Hhex } impl ToHaskell for HOBundle { @@ -402,7 +428,7 @@ impl ToHaskell for HOBundle { impl HOBundle { pub fn from_bundle(b: &OrchardBundle) -> HOBundle { - return HOBundle {actions: b.actions(), flags: b.flags(), value: b.value_balance(), anchor: b.anchor(), auth: b.authorization()} + return HOBundle {empty: false, actions: Haction::pack(b.actions()), flags: Hflags::pack(b.flags()), value: i64::from(b.value_balance()), anchor: Hhex{ bytes: b.anchor().to_bytes().to_vec()}, proof: Hhex { bytes: b.authorization().proof().as_ref().to_vec()}, bindingsig: Hhex {bytes: <[u8; 64]>::from(b.authorization().binding_signature()).to_vec()}} } } @@ -786,10 +812,10 @@ pub extern "C" fn rust_wrapper_tx_read( None => {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: false}}} , s_bundle: match sb { Some(sb1) => {HSBundle::from_bundle(sb1)}, - None => {} } + None => {HSBundle{empty: true, spends: vec![Hspend{cv:Hhex { bytes: vec![0]} , anchor:Hhex { bytes: vec![0]} , nullifier:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , proof:Hhex { bytes: vec![0]} , authsig:Hhex { bytes: vec![0]} }], outputs: vec![HshieldedOutput {cv: Hhex { bytes: vec![0]}, cmu: Hhex { bytes: vec![0]}, eph_key: Hhex { bytes: vec![0]}, enc_txt: Hhex { bytes: vec![0]}, out_txt: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]}}], value: 0, sig: vec![0]}} } , o_bundle: match ob { Some(ob1) => {HOBundle::from_bundle(ob1)}, - None => {} + None => {HOBundle{empty: true, actions: vec![Haction {nf:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , cmx:Hhex { bytes: vec![0]} , eph_key:Hhex { bytes: vec![0]} , enc_txt:Hhex { bytes: vec![0]} , out_txt:Hhex { bytes: vec![0]} , cv:Hhex { bytes: vec![0]} , auth:Hhex { bytes: vec![0]} }], flags: Hflags{ spends:false, outputs:false}, value: 0, anchor: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]} , bindingsig: Hhex { bytes: vec![0]}}} } }; marshall_to_haskell_var(&h1, out, out_len, RW); @@ -804,12 +830,8 @@ pub extern "C" fn rust_wrapper_tx_read( 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}, - s_bundle: HSBundle - {empty: true, - }, - o_bundle: HOBundle - {empty: true, - } + s_bundle: HSBundle{empty: true, spends: vec![Hspend{cv:Hhex { bytes: vec![0]} , anchor:Hhex { bytes: vec![0]} , nullifier:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , proof:Hhex { bytes: vec![0]} , authsig:Hhex { bytes: vec![0]} }], outputs: vec![HshieldedOutput {cv: Hhex { bytes: vec![0]}, cmu: Hhex { bytes: vec![0]}, eph_key: Hhex { bytes: vec![0]}, enc_txt: Hhex { bytes: vec![0]}, out_txt: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]}}], value: 0, sig: vec![0]}, + o_bundle: HOBundle{empty: true, actions: vec![Haction {nf:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , cmx:Hhex { bytes: vec![0]} , eph_key:Hhex { bytes: vec![0]} , enc_txt:Hhex { bytes: vec![0]} , out_txt:Hhex { bytes: vec![0]} , cv:Hhex { bytes: vec![0]} , auth:Hhex { bytes: vec![0]} }], flags: Hflags{ spends:false, outputs:false}, value: 0, anchor: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]} , bindingsig: Hhex { bytes: vec![0]}} }; marshall_to_haskell_var(&h0, out, out_len, RW); } @@ -971,7 +993,7 @@ pub extern "C" fn rust_wrapper_derive_orchard_spending_key( out_len: &mut usize ){ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); - let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); + let sk = SpendingKey::from_zip32_seed(&s, coin_type, u32::from(zip32::AccountId::try_from(acc_id).unwrap())); match sk { Ok(key) => { marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW); diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index ad81d41..a00b8d2 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -129,6 +129,8 @@ data Transaction = Transaction , tx_conf :: !Int , tx_expiry :: !Int , tx_transpBundle :: !(Maybe TransparentBundle) + , tx_saplingBundle :: !(Maybe SaplingBundle) + , tx_orchardBundle :: !(Maybe OrchardBundle) } deriving (Prelude.Show, Eq, Read) -- | The transparent portion of a Zcash transaction @@ -285,6 +287,8 @@ data RawZebraTx = RawZebraTx , zt_locktime :: !Word32 , zt_expiry :: !Word32 , zt_tBundle :: !RawTBundle + , zt_sBundle :: !RawSBundle + , zt_oBundle :: !RawOBundle } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -301,6 +305,78 @@ data RawTBundle = RawTBundle deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle +-- | Type for a raw deserialized Zebra Sapling bundle +data RawSBundle = RawSBundle + { zsb_empty :: !Bool + , zsb_spends :: ![ShieldedSpend] + , zsb_outputs :: ![ShieldedOutput] + , zsb_value :: !Int64 + , zsb_sig :: !HexString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawSBundle + +data SaplingBundle = SaplingBundle + { sbSpends :: ![ShieldedSpend] + , sbOutputs :: ![ShieldedOutput] + , sbValue :: !Int64 + , sbSig :: !HexString + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) + +fromRawSBundle :: RawSBundle -> Maybe SaplingBundle +fromRawSBundle b = + if zsb_empty b + then Nothing + else Just $ + SaplingBundle (zsb_spends b) (zsb_outputs b) (zsb_value b) (zsb_sig b) + +-- | Type for a raw deseralized Zebra Orchard bundle +data RawOBundle = RawOBundle + { zob_empty :: !Bool + , zob_actions :: ![OrchardAction] + , zob_flags :: !OrchardFlags + , zob_value :: !Int64 + , zob_anchor :: !HexString + , zob_proof :: !HexString + , zob_sig :: !HexString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawOBundle + +-- | Type for an Orchard Bundle +data OrchardBundle = OrchardBundle + { obActions :: ![OrchardAction] + , obFlags :: !OrchardFlags + , obValue :: !Int64 + , obAnchor :: !HexString + , obProof :: !HexString + , obSig :: !HexString + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) + +fromRawOBundle :: RawOBundle -> Maybe OrchardBundle +fromRawOBundle b = + if zob_empty b + then Nothing + else Just $ + OrchardBundle + (zob_actions b) + (zob_flags b) + (zob_value b) + (zob_anchor b) + (zob_proof b) + (zob_sig b) + +-- | Type for the Orchard bundle flags +data OrchardFlags = OrchardFlags + { of_spends :: !Bool + , of_outputs :: !Bool + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardFlags + -- | Type for the response from the `zebrad` RPC method `getinfo` data ZebraGetInfo = ZebraGetInfo { zgi_build :: !T.Text @@ -427,7 +503,7 @@ data ShieldedOutput = ShieldedOutput , s_encCipherText :: !HexString -- ^ The output note encrypted to the recipient , s_outCipherText :: !HexString -- ^ A ciphertext enabling the sender to recover the output note , s_proof :: !HexString -- ^ Zero-knowledge proof using the Sapling Output circuit - } deriving stock (Eq, Prelude.Show, GHC.Generic) + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput @@ -482,10 +558,10 @@ data RawUA = RawUA -- | Type to represent a Unified Full Viewing Key data UnifiedFullViewingKey = UnifiedFullViewingKey - { net :: Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@. - , o_key :: BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) - , s_key :: BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) - , t_key :: BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316) + { net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@. + , o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) + , s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) + , t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316) } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -493,14 +569,14 @@ data UnifiedFullViewingKey = UnifiedFullViewingKey -- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf) data OrchardAction = OrchardAction - { nf :: HexString -- ^ The nullifier of the input note - , rk :: HexString -- ^ The randomized validating key for @auth@ - , cmx :: HexString -- ^ The x-coordinate of the note commitment for the output note - , eph_key :: HexString -- ^ An encoding of an ephemeral Pallas public key - , enc_ciphertext :: HexString -- ^ The output note encrypted to the recipient - , out_ciphertext :: HexString -- ^ A ciphertext enabling the sender to recover the output note - , cv :: HexString -- ^ A value commitment to the net value of the input note minus the output note - , auth :: HexString -- ^ A signature authorizing the spend in this Action + { nf :: !HexString -- ^ The nullifier of the input note + , rk :: !HexString -- ^ The randomized validating key for @auth@ + , cmx :: !HexString -- ^ The x-coordinate of the note commitment for the output note + , eph_key :: !HexString -- ^ An encoding of an ephemeral Pallas public key + , enc_ciphertext :: !HexString -- ^ The output note encrypted to the recipient + , out_ciphertext :: !HexString -- ^ A ciphertext enabling the sender to recover the output note + , cv :: !HexString -- ^ A value commitment to the net value of the input note minus the output note + , auth :: !HexString -- ^ A signature authorizing the spend in this Action } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) @@ -521,9 +597,9 @@ instance FromJSON OrchardAction where -- | 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. + { 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) diff --git a/test/Spec.hs b/test/Spec.hs index 04f5678..8ec7d42 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -79,6 +79,8 @@ import ZcashHaskell.Types , ZcashNet(..) , ZebraTxResponse(..) , decodeHexText + , fromRawOBundle + , fromRawSBundle , fromRawTBundle , getValue ) @@ -774,26 +776,42 @@ main = do let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk) fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr describe "Zebra response processing: " $ do - it "Raw transaction from faucet" $ do + describe "Raw transaction from faucet" $ do let h = hexString "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" let t = readZebraTransaction h - case t of - Nothing -> assertFailure "Couldn't decode" - Just t' -> do - let tb = zt_tBundle t' - fromRawTBundle tb `shouldNotBe` Nothing - it "Raw transaction" $ do + it "Transparent component is read" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let tb = zt_tBundle t' + let myTb = fromRawTBundle tb + print myTb + myTb `shouldNotBe` Nothing + it "Sapling component is read" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let sb = zt_sBundle t' + fromRawSBundle sb `shouldNotBe` Nothing + describe "Raw transaction with Orchard" $ do let h = hexString - "0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603204c2a010cffffffff0480b2e60e000000001976a914278aff0c0f8734638ce81aaef4ab0afddd36552888ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000204c2a000000000000000000000000" + "050000800a27a726b4d0d6c200000000ff8e210000000001146cc65bd6d252d09b8eb0a8ab0aab6d7a798325aefc1d3032fc6d31373a85a25a3a16b447a698f720ade1bc290a74d85574b5b20515391035a67f8d5883dc65ea3ba4a17b009d6f325d41072b3ce240270959a7ffd040e5f16c697d8148973c62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d23301c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668f0d8ffffffffffff9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900c734b62e56792f6c8bed48e4f108a817e83d64d6a59e38cfdb55c0f8a89bc7507c89326266f7ac03a3941f448cb879bd792bb116d0be8876c0856a76ddec0f0c02e16f0338626013ee5f6037fc6a3c69fa291204039d04d17c11295ee3024aea8f5d381e9b7eb3f938b6f9182bf4f889f1e53e30f998b1cdd23f45cfaa20aaef058248cc2e1c487fcdf54a4bc22a68a17cb6fa7b2fbf333b99feb84643d321398b675634929602126b2fb40171e514769bf82f18c267ce9cda0c24300caa9a5a361144d3b7b9ab2243ee9811d9b2e72c8bb1d145cdfcf6b29994a969b41c47208f5dba8d6d871e490e9b970afec4d8bca40ba51825cdc78cc7cde6b6f235a4105b1d1b5e2765efd753095ce770f070b02cce3316721b9345680c146c2f428c0bbca90d5a8cd0a1c4c31cbfa8ec165ea9f9c71d2d05e3cf8bae5e779786f179c45a3cd8087d820cae812aded04f8acda9068af80ea834f79f1bd03bfd66f8a19074649a85ce877df1a621a867debb423ec0d19015b326fcf6f143aba34029c1da2fc7b099378a366c38c9609ef6a9d9e175e21b0c1ab94a84e28ee7f1a00e39cb6fb59f44e4567e9f85f8f98158263c52ec433c042397c784edb07c28d2bca036f59090e819157375d610acb1993a4107b48da13a371f5383429baee209b2c0cc150fcef79a042749668ba1f89ad24a8c746142191ed0e8fd63624a331d98d50daa84ccf9043076947cf5115b9f8787acd36000c5e72c8d783b29bb28a3e46036d0a592ce8a158ee5a7ac210be72d3a6185c13645d96a8446021b64043ab8b589a20091c152e7d5a993ba94770eea988e289e1536d0d81dbc7046ca9c6d918446bf970894f073c920006681ccf6d1a3f138519c68eba0296069e42dc60f2bcd0f17c400efe4f4e87de8606606dc4fdf31494df4d454d14a440b1d9db4265c7aa9bc8683c68cb149f2cc826427575e2af82e842199a9cb9fdc7243b3bc12f1a71c37eac5cf88ba830cb95728897fa4c177a290d6b2b3814173262da14db9b4ef39fc54f888a6ffef4221ae672fb03bc78ebef479360a682ddb12ea0369a428a6c2960ff8327e9a2f5e5d98ce1eae748db8f6a4631c789b4d751d6b99c97c149a813998d44a7b57ba06c8bcb8a6c73c6388cdcfeb1346cec8fee7bdebf2a2388d9722183eb2d2e0e183cdd092152ef640880f4514f3c5e836cc3a8249413500630aa8da85f9e3cd92bdadbb69a2bab8d71f0b3ec5832a7ddbddd67b34c33b2e12a0c8468e852e4a8f7df45657e9632088aa7c6c5048a2686019cfec33b27fc88e23759938dd55a5dff589c1c21a37da617609e9d8be37dbf9bd6e84ee160fe10268171d969e4611afe9d3482ed4b132dcdd11ee516f36d512a333da20266fd984caebf4937fdfd18ed07b4a45771cf5c8c16c6b258b289a07d136a22acc766011f366c420bafb8fc1a10e42219bede5a3d1166c525491ab60bbd1f973fd3fb2e94cea888e24d5fb0adce51faeda75d62de70094d4b36d38d03cd824d284fad577c3ead4d98bcc8ceccd18174a889b22380bfcc12656e764ea0b8fe1409971283008ed02cbef89d6f544c62c3b001bfe96723fda9190deecba534d69cfa358036fdaf16127b89f925c52d4e750919ffb7182b6a8ad13d0a8e00e0b906978dd24ee11869c1a63837a80e46e1216e2e273aba07aa5b0d97558db0ba7f9ac4c89403c65f1719394e479311f5cf84746e6be6f1abcac03194aa8bf1735811198b5df90dd6cac345779c185c24beda0101b932048dc4144af664a63acc0c395052882ee1f18bd0ddf13bb583861923bc00ed5ae815b964698ca097eda1c4281e039139fa3091890244f926cc4ab773ca8a35d5263d3bb48fd6ac53a4bb4d7d60b36446dbc714c35b5e13a17c5b0c70f67207839d1f7404604aff63b2fa83a4da7dac92aac96b3f250412f8d04a9e298004313b02edefd076c67d8a1316355777814e2e1ab03690e426b672d32ff65c03c592ecce6a70e34fea2e15b9a6b4fd092d027199caf27e84e25c09380b38a5eb8985355b3259aa1d94be74269b84f953053b02ba3be9df872ae5fb2d893188575bdfe222ba267b5461a0d0be274a7d9e6ee51490d98e4cd97978804c4f0f8e9f4908fd8c102b01080f5a02b7578591e95d60f3f56d8e48514b1ce7ea6894f55a32c8ac8564985d18c6b82f8dcde5b315624e9321bdd49dd350c87907cc373c0238a79321e6250e38a0ceb2c060ecee6708c11cb30a49687da9923bcdf011f9aca27e6eb5a8477a2bae2dcff9884cc2349b51a66b5179ed2d8f69e4bbba74c694194e83d04a8566228227eb732a95180c6788483d1f259d52c52fe43357656d50a1cf2902c3124d60d15fc85f0447a1203f824c1106452cfec1c92b18de003f82a0000000000001cbd27436a221a53d08c4838831d1bc60ff7e93df41a51412ef6096eec98bb28fd601c53a5371b23a497062635b5cdde715c23840d37f1cf328f0a2ba96260357689ae3f84a80dbdca1520df68513be1285177d3c0da664c64944de78d8b8d5864f5ac15444cd3204adc4fe487503066c18fbbef8d0515248b0a97577f5aea1d255788ed4bb66d4d56303efe135063392c312b4671963daa20e0ade262984e11263a1588eba3cf829e6131ab506e6a850aacce603e8ecfd6e794c90a772603d80fd2aad6027b34854072a0d23079252adb1ba637bbc650ed4afd35d977e1498d998020bc1c814718b48ba7378a92c56827d3c2f20daa231fa51f0a9188520e2a11149e162489f0d6dbd27cf94fd5775311d3dfbcfeb431bafc3515bbb8c4ba4488c320dca0dfec548fe9f46d8810b3f6b16bb3e3eb0ea130747d3d127c5953ca8d561f8d425a35dc3f2cd831743139fbdcada42308b524313782e23b32d5d54a265eae408623e3b2779fe60e13cf47d54dfe520f9f4e57c68aed31f78629a9074d72ab87bea993a38f95ab40df3ef01735e7d44ad365a786e0d3032f1c1dc4e6839c974185dbe63f8725e79831ebe269f94c96705639ab38d5d0700da04c6a9f686e1ea13391885287ba43cf3ccef1c2227918f15ed55441c45adca84153530bbfea3cf37adbf84831a2bfcbf0ca4a4bbd90e623789fe993dc17503ec11b1ef3049f27b27ff778af364d634a46165cda1dd8241cb88740bce74a73e7e3d656df2dee05bb561a85e64671b191ec802c5bfaca49b8168e44271cf13df756395896ff41a99654f55b6951f20d04b2007938a420218db8e37445ef3267130e288e3270b13a92596a26043e1ae84f3934cdb13363bc2843f74a0f6608a36b52c985132aa427c56b7275a864b3c76502c37b8abb8d0286b3199c78492ba8103f5a23c6cdca2292c75d7d6d7080108850807f78af3dc7e418371c6b8951bd89b79fa586af4e16096b08ac1f4dc2b1e4feaa5c040bb002b57311523197b6e2bef5b79ac9c9b4a339be6f6bf7fbe9b5c93862c87be6647949c70bb2c7e268e2ab39cbadd69de628376b3af744eeabc85b599bbdd09defacefa443e05c9b5f259a7783743fecf1a749c57cacc85703269ed67db1d8d475f6fe25d66f84a77379411ba123d98fcb3ae4eec306489a08372893616a91268ea6bf34ddbf0fdef1360ab9e82f4ac80a24e41f439af06fadc223c61f445b7261eda5e1320e269d1277631ee2245cf930244bf8c04050c514e2d59035b80827586cbfeb7da7a59c1208aa86390b9dc7a9b6ef38879ba4deea5eef47c5c98d9167594cd730abdfaf082090efe759d1b13199d739c112ae324ba24b275bf1d89867b81f4580a7ea3a8d3d07b45e2de6c1c7099de3606873b13f3083ecd1e84456c9a1b1d358075c68b1a7cf0b1f26031a2909e226f5da7877d0085b879165ec4b1d9abb7b0732ab4a6f22d9a7bbd0d494ef3f9af4903dc733fe92c6b2f557d1406d223a93e8ad6e579ebcde9c39a5652ad31335df924e5b6a09a0191821b4a0c8f886e2d7860b75ae79ad9dfbebf3500c8b9762dcd131eb5c8b866b5efb4fbfdcc5e31605c2b7d2ff8db5198a6c41bcf880065ff232ff8f84ca3f8022d3428359dc9fb19f57a6ad3f3d174d8a348879a754b37095f01d9a7f6f873798b97dfc5d7c7eaf0383b3fdccdcc11b30dbb3a0fe3186a36c4ddc9674624e38a81cca60a9bbc1b124021b61a383b7547d6af187022c133ba9d6dadf711a3af3b0255b859b214ef6c5dec592248fc94339a64f19196ca0fdad80f7f8e3d78b1f783b1f038008d0d106bd86e23e33ae5728872d42a555bb36d0e3303f0b4ab41180f4251590ee3ee244b77191c31b9f3f990f71c6e237b9dbcf7ca21c9b4c2446b856c67861785bb9edb920b8f530a7a088313ef044419a879f26db137e1557d079315844f9f60bae03d8cffa7a28bd2857a001fd5d2d999fca95ff91df0e228567f6c9ff592b77b7ccdc93a951f7e34910361a8f4fb517e1c9fb956a3bb50ddf37ed37e8d26adfc0f71e059ba95ec77a1e34e1b3420c893f89b79fc72e3a1d864fd35526727f939badc29740f5ef9c0bb9a3f72e1e08b2ef2ab366f80d8e14e03e92162736e2ccb7cde82b2af08de8a6a81c03c63e2396974ec29fb122d818a2d2d5b29d11b704d3ac3b39431099b7b2f6aae04d28a2182b55380503127e4986ee9e8d5c0c2058e09e4592d08d013a4f088e45403720160622236bd56fbd9cc240efceb1b23c19ceafec49e9d5776ca9da5f7810ac979cf6ec5f678c09257abf79c9ab55dea00054e11b62c0a0ed4363d0a96a37ae1a323aa93bb1af253885afb684ed30caf5cf3b37afe6a6463a16f34cc28b4530c6bc6281f597bea476cd9a773205b96d47ed4b0bfeeee39b7ea44ff194911bcddbb161c2c0ce9488978b99e880d8e43624dbb4a567483ed293348d752634b2f46219575175e3c249b8e4e853142b66491aa1c142e7bb558955747cf2bd61ac802a2a4784d9fe080f771dd537d0ff928b3b04029d9ac03175c2d535ad7f3c123eff30c0437b32dd9fa31b2976e369b89b79a2a95e31aee15a462c5fe25fd937ce6b0795808d16163f2cea8f19b7c83913cb4a793576aa5c0ddfb6415326d8f2be0621017616c85ee46aa768b077dbec72311cb8a0f78ba0621a277a79af6607063839a52c6825a20e1c5818b24628862aff5fbbfe87311866b9956eeea7412ef69a3e4da84699b8d8b45c74ac96c3356989fd4962ba79fc26a92488304fd9f42486266b433eeb57368d26ae91ba4e7ed812581f790314cc7f44639aa7f6775618111369e8e2d68a6ab24388824bbbe3c3b0fbfb88635c1fc3216af1af40eba3555c0b0390a18ccba9e68afbcd21ecb212aaf82846f0a55793945902c48cac5d19332e23248a464529f4cca177137c508b6b13637e7df523254f24b8343d19164174202bcb00d5fe618b760c374f69b5065b1f9acf91af95abd7eb271586cae14fc835f633aacb4cd2ecd0f0ac08b688ef4d13b8a7b4c487ad46485bde0e340309672dc38af275e6ab525971409a39eff0ad134b1674db5d1f9725874e36d8730dc034b0a596c6e0a26e521c199d3e3f86815a64d148ffc394290b19f15390934b5d0da27dc8365360511628b93ebc375d2a531e4f4cfa031eeb501afe96d201c7b6078bbceb8e5d8615599f4c613bbb81a88f4eeaa57a9c008125073a30154044c422eaf2a32cecb15aab0774bb44e52b1792d154b8036ff9224af53e023fc011ab251d47d7d76e55c5015db1926d43c56d055feca1259de53ce98a2faecb5843ce17a3e83ccf678f5d13a8321f278a670c684e62b720e1eefd0abb2e9d0a3ddea81d5eb6e380a1c22af5587daac852e93a86f5e4293c18bb26b32035c7e5ca20cd2e3eabd3de0e55092a79a42c7ccc0aef033d6683043c2a29531a2ef1f503595c0e464ac042153c4685b062275f88bab93cc017f1ef9dc6f8aeab7b0b234f1c543420324512554f1786c82b37836238a4dfdf86f97d09ae466eb39d5f3ab159e2060be309d1284be133ab40abd61468c1706f7f9e57a7bc747479693a03edc8863dd196fd7cb2721260e42f4f606389c4972c74d357e7467b61cb7f455562015d29a59c7cafe0df03f26b77bca81c2bbac8cbccf8a65190b0c4e5ca832e82ce4e11044433aa397106cafc05634ad778270d20d8a13068586bc6b582ea24524fd921a5ee22dbae5296ee86d80f12b78bba26f8b42c1401b75d5cfcc4775c5cd1cb0a9248dabc8f82d216787b2a2780f7d13a5ee8c6ab56399a8dd5db3a152677d01eb8ee98d1927ca5069e0d1ba3907971a2199ba3634b48e570dd97d93729a6c43e4f359e2d89795218d52270a338a1f511b1f008cd04553c1a89caf987fd18c329be7ac2282084ef1789615d7eb7afd2261f606d3953b8863abe57796289e7761b01c3ca0faf2291287f9ad7027d7f0876b5f77d2a7f87dfa6ad4db905d4bcad042f403824aee3b4f8d7b5ab027fe1eda9d683db24f56f694b0b10ec72ba0df40bdd6e52b4a7b8d064ad46c7490c4705c14b06ef55222435d2d6316c7dfee83d225eaa431c11a4b85b0bafbe66fda1abadaec8eabcc2f8c688a7b9cb2942597f20cfafd7520892c535bbf6359a6989a84dd89989d95b8e5222c3aca2e8b0f8881d759e450466b75b5f36b7b723b0a212edf52abd591e7e545a3974b8b31b84b523af7b47e3804b5a268c86ae0bd7c80bc6b578b79f749eccdb4a00813925ef40259ac10bbd0fc4f2fc536c30f7c1efe68a52bee22f57021d23f445211b36fee6302202c9b62c6cf9064a2df424563f9f805e51c4092482253e4c258d53b80a2d26eec9fcdc104f7124d876a3ad573a7f419b0d67a41a34dac9d8f28cf9519b9c2677c9d1e720667d5fce26091d64c6df8b46c98b58017de0d055651e8caa3a230f57aef214ae2f27fa85400e34ce7087538fb6b854a6ad534780052210b8b8c90b4de4c2afbad9f58a71770ec186cfa44b61b53876bf904972078673845ec3181caaccf11f71a8e2a502deaf144f16df1df3bd81277dfbf6e1ae5a17363725ec31759b743066fbd4cafb5eda6e09418bc375f42e0dcbd4624dbe26c5fabb77152124f400e677004fcf3c862a9b5576140cbeb800fcde4409caae06286cb643842687bc6b89738374c7c759c911d7bbec8613f1fdd996d4b970fb6f2a9290a84c34d5fea6b8006357c8e6e9d4048d5a8ec476dab0b55e8358ecf7d27c31da86681f3fa74d072b1150223eaa21c4027378a99c8a2dbdc07d4062c401e92eddfff82841292bda2798c4e2ee9e09f618cc181c91a4dbefa44c410dff5cb705ee005e3a0470c13baddf9066109797a3b51e73a0ef229796d330aee0c0160529a4ca3b39e861ee5c4a0f78619007ecdf32266c7c42f0c972b91cccc45f6f8688b2692f298721cb5da39cbca9a5adef6969a2592ba421680241f8a5384bb92e70acf79c2f41d1171dfc6e1939887b9c8ea94429bcbf3532919fdcfcd0f443d3c95515b41e3f9c84bdcd3de1fd481f98482f667f2d017e3579208341e9a225f85516c8ca133cbada77598b6f596e6151eca377fcc8029cc99a879b26d975684173c0874509117ece4136bd2d69848f858c05e8ba3421499d7fb5e3e7645fc135117d8fdd1dc46bebaedfbad4dc7cc23fad6e696fe349712cf7579b4e63b38cc7d02a4c6a33ee4117d7ccdb86ea02cd791756b2a3c516d59d39ed83a8c328823f1934731820c187624219b487ca86edcc2f61a064e4e8d17f58f4a71462f3f0cfef6be95c0eb3737616de5954096d761a51534b36d798c651541acfe2e5bed6e58c45c46e014923a342409d49e782054f2a4877332e0adb0663dce0e84df0ff0d71c4c5ef18d9cacc8b8d47c78d53fc7649bab719334601c79a345d2101e65d4c3f1616741b24fdd9f7d6569fb8de67799648b323856c2ca96ac91823aa12249934c7050505258c43763ac9b174d55fee71c7817d65056c30d7853500656040621eff9f291e9db198772451cd3d58bc9421f6191a863777edf49125543b22ba127ea3063e1989bc4635d50bb22270948ba594b2066d91e1589aa0c1f476af8b170820ae0d2409df83819579621161ba55cc8d020e1ea68cedbccc9a737746d824b06e12cd2e3730260fdc52a68b6e142bdb997d8f93f25d241360285e7372939828fd54c015ba90bb4d553558ffa2558a43098644357860785addc455bf1ef4ffa5aebb8f002eb0f6a8ddd6f45edcc7cfe88d7ae7141b9113a4ab851b5fa8ff39c7024d2b3202841266f256b1f4ce4e4ea8b83d0067555ddd56fb1f8ed6cb5ce7340cdc403fac1dd3b48c373629202c70f1f95c0001b4f94a9b4bad9e1c64429b541ff57a2f2ea0c1fb2af8f0e490fac415cc3bcdd5739f2069e2bfb873206b8c8811619f1db39386cd2dd748c6301239b4e873f3d4618a43c52ef7d8abc0ca127881dc8809dc20a8730718c64c5a132cf638ec1e3b3cedaae55763637b4c6341baeff17e14c1e3c34e5d23befdddec0c7e25f0ebfa90a0bca07a0f5446507b7fb9cf91cb3cb8d24a111f46e7dbe3ea62b384eebecaf8de49b15b17f9cf151c4c3d33220b3adcf9f87222813ff2125120e77c638ab19c1daf9631d53b3efee67c3c40c9c23495848d4c5ecafc0998bf2704bb78ade666f414e9c6bb100b90451d93397d062b741a3ef20e7e6ebebab8238e2adf415775179c866eb4a2f628924674b20f711d2515d3b024e7e0fd3af6e116cfb0030709fb9bed4a441646c03dfa0b11cb460415ccb3c0999b738a0b07d55be741f3d2646a3d9e22bbfeb46b87acb6fcc2d83cdf44b656d2269a0734259a738d83a7429bfc99da7238b9497fbb10e0f623dc1f793e3be2326a7e55677e20ebb152f71794fd70d7698d0cff1334341b3a8eee38d66a021cae6f8dfe67913fdeb8d46efd9b77f28950aee7ce112f8f0dfc04132a99d95dfa923d391e06a578056ec8d15ddfb0fe8cd2790ed55f8254c6351d471c7273c2e61c2694bf6b0a1173cdc2d422246ca5cc51a9b8669dc331fe33bfe330d5167a244cf041f8444775562eaa8ddb92e0623d4c689db0919ece87a7feeb1ddd2d034a0d884d6bc8b5fa5fc7df00cf667dfc82c9e98e9b57fbe07fed391729034c467b46d3d16ed72c8ded069a117ca2d28e71ef4c37f1b1939590af19433ff2a784ff38134aacc59719673c53ed047d79e0c36f3acc03f64458826107853ff8d02de9362ffada79feabc597d019de6afb6577dbbefcaeaee801a49b1718151690b43a4d70b2fcb73ece3e3ea5fe2a9616cc6a9a494b8c6c7121497688652eb854e7bae19af3e5ae1a8df75138ce18621d11f15054ac8971f6ac084bd90e3909b46db321916c9e3c8950f3cc8e0a10dd255bd6b359c73dfde7de6e01538d03609253968177af14d327337bc1bc4e866c75ab5b69125f9e9a758f061418110a615b97f733f899382e2a14fa0ff0d1bda3b7449e1f54362697aef064fc9dd50692ad2b9ec286a1b7c1f845b09f4b1ccd46142f9e272d018eabf76c23c26b1831762a8a2c9152ef0cc843ebba20bf979e851e76141f41e817cca0ac60c1baf239a6c920a244b0be031a3b65d76eebae15bd9d49c53dd3e4303ca379f26840901f1da886d330d8ab4408a25eaa280ddb545b968b4aa59ca6f9cecbf82984264a3232b97498be0729f3d97dbd64823393b42794ec1676244212af382c37d53d66019a5db34c3d8ac156cac852ecb1c57f0f6057815460eb9f666c368acca6dfee0d2b7bdacd2148d74cea3bbac8b528f00809f403b1964b9d199d2db93363a53732efd97c66f28cb51442dbfb1c912dce078944e02889f9395ff2655e2d7118a11848e3ca64efb5eca119240b537b5d4548a744d96962e9b2a99af73bc4d753df16fe75a08005da7773b7bef68a44f56951a3a93995c5d0516a8b2c39abd2057b3d86ba7fe511dc33292596fcc4d3c67df88812e26d3628527752a81c8ce6eba711ae7ea8748229caa2580e18937f45cbfb0041f0a9254eade41c95ef4f390db6174e6b0ba89af344c215393b627348ebf00901e343c1599c832bd2cfb751750808719f18119ab3fee6baf5fd4b8bf7c92a271ff24576984610abaf1b2297c18a2784f57d7aad26f52c0d60fa6cd2484492a1524dd14924eebf53348ee2dca0643ef47381541b91d1843817067a94bb7c79e4e08c4d81c7266afefeda4a0db21b0db629cedf13b4f6ba11e6480116a7c9780009ee0e1e2bd543dbb68bea356a6010a1e3ded4229c4d8035420833fc83337dce4bc483eec76f479f33be2d6a8fb013813952eb66bc247f37f9def7e9bea0f277f380079aeb48c192b9c2c24f909435586bcb994ae6f75eea111c8842b855f90df21e956f839aef89bb1b96899e343cc66814ac08eed4defe5e2a62c54cf83a27f0499224e02bac652aebe6529143372c483edcdbfbdd5a02106fd3651875f23724d2154c0138ace745d038d6cf6b233ae2bbb8313c8ab59dc896844da5a6610845d9147e57518ada61b7debcd66111d609587632cccb936751d099639735f09f77b2b9aa1177e95ac0f8070bcbeb01bbfb3e7a638811d919016ed628865e2405aa17f5129ea43a836a76ec7cbd943e20b1e388d47db2df3394785e27dd7efbe350ccec6057af7483303a6a588d4005c05ff7f3c90dc5b9a0f95c3c541ea40615f6b0ed6cd3787e608e7612b52199062991a63511b3055dfab25b18ab55948d9d529aff91a0d8aa7705afc187e32d1107c07623a309390f32c62e1abb830bb090a7dc4c6817b554720fce354563662d78cc2bcd0483149b304ab86502f0ad36ba2c262fa60b1b84be2499e56fd72129c0fdf1961214df8dd6c9b831109fadc7464df090aac96d221ab387b6eee3080ea0d94e195ca795b5d80c42946c0c353e39075bb1aa4f3fac36a77445cd57b11b3c7ef0e89aca911be35fbb72e1e2fff84b7ece66bb8b3dea52b0aff473475d298f730e79ff77e955856817514908baaec1b53f39f9d5dcbb1cef29536a483a1845d8538e5ec43f634914bae4f2319579adcf3f0fbb151c80b54f02be54be612f9417f916579863dc16da5af07fab05db2094c77612b445b7b9784198abbfc8387486449969118d3a83142022017d69b939d30fd6b1c5ba360c0e37f264b5d604c7ebdaf327874b22d4092dcf35aad93f0f0b3037f90000d4a1892dab827d3c2f1ddede948d330706ef17bf442f8228c168401541ea89a837f81cfffde8480e1b6014f2f5d3132ee740e04622600ad93dba41e39267f17554b29102390f8196094de19e8d966d3c03a9e3348494b54045629224612734a1304ed76d17a465c305675bf99b7aff1950dd2012e6cbe8d15a9e35a7c16081bf647e2766c7b8e9d12ef8a2c94fd7531f6d1a7ac7ce6bac028917a2a71c8eded4f3ce3be8c7d98422094e88414338fc25839a582b5965c71179ce3ddf7a645bb163f3265f4bc4ebe91ff25d86520696d237741ba73084070e23494d6a890e1ae748091a6936a3137d16dd14571f86ee118bcb17c8ecd2ed20c25cbab79a78c53fba1d5db4fdb15e6a124bdb2d41fef080e7318f78c67451beff04b58a182f592fec3feab6011be4663dca8b62ad2a8e40c716b3c8a170e1574517c44e146109d223529b0de480aa168b5a4b0522cea03dd26a684b08c58aa2e1fe41f44ba8837737a74a4681b0508d9604aa7484c5ec68ccb369c056702b03f75722033597ce5b55e5a88f14e3ee23b1c96f5d38604128cf2e901a745996f4a29d412e5ce1b4ee82cf2f248aa80f8c8f2eb7b6951f1b0910857a17edc319bc76b1a59fe05f9394a5b16a91ef15afba9ee2e115c9c9afc51a8d7c4a61cb335724855007f564fec341342d53717023118917304c2dfcf0bd79e456c94718b8388767564d6055aad1d505f36506fd6e8cb1a10f6fe8a67e772030dab78b43ec15fae0322fe991944be86cd0bd78ba00110474dec84dd9597a602c4a6c97a27f13159ff1e32fa63e28f63278b09c9d051cabf8bb49f2780d4c33ca061ff16c6288ff29d46a22e31c719dfaf6087590ea8197c066f9057e0c24b40f5eb205b730985daaf7e7d85fecd7449677c0ec1abd7111d795de6de4a6ab30cf266987dd55569f2a705ae16f51de738fcc4cc218f2cb9dab8af509ba26ed93ddc7a73028d9187527fa6d554b491faade05852eb50847affb81b58d2501989d5b4a9de4237b33bea154c4d016b08f3e1c39ee61382fb500be2123ccfcd30e37c88979a19d636c9dcf5fcec822e5558cb2939e40e1af039aa942b503c9fce95f8ff51a2977f58e4d5032f9099fb4783b7764dd1299a1a89f21c1f5e38038f219e9a4e9d6c6edaef70132ee1f2a580227b256c9139d339183e278277a9dd260bf7d8a9853c4185f9b6509ab9b67a3563ed0b9afca6f2db28258025d8c2036cc4cd9fb4f7007dca96597c26476def29191cb0c77c44df80994ad3c79833820ce2f1108d17a083fa857397d2839c37899ce2e634578b3eee0135f66cfe1aeb7bcb8732fedba16093a6e666321236f56e285058d00c1bba2db3263844e4b0ea013e7a8d370ecae9ac7a9620eaaa244b25be1eb6e71368f550736d3ca13747edfb6254c9a373aadcc30a148907d39e81bdda8a53e8644b39a1625de851fc5d759686c63c76bb162b244415175eb0ccaf3a63d4ad910369e30313bdeb175ace63ad33e400e642dbdcb472a336d82d2f818e6888e5aef472d03fd298a5d14b8da432b72e6c5f5694887a7e4e165bc41055379bd204298d65796d5351a9121f949904bd6c87510649127ef5797d432c6c759939c99026d57076db225d7d0df01ab9b9e3e1221e7e323ff752eb7988cd2b2bbfc8ae7fdd23c3f7cfd2ae2679014e986e3c01b6c66a67fbf69483ad3e89c1f2dd0867f411151e048d81212c0b5e2fbba9d317f283f97c9576180121f41856355b906a22c69869bc27729db5974c5b1257b35d186dcb4e9f7bc51dc331c0ff9baed5955e5052a6ac7743b" let t = readZebraTransaction h - case t of - Nothing -> assertFailure "Couldn't decode" - Just t' -> do - let tb = zt_tBundle t' - fromRawTBundle tb `shouldNotBe` Nothing + it "Sapling component is read" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let sb = zt_sBundle t' + fromRawSBundle sb `shouldNotBe` Nothing + it "Orchard component is read" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let ob = zt_oBundle t' + fromRawOBundle ob `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index aeeb2ca..c385e5c 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.2.0 +version: 0.5.3.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 2f18c15c868c665a8c9dfad208a846b02d04ba8f Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 2 Apr 2024 13:11:59 -0500 Subject: [PATCH 095/149] Add test to identify transparent outputs --- test/Spec.hs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/Spec.hs b/test/Spec.hs index 9449996..70bf04a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -33,7 +33,12 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as E import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO +import Data.Word +import Foreign.C.Types import GHC.Float.RealFracMethods (properFractionDoubleInteger) +import Haskoin.Crypto.Hash (ripemd160) +import Haskoin.Crypto.Keys.Extended +import Haskoin.Transaction.Common import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck @@ -73,6 +78,7 @@ import ZcashHaskell.Types , ShieldedOutput(..) , ToBytes(..) , TransparentAddress(..) + , TransparentBundle(..) , TransparentType(..) , UnifiedAddress(..) , UnifiedFullViewingKey(..) @@ -86,10 +92,6 @@ import ZcashHaskell.Types ) import ZcashHaskell.Utils -import Data.Word -import Foreign.C.Types -import Haskoin.Crypto.Keys.Extended - m2bs :: Maybe BS.ByteString -> BS.ByteString m2bs x = fromMaybe "" x @@ -785,13 +787,23 @@ main = do hexString "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" let t = readZebraTransaction h - it "Transparent component is read" $ do + it "Transparent output is identified" $ do case t of Nothing -> assertFailure "Couldn't decode" Just t' -> do + let a = + "utest1hhggl4nxfdx63evps6r7qz50cgacgtdpt9k7dl0734w63zn5qmrp6c2xdv9rkqyfkj6kgau4kz48xtm80e67l534qp02teqq86zuzetxql6z5v32yglg9n2un5zsu0hwcvaunzdfg5qnry6syh2dh9x8eu27de03j9pjfvrqda6acgtc6f0emdfh6r5jvfanmjml4ms5wwj9wfqmamq" + let a' = isValidUnifiedAddress a let tb = zt_tBundle t' let myTb = fromRawTBundle tb - print myTb + case a' of + Nothing -> assertFailure "Couldn't read address" + Just addr -> do + case myTb of + Nothing -> assertFailure "Couldn't read transparent bundle" + Just myTb' -> + show ((\(TxOut v s) -> s) (head (tb_vout myTb'))) `shouldBe` + maybe "" (show . ta_bytes) (t_rec addr) myTb `shouldNotBe` Nothing it "Sapling component is read" $ do case t of From 938ccb4b9730fd8615513eb27bdbffacd62e29cc Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 2 Apr 2024 19:03:50 +0000 Subject: [PATCH 096/149] Add test to identify transparent outputs (#50) Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/50 Co-authored-by: Rene Vergara Co-committed-by: Rene Vergara --- test/Spec.hs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/Spec.hs b/test/Spec.hs index 9449996..70bf04a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -33,7 +33,12 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as E import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO +import Data.Word +import Foreign.C.Types import GHC.Float.RealFracMethods (properFractionDoubleInteger) +import Haskoin.Crypto.Hash (ripemd160) +import Haskoin.Crypto.Keys.Extended +import Haskoin.Transaction.Common import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck @@ -73,6 +78,7 @@ import ZcashHaskell.Types , ShieldedOutput(..) , ToBytes(..) , TransparentAddress(..) + , TransparentBundle(..) , TransparentType(..) , UnifiedAddress(..) , UnifiedFullViewingKey(..) @@ -86,10 +92,6 @@ import ZcashHaskell.Types ) import ZcashHaskell.Utils -import Data.Word -import Foreign.C.Types -import Haskoin.Crypto.Keys.Extended - m2bs :: Maybe BS.ByteString -> BS.ByteString m2bs x = fromMaybe "" x @@ -785,13 +787,23 @@ main = do hexString "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" let t = readZebraTransaction h - it "Transparent component is read" $ do + it "Transparent output is identified" $ do case t of Nothing -> assertFailure "Couldn't decode" Just t' -> do + let a = + "utest1hhggl4nxfdx63evps6r7qz50cgacgtdpt9k7dl0734w63zn5qmrp6c2xdv9rkqyfkj6kgau4kz48xtm80e67l534qp02teqq86zuzetxql6z5v32yglg9n2un5zsu0hwcvaunzdfg5qnry6syh2dh9x8eu27de03j9pjfvrqda6acgtc6f0emdfh6r5jvfanmjml4ms5wwj9wfqmamq" + let a' = isValidUnifiedAddress a let tb = zt_tBundle t' let myTb = fromRawTBundle tb - print myTb + case a' of + Nothing -> assertFailure "Couldn't read address" + Just addr -> do + case myTb of + Nothing -> assertFailure "Couldn't read transparent bundle" + Just myTb' -> + show ((\(TxOut v s) -> s) (head (tb_vout myTb'))) `shouldBe` + maybe "" (show . ta_bytes) (t_rec addr) myTb `shouldNotBe` Nothing it "Sapling component is read" $ do case t of From 09411adfffbc6ac10fd744c9231ef068e00f84bd Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 3 Apr 2024 16:16:42 -0500 Subject: [PATCH 097/149] Implement test to validate transparent outputs --- test/Spec.hs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/Spec.hs b/test/Spec.hs index 70bf04a..d631b23 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -792,7 +792,7 @@ main = do Nothing -> assertFailure "Couldn't decode" Just t' -> do let a = - "utest1hhggl4nxfdx63evps6r7qz50cgacgtdpt9k7dl0734w63zn5qmrp6c2xdv9rkqyfkj6kgau4kz48xtm80e67l534qp02teqq86zuzetxql6z5v32yglg9n2un5zsu0hwcvaunzdfg5qnry6syh2dh9x8eu27de03j9pjfvrqda6acgtc6f0emdfh6r5jvfanmjml4ms5wwj9wfqmamq" + "utest13dq4u4dnf3yddw8lq2n6zdclshra6xsp8zgkc5ydyu6k20zrsscmuex46qa4vh84rgd78sqnlleapznnz7mnzx9wv0unts8pv32paj8se5ca3kves2u4a89uy6e8cf4hnarxydxh7hq2e9uu39punfmm53k5h45xn9k3dx35la8j7munh9td7774m8gkqgc4mn40t69w20uu2gtks7a" let a' = isValidUnifiedAddress a let tb = zt_tBundle t' let myTb = fromRawTBundle tb @@ -801,9 +801,13 @@ main = do Just addr -> do case myTb of Nothing -> assertFailure "Couldn't read transparent bundle" - Just myTb' -> - show ((\(TxOut v s) -> s) (head (tb_vout myTb'))) `shouldBe` - maybe "" (show . ta_bytes) (t_rec addr) + Just myTb' -> do + let pkHash = + BS.take 20 $ + BS.drop 3 $ + (\(TxOut v s) -> s) (head (tb_vout myTb')) + pkHash `shouldBe` + maybe "" (hexBytes . ta_bytes) (t_rec addr) myTb `shouldNotBe` Nothing it "Sapling component is read" $ do case t of From 3c2e8fc99487d9842695548a6c94c1c42709c7d0 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 5 Apr 2024 12:47:17 -0500 Subject: [PATCH 098/149] Fix `TxIn` parsing --- CHANGELOG.md | 6 ++++++ src/ZcashHaskell/Types.hs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f357bb..39c8d2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.3.0] + +### Fixed + +- Parsing of `TxIn` for FFI + ## [0.5.2.0] ### Added diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index a00b8d2..f2c5359 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -160,7 +160,7 @@ fromRawTxIn t = H.TxIn op (rti_script t) (rti_seq t) then H.nullOutPoint else H.OutPoint ((fromJust . - H.hexToTxHash . E.decodeUtf8Lenient . rop_hash . rti_outpoint) + H.hexToTxHash . toText . hexString . rop_hash . rti_outpoint) t) (rop_n $ rti_outpoint t) From 5814cb7c9ec01c4c0ae817cb3beaace186ba32ea Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 5 Apr 2024 14:12:39 -0500 Subject: [PATCH 099/149] Correct the parsing of the tx hash in `TxIn` --- src/ZcashHaskell/Types.hs | 2 +- test/Spec.hs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index f2c5359..0320479 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -160,7 +160,7 @@ fromRawTxIn t = H.TxIn op (rti_script t) (rti_seq t) then H.nullOutPoint else H.OutPoint ((fromJust . - H.hexToTxHash . toText . hexString . rop_hash . rti_outpoint) + H.hexToTxHash . toText . fromRawBytes . rop_hash . rti_outpoint) t) (rop_n $ rti_outpoint t) diff --git a/test/Spec.hs b/test/Spec.hs index d631b23..6db774b 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -832,6 +832,18 @@ main = do Just t' -> do let ob = zt_oBundle t' fromRawOBundle ob `shouldNotBe` Nothing + describe "Raw transaction with Transparent inputs" $ do + let h = + hexString + "0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000" + let t = readZebraTransaction h + it "TxIn parsing" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let tb = zt_tBundle t' + print tb + show tb `shouldNotBe` "" -- | Properties prop_PhraseLength :: Property From e00cf2170994245d2a664364bd2e59e74ad3a28c Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 8 Apr 2024 12:54:05 -0500 Subject: [PATCH 100/149] Add Sapling output decrypting with spending key --- CHANGELOG.md | 4 ++ librustzcash-wrapper/Cargo.lock | 1 + librustzcash-wrapper/Cargo.toml | 1 + librustzcash-wrapper/src/lib.rs | 78 ++++++++++++++++++++++++++++++--- src/C/Zcash.chs | 10 +++++ src/ZcashHaskell/Sapling.hs | 34 +++++++++++++- 6 files changed, 120 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c8d2a..ea99a3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 812bccd..6a172d0 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -1284,6 +1284,7 @@ dependencies = [ "borsh 0.10.3", "f4jumble", "haskell-ffi", + "incrementalmerkletree", "nonempty", "orchard", "proc-macro2", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 7abadcc..73ee84c 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -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] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 15c242f..623f001 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -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 = 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 = marshall_from_haskell_var(key, key_len, RW); + let note_input: Vec = 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.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); } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 7bf023f..c236b03 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -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'& diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 2dc7f49..ef287d6 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -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 From 4f9601f8d8ce7859c362ccb6dab6d51977da0e81 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 9 Apr 2024 13:05:57 -0500 Subject: [PATCH 101/149] Add function to decode Orchard action with spending key --- librustzcash-wrapper/src/lib.rs | 41 +++++++++++++++++++++++++++++++++ src/C/Zcash.chs | 8 +++++++ src/ZcashHaskell/Orchard.hs | 13 +++++++++++ 3 files changed, 62 insertions(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 623f001..d5533f5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -852,6 +852,47 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt( } } +#[no_mangle] +pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk( + key: *const u8, + key_len: usize, + note: *const u8, + note_len: usize, + external: bool, + out: *mut u8, + out_len: &mut usize + ){ + let sk_input: Vec = marshall_from_haskell_var(key, key_len, RW); + let note_input: Haction = marshall_from_haskell_var(note, note_len, RW); + let action: Action> = Action::from_parts( + Nullifier::from_bytes(&to_array(note_input.nf.bytes)).unwrap(), + VerificationKey::try_from(to_array(note_input.rk.bytes)).unwrap(), + ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx.bytes)).unwrap(), + TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key.bytes), enc_ciphertext: to_array(note_input.enc_txt.bytes), out_ciphertext: to_array(note_input.out_txt.bytes)}, + ValueCommitment::from_bytes(&to_array(note_input.cv.bytes)).unwrap(), + Signature::from(to_array(note_input.auth.bytes))); + let sk_array = to_array(sk_input); + let domain = OrchardDomain::for_nullifier(*action.nullifier()); + let dec_sk = SpendingKey::from_bytes(sk_array).unwrap(); + let fvk = FullViewingKey::from(&dec_sk); + let ivk = if external { + fvk.to_ivk(Scope::External) + } else { + fvk.to_ivk(Scope::Internal) + }; + let pivk = PreparedIncomingViewingKey::new(&ivk); + 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()}; + 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); + } + } +} #[no_mangle] pub extern "C" fn rust_wrapper_tx_read( diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index c236b03..ea2f9ea 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -126,6 +126,14 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK + { toBorshVar* `BS.ByteString'& + , toBorshVar* `OrchardAction'& + , `Bool' + , getVarBuffer `Buffer DecodedNote'& + } + -> `()' +#} {# fun unsafe rust_wrapper_tx_parse as rustWrapperTxParse { toBorshVar* `BS.ByteString'& , getVarBuffer `Buffer [BS.ByteString]'& diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index a810114..b503da2 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -22,6 +22,7 @@ import C.Zcash , rustWrapperGenOrchardSpendKey , rustWrapperOrchardCheck , rustWrapperOrchardNoteDecode + , rustWrapperOrchardNoteDecodeSK , rustWrapperUADecode , rustWrapperUfvkDecode ) @@ -153,3 +154,15 @@ decryptOrchardAction key encAction = decodedAction = withPureBorshVarBuffer $ rustWrapperOrchardNoteDecode (o_key key) encAction + +-- | Attemtps to decode the given @OrchardAction@ using the given @OrchardSpendingKey@ +decryptOrchardActionSK :: + OrchardSpendingKey -> Scope -> OrchardAction -> Maybe DecodedNote +decryptOrchardActionSK sk scope oa = + case a_value decodedAction of + 0 -> Nothing + _ -> Just decodedAction + where + decodedAction = + withPureBorshVarBuffer $ + rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External) From 7c5d4cf99fa11961a27e3c9c954a73d4778f9640 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 9 Apr 2024 13:06:23 -0500 Subject: [PATCH 102/149] Update version --- CHANGELOG.md | 4 ++++ zcash-haskell.cabal | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea99a3a..97a42c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.4.0] + +- Function to decode Orchard actions with a spending key + ## [0.5.3.0] ### Added diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index aa3c73f..2e0cae2 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.3.0 +version: 0.5.4.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 1f8a4cbd2b723f8814c7dd5297af493c0dcfadf7 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Tue, 9 Apr 2024 20:23:16 -0400 Subject: [PATCH 103/149] Branch rvv040 - Get Sapling Human Readable representation - In proccess Bug - Bech32m error calculating checksum - to resolve --- src/ZcashHaskell/Orchard.hs | 18 ++++++++++++++++++ src/ZcashHaskell/Utils.hs | 1 + test/Spec.hs | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index a810114..0295a30 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -153,3 +153,21 @@ decryptOrchardAction key encAction = decodedAction = withPureBorshVarBuffer $ rustWrapperOrchardNoteDecode (o_key key) encAction + +chrToByteString :: [Char] -> C.ByteString +chrToByteString = C.pack + +getSaplingFromUA :: BS.ByteString -> T.Text +getSaplingFromUA uadd = do + let a = isValidUnifiedAddress uadd + case a of + Nothing -> "xxx" + Just a -> do + let sraw = s_rec a + case sraw of + Nothing -> "xxx" + Just sraw -> do + let net = ua_net a + case net of + MainNet -> encodeBech32m (chrToByteString sapPaymentAddressHrp) ( getBytes sraw ) + TestNet -> encodeBech32m (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw ) \ No newline at end of file diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index b6598da..b858c7c 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -115,3 +115,4 @@ readZebraTransaction hex = else Just rawTx where rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex + diff --git a/test/Spec.hs b/test/Spec.hs index 6db774b..fab4ccb 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -844,6 +844,12 @@ main = do let tb = zt_tBundle t' print tb show tb `shouldNotBe` "" + describe "Extract Sapling Address" $ do + let sr = getSaplingFromUA "u1y3224rl7f5tz262apkmj33z6pe5rn3z4y6pqtces45valg9qsvuer688crr8xvvqq4u7umhrvt0qaj5jv05ec7jd66qedq97dvrhhzryq69lrm8mc25ql0zt6yk2yks9zyp0qv7tgtfqw5zgl7q3p660v37f0ra5aj2g8y75mrzkgrgr8fl5cmur07tcqcrvzf96t4ennh45vnxemp8" + it "Extract sapling address" $ do + print sr + sr `shouldBe` "zs1algtrzkczkl5708yaw2sr58jkutm8wcx2tj0a9ex3ns8nu8l3wtlusemfcpllnfmhyhw7vk9m0h" + -- | Properties prop_PhraseLength :: Property From 27b291c49a64542232fea2b966835229bfc281b2 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 10 Apr 2024 07:06:04 -0500 Subject: [PATCH 104/149] Add function to append nodes to commitment tree --- librustzcash-wrapper/src/lib.rs | 46 ++++++++++++++++++++++++++++++++- src/C/Zcash.chs | 8 ++++++ src/ZcashHaskell/Sapling.hs | 17 +++++++++++- test/Spec.hs | 19 ++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index d5533f5..ad70b02 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -6,7 +6,8 @@ use std::{ marker::PhantomData, io::{ Write, - Cursor + Cursor, + Error }, }; @@ -28,6 +29,10 @@ use incrementalmerkletree::frontier::CommitmentTree; use zip32; use zcash_primitives::{ + merkle_tree::{ + read_commitment_tree, + write_commitment_tree + }, zip32::{ Scope as SaplingScope, ChildIndex, @@ -57,6 +62,7 @@ use zcash_primitives::{ MerklePath, NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, PaymentAddress, + note::ExtractedNoteCommitment as SaplingNoteCommitment, keys::{ PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, ExpandedSpendingKey, @@ -187,6 +193,14 @@ pub struct Hhex { bytes: Vec } +impl ToHaskell for Hhex { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + + #[derive(BorshSerialize, BorshDeserialize)] pub struct Haction { nf: Hhex, @@ -1129,3 +1143,33 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver( marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); } + +#[no_mangle] +pub extern "C" fn rust_wrapper_read_commitment_tree( + tree: *const u8, + tree_len: usize, + node: *const u8, + node_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); + let tree_reader = Cursor::new(tree_in); + let mut ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); + + let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); + let n = Node::from_cmu(&SaplingNoteCommitment::from_bytes(&to_array(node_in)).unwrap()); + ct.append(n); + let mut out_bytes: Vec = Vec::new(); + let result = write_commitment_tree(&ct, &mut out_bytes ); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index ea2f9ea..f131d40 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -200,3 +200,11 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_read_commitment_tree as rustWrapperReadSaplingCommitmentTree + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index ef287d6..8153034 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -19,6 +19,7 @@ module ZcashHaskell.Sapling where import C.Zcash ( rustWrapperIsShielded + , rustWrapperReadSaplingCommitmentTree , rustWrapperSaplingCheck , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingDecodeEsk @@ -30,7 +31,7 @@ import C.Zcash ) import Data.Aeson import qualified Data.ByteString as BS -import Data.HexString (HexString(..), fromText, toBytes, toText) +import Data.HexString (HexString(..), fromText, hexString, toBytes, toText) import Data.Word import Foreign.Rust.Marshall.Variable ( withPureBorshVarBuffer @@ -166,3 +167,17 @@ genSaplingInternalAddress sk = where res = withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk) + +-- | Update a Sapling commitment tree +updateSaplingCommitmentTree :: + HexString -- ^ the base tree + -> HexString -- ^ the new note commitment + -> Maybe HexString +updateSaplingCommitmentTree tree cmu = + if BS.length updatedTree > 1 + then Just $ HexString updatedTree + else Nothing + where + updatedTree = + withPureBorshVarBuffer $ + rustWrapperReadSaplingCommitmentTree (hexBytes tree) (hexBytes cmu) diff --git a/test/Spec.hs b/test/Spec.hs index 6db774b..95320b9 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -54,6 +54,7 @@ import ZcashHaskell.Sapling , isValidSaplingViewingKey , isValidShieldedAddress , matchSaplingAddress + , updateSaplingCommitmentTree ) import ZcashHaskell.Transparent import ZcashHaskell.Types @@ -844,6 +845,24 @@ main = do let tb = zt_tBundle t' print tb show tb `shouldNotBe` "" + describe "Sapling commitment trees" $ do + let tree = + hexString + "01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" + let cmu1 = + hexString + "45e47c5df6f5c5e48aa3526e977b2d1b57eda57214e36f06128008cb17b0125f" + let cmu2 = + hexString + "426ef44b3b22e0eeda7e4d2b62bac63966572b224e50f97ee56c9490cde4910d" + let tree2 = + hexString + "01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" + it "Commitment tree is updated correctly" $ do + let t1 = updateSaplingCommitmentTree tree cmu1 + case t1 of + Nothing -> assertFailure "Tree 1 failed" + Just t2 -> updateSaplingCommitmentTree t2 cmu2 `shouldBe` Just tree2 -- | Properties prop_PhraseLength :: Property From 3f918f734a878a2b8a032f9774ae8e82966a4576 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Wed, 10 Apr 2024 09:55:41 -0400 Subject: [PATCH 105/149] rvv040 - Sapling address encoding to human readable format Change Bech32m to Bech32 encoding for Sapling address --- librustzcash-wrapper/src/lib.rs | 20 +++++++++++++++++++- src/C/Zcash.chs | 11 ++++++++++- src/ZcashHaskell/Orchard.hs | 6 +++--- src/ZcashHaskell/Utils.hs | 7 ++++++- test/Spec.hs | 4 ++-- 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 623f001..b58f948 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -105,6 +105,7 @@ use orchard::{ use bech32::{ Hrp, + Bech32, Bech32m }; @@ -585,7 +586,7 @@ pub extern "C" fn rust_wrapper_bech32decode( } #[no_mangle] -pub extern "C" fn rust_wrapper_bech32_encode( +pub extern "C" fn rust_wrapper_bech32m_encode( hr: *const u8, hr_len: usize, b: *const u8, @@ -1088,3 +1089,20 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver( marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); } + +#[no_mangle] +pub extern "C" fn rust_wrapper_bech32_encode( + hr: *const u8, + hr_len: usize, + b: *const u8, + b_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let hr: String = marshall_from_haskell_var(hr, hr_len, RW); + let hrp = Hrp::parse(&hr).unwrap(); + let b: Vec = marshall_from_haskell_var(b, b_len, RW); + let string = bech32::encode::(hrp, &b).unwrap(); + marshall_to_haskell_var(&string, out, out_len, RW); +} + diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index c236b03..9b8b643 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -38,7 +38,7 @@ import ZcashHaskell.Types -> `()' #} -{# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode +{# fun unsafe rust_wrapper_bech32m_encode as rustWrapperBech32mEncode { toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'& , getVarBuffer `Buffer (T.Text)'& @@ -192,3 +192,12 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (T.Text)'& + } + -> `()' +#} + diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 0295a30..19655e6 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -33,7 +33,7 @@ import qualified Data.Text.Encoding as E import Data.Word import Foreign.Rust.Marshall.Variable import ZcashHaskell.Types -import ZcashHaskell.Utils (encodeBech32m, f4Jumble) +import ZcashHaskell.Utils (encodeBech32m, encodeBech32, f4Jumble) -- | Derives an Orchard spending key for the given seed and account ID genOrchardSpendingKey :: @@ -169,5 +169,5 @@ getSaplingFromUA uadd = do Just sraw -> do let net = ua_net a case net of - MainNet -> encodeBech32m (chrToByteString sapPaymentAddressHrp) ( getBytes sraw ) - TestNet -> encodeBech32m (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw ) \ No newline at end of file + MainNet -> encodeBech32 (chrToByteString sapPaymentAddressHrp) ( getBytes sraw ) + TestNet -> encodeBech32 (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw ) \ No newline at end of file diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index b858c7c..3187c62 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -19,6 +19,7 @@ module ZcashHaskell.Utils where import C.Zcash ( rustWrapperBech32Decode + , rustWrapperBech32mEncode , rustWrapperBech32Encode , rustWrapperF4Jumble , rustWrapperF4UnJumble @@ -45,7 +46,11 @@ decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode -- | Encode the given Human Readable Part and bytestring as a Bech32m string encodeBech32m :: BS.ByteString -> BS.ByteString -> T.Text -encodeBech32m h d = withPureBorshVarBuffer $ rustWrapperBech32Encode h d +encodeBech32m h d = withPureBorshVarBuffer $ rustWrapperBech32mEncode h d + +-- | Encode the given Human Readable Part and bytestring as a Bech32 string +encodeBech32 :: BS.ByteString -> BS.ByteString -> T.Text +encodeBech32 h d = withPureBorshVarBuffer $ rustWrapperBech32Encode h d -- | Apply the F4Jumble transformation to the given bytestring f4Jumble :: BS.ByteString -> BS.ByteString diff --git a/test/Spec.hs b/test/Spec.hs index fab4ccb..18b4cdf 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -845,10 +845,10 @@ main = do print tb show tb `shouldNotBe` "" describe "Extract Sapling Address" $ do - let sr = getSaplingFromUA "u1y3224rl7f5tz262apkmj33z6pe5rn3z4y6pqtces45valg9qsvuer688crr8xvvqq4u7umhrvt0qaj5jv05ec7jd66qedq97dvrhhzryq69lrm8mc25ql0zt6yk2yks9zyp0qv7tgtfqw5zgl7q3p660v37f0ra5aj2g8y75mrzkgrgr8fl5cmur07tcqcrvzf96t4ennh45vnxemp8" + let sr = getSaplingFromUA "u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" it "Extract sapling address" $ do print sr - sr `shouldBe` "zs1algtrzkczkl5708yaw2sr58jkutm8wcx2tj0a9ex3ns8nu8l3wtlusemfcpllnfmhyhw7vk9m0h" + sr `shouldBe` "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" -- | Properties From cea8a70f447f336677c599a9882a2ed198d44420 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Wed, 10 Apr 2024 10:27:09 -0400 Subject: [PATCH 106/149] rvva040 - Convert Sapling receiver to Human readable format Function getSaplingFromUA -> Now returns "Maybe T.Text" instead of "T.Text" --- src/ZcashHaskell/Orchard.hs | 10 +++++----- test/Spec.hs | 13 ++++++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 19655e6..4cb6f7d 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -157,17 +157,17 @@ decryptOrchardAction key encAction = chrToByteString :: [Char] -> C.ByteString chrToByteString = C.pack -getSaplingFromUA :: BS.ByteString -> T.Text +getSaplingFromUA :: BS.ByteString -> Maybe T.Text getSaplingFromUA uadd = do let a = isValidUnifiedAddress uadd case a of - Nothing -> "xxx" + Nothing -> Nothing Just a -> do let sraw = s_rec a case sraw of - Nothing -> "xxx" + Nothing -> Nothing Just sraw -> do let net = ua_net a case net of - MainNet -> encodeBech32 (chrToByteString sapPaymentAddressHrp) ( getBytes sraw ) - TestNet -> encodeBech32 (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw ) \ No newline at end of file + MainNet -> Just $ encodeBech32 (chrToByteString sapPaymentAddressHrp) ( getBytes sraw ) + TestNet -> Just $ encodeBech32 (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw ) \ No newline at end of file diff --git a/test/Spec.hs b/test/Spec.hs index 18b4cdf..2333a69 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -844,11 +844,18 @@ main = do let tb = zt_tBundle t' print tb show tb `shouldNotBe` "" - describe "Extract Sapling Address" $ do + describe "Extract Sapling Address - UA Valid" $ do let sr = getSaplingFromUA "u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" it "Extract sapling address" $ do - print sr - sr `shouldBe` "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" + case sr of + Nothing -> assertFailure "UA invalid or does not contain a Sapling receiver" + Just t -> do + print t + t `shouldBe` "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" + describe "Extract Sapling Address - UA Invalid" $ do + let sr = getSaplingFromUA "u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" + it "Try to extract sapling address from invalid UA" $ do + sr `shouldBe` Nothing -- | Properties From 30ca143dc9bf97f4c7159f5ccc52f33216ec4f38 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 10 Apr 2024 11:29:59 -0500 Subject: [PATCH 107/149] Update changelog --- CHANGELOG.md | 7 +++++++ src/ZcashHaskell/Orchard.hs | 28 ++++++++++++++-------------- zcash-haskell.cabal | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea99a3a..64cfe16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.3.1] + +### Added + +- Functions for Bech32 encoding +- Function to encode a Sapling address + ## [0.5.3.0] ### Added diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 4cb6f7d..438602f 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -33,7 +33,7 @@ import qualified Data.Text.Encoding as E import Data.Word import Foreign.Rust.Marshall.Variable import ZcashHaskell.Types -import ZcashHaskell.Utils (encodeBech32m, encodeBech32, f4Jumble) +import ZcashHaskell.Utils (encodeBech32, encodeBech32m, f4Jumble) -- | Derives an Orchard spending key for the given seed and account ID genOrchardSpendingKey :: @@ -154,20 +154,20 @@ decryptOrchardAction key encAction = withPureBorshVarBuffer $ rustWrapperOrchardNoteDecode (o_key key) encAction -chrToByteString :: [Char] -> C.ByteString -chrToByteString = C.pack - getSaplingFromUA :: BS.ByteString -> Maybe T.Text -getSaplingFromUA uadd = do +getSaplingFromUA uadd = do let a = isValidUnifiedAddress uadd - case a of + case a of Nothing -> Nothing Just a -> do - let sraw = s_rec a - case sraw of - Nothing -> Nothing - Just sraw -> do - let net = ua_net a - case net of - MainNet -> Just $ encodeBech32 (chrToByteString sapPaymentAddressHrp) ( getBytes sraw ) - TestNet -> Just $ encodeBech32 (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw ) \ No newline at end of file + let sraw = s_rec a + case sraw of + Nothing -> Nothing + Just sraw -> do + let net = ua_net a + case net of + MainNet -> + Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sraw) + TestNet -> + Just $ + encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sraw) diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index aa3c73f..6afc133 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.3.0 +version: 0.5.3.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 0f19e376dcf24849b9a92b525e1f6852d9b04e88 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 11 Apr 2024 16:01:29 -0500 Subject: [PATCH 108/149] Add Sapling commitment tree functionality --- CHANGELOG.md | 7 +++ librustzcash-wrapper/src/lib.rs | 77 +++++++++++++++++++++++++++------ src/C/Zcash.chs | 19 +++++++- src/ZcashHaskell/Sapling.hs | 32 +++++++++++--- src/ZcashHaskell/Types.hs | 10 +++++ test/Spec.hs | 17 ++++++-- zcash-haskell.cabal | 2 +- 7 files changed, 139 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80e6446..d271b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.4.1] + +### Added + +- Functions to handle Sapling commitment trees, incremental witnesses and note positions + ## [0.5.4.0] +### Added - Function to decode Orchard actions with a spending key - Functions for Bech32 encoding - Function to encode a Sapling address diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 9fc6c59..559ca5d 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -24,14 +24,19 @@ use haskell_ffi::{ FromHaskell, HaskellSize, ToHaskell }; -use incrementalmerkletree::frontier::CommitmentTree; +use incrementalmerkletree::{ + frontier::CommitmentTree, + witness::IncrementalWitness +}; use zip32; use zcash_primitives::{ merkle_tree::{ read_commitment_tree, - write_commitment_tree + write_commitment_tree, + read_incremental_witness, + write_incremental_witness }, zip32::{ Scope as SaplingScope, @@ -1146,7 +1151,23 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver( } #[no_mangle] -pub extern "C" fn rust_wrapper_read_commitment_tree( +pub extern "C" fn rust_wrapper_bech32_encode( + hr: *const u8, + hr_len: usize, + b: *const u8, + b_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let hr: String = marshall_from_haskell_var(hr, hr_len, RW); + let hrp = Hrp::parse(&hr).unwrap(); + let b: Vec = marshall_from_haskell_var(b, b_len, RW); + let string = bech32::encode::(hrp, &b).unwrap(); + marshall_to_haskell_var(&string, out, out_len, RW); +} + +#[no_mangle] +pub extern "C" fn rust_wrapper_read_sapling_commitment_tree( tree: *const u8, tree_len: usize, node: *const u8, @@ -1176,18 +1197,46 @@ pub extern "C" fn rust_wrapper_read_commitment_tree( } #[no_mangle] -pub extern "C" fn rust_wrapper_bech32_encode( - hr: *const u8, - hr_len: usize, - b: *const u8, - b_len: usize, +pub extern "C" fn rust_wrapper_read_sapling_witness( + tree: *const u8, + tree_len: usize, out: *mut u8, out_len: &mut usize - ) { - let hr: String = marshall_from_haskell_var(hr, hr_len, RW); - let hrp = Hrp::parse(&hr).unwrap(); - let b: Vec = marshall_from_haskell_var(b, b_len, RW); - let string = bech32::encode::(hrp, &b).unwrap(); - marshall_to_haskell_var(&string, out, out_len, RW); + ){ + let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); + let tree_reader = Cursor::new(tree_in); + let ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); + let inc_wit = IncrementalWitness::from_tree(ct); + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&inc_wit, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } } +#[no_mangle] +pub extern "C" fn rust_wrapper_read_sapling_position( + wit: *const u8, + wit_len: usize, + ) -> u64 { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let path = iw.path(); + match path { + Some(p) => { + let pos = p.position(); + return u64::from(pos); + }, + None => { + return 0; + } + } +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 4f7818e..37537c8 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -21,6 +21,7 @@ import qualified Data.Text as T import Data.Word import Data.Int import Data.Structured +import Data.HexString (HexString(..)) import Foreign.C.Types import Foreign.Rust.Marshall.External import Foreign.Rust.Marshall.Fixed @@ -201,14 +202,27 @@ import ZcashHaskell.Types -> `()' #} -{# fun unsafe rust_wrapper_read_commitment_tree as rustWrapperReadSaplingCommitmentTree +{# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree { toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'& - , getVarBuffer `Buffer (BS.ByteString)'& + , getVarBuffer `Buffer HexString'& } -> `()' #} +{# fun unsafe rust_wrapper_read_sapling_witness as rustWrapperReadSaplingWitness + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun pure unsafe rust_wrapper_read_sapling_position as rustWrapperReadSaplingPosition + { toBorshVar* `BS.ByteString'& + } + -> `Word64' +#} + {# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode { toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'& @@ -216,3 +230,4 @@ import ZcashHaskell.Types } -> `()' #} + diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 8153034..f737622 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -20,6 +20,8 @@ module ZcashHaskell.Sapling where import C.Zcash ( rustWrapperIsShielded , rustWrapperReadSaplingCommitmentTree + , rustWrapperReadSaplingPosition + , rustWrapperReadSaplingWitness , rustWrapperSaplingCheck , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingDecodeEsk @@ -43,8 +45,10 @@ import ZcashHaskell.Types , DecodedNote(..) , RawData(..) , RawTxResponse(..) + , SaplingCommitmentTree(..) , SaplingReceiver(..) , SaplingSpendingKey(..) + , SaplingWitness(..) , Scope(..) , Seed(..) , ShieldedOutput(..) @@ -170,14 +174,32 @@ genSaplingInternalAddress sk = -- | Update a Sapling commitment tree updateSaplingCommitmentTree :: - HexString -- ^ the base tree + SaplingCommitmentTree -- ^ the base tree -> HexString -- ^ the new note commitment - -> Maybe HexString + -> Maybe SaplingCommitmentTree updateSaplingCommitmentTree tree cmu = - if BS.length updatedTree > 1 - then Just $ HexString updatedTree + if BS.length (hexBytes updatedTree) > 1 + then Just $ SaplingCommitmentTree updatedTree else Nothing where updatedTree = withPureBorshVarBuffer $ - rustWrapperReadSaplingCommitmentTree (hexBytes tree) (hexBytes cmu) + rustWrapperReadSaplingCommitmentTree + (hexBytes $ sapTree tree) + (hexBytes cmu) + +-- | Get the Sapling incremental witness from a commitment tree +getSaplingWitness :: SaplingCommitmentTree -> Maybe SaplingWitness +getSaplingWitness tree = + if BS.length (hexBytes wit) > 1 + then Just $ SaplingWitness wit + else Nothing + where + wit = + withPureBorshVarBuffer $ + rustWrapperReadSaplingWitness (hexBytes $ sapTree tree) + +-- | Get the Sapling note position from a witness +getSaplingNotePosition :: SaplingWitness -> Integer +getSaplingNotePosition = + fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 0320479..aeca505 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -519,6 +519,16 @@ instance FromJSON ShieldedOutput where p <- obj .: "proof" pure $ ShieldedOutput cv cmu ephKey encText outText p +-- | Type for a Sapling note commitment tree +newtype SaplingCommitmentTree = SaplingCommitmentTree + { sapTree :: HexString + } deriving (Eq, Prelude.Show, Read) + +-- | Type for a Sapling incremental witness +newtype SaplingWitness = SaplingWitness + { sapWit :: HexString + } deriving (Eq, Prelude.Show, Read) + -- * Orchard -- | A spending key for Orchard newtype OrchardSpendingKey = diff --git a/test/Spec.hs b/test/Spec.hs index a6b7665..10b6da6 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -50,6 +50,8 @@ import ZcashHaskell.Sapling , genSaplingInternalAddress , genSaplingPaymentAddress , genSaplingSpendingKey + , getSaplingNotePosition + , getSaplingWitness , getShieldedOutputs , isValidSaplingViewingKey , isValidShieldedAddress @@ -72,6 +74,7 @@ import ZcashHaskell.Types , RawTxOut(..) , RawTxResponse(..) , RawZebraTx(..) + , SaplingCommitmentTree(..) , SaplingReceiver(..) , SaplingSpendingKey(..) , Scope(..) @@ -843,10 +846,10 @@ main = do Nothing -> assertFailure "Couldn't decode" Just t' -> do let tb = zt_tBundle t' - print tb show tb `shouldNotBe` "" describe "Sapling commitment trees" $ do let tree = + SaplingCommitmentTree $ hexString "01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" let cmu1 = @@ -859,10 +862,18 @@ main = do hexString "01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" it "Commitment tree is updated correctly" $ do + let t1 = updateSaplingCommitmentTree tree cmu1 + t1 `shouldNotBe` Nothing + it "Incremental witness is generated" $ do let t1 = updateSaplingCommitmentTree tree cmu1 case t1 of - Nothing -> assertFailure "Tree 1 failed" - Just t2 -> updateSaplingCommitmentTree t2 cmu2 `shouldBe` Just tree2 + Nothing -> assertFailure "Failed to append node to tree" + Just t -> getSaplingWitness t `shouldNotBe` Nothing + it "Position of note is obtained" $ do + let p = + getSaplingNotePosition <$> + (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1) + p `shouldBe` Just 129405 describe "Extract Sapling Address - UA Valid" $ do let sr = getSaplingFromUA diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 2e0cae2..fea4200 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.4.0 +version: 0.5.4.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 6a2849aae0f574bd0dfdc4dbce2b9de3f2691f56 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Fri, 12 Apr 2024 13:15:41 -0500 Subject: [PATCH 109/149] Add Sapling nullifier calculation to decoding --- librustzcash-wrapper/src/lib.rs | 32 +++++++++++++++++--------------- src/C/Zcash.chs | 1 + src/ZcashHaskell/Sapling.hs | 4 +++- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 559ca5d..81f7235 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -240,7 +240,8 @@ impl Haction { pub struct Hnote { note: u64, recipient: Vec, - memo: Vec + memo: Vec, + nullifier: Vec } impl ToHaskell for Hnote { @@ -732,6 +733,7 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( note_len: usize, external: bool, net: bool, + pos: u64, out: *mut u8, out_len: &mut usize ){ @@ -759,24 +761,24 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( else {zcash_note_encryption::try_note_decryption(&test_domain, &pivk, &action2)}; match result { Some((n, r, m)) => { - //let nullifier = n.nf(&nk, MerklePath.position()); - let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec() }; + let nullifier = n.nf(&nk, pos); + let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.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], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -806,23 +808,23 @@ 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(), nullifier: vec![0]}; 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], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } } Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -856,17 +858,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(), nullifier: vec![0]}; 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], nullifier: 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], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -904,11 +906,11 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk( 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(), nullifier: vec![0]}; 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], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 37537c8..4b08f78 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -107,6 +107,7 @@ import ZcashHaskell.Types , toBorshVar* `BS.ByteString'& , `Bool' , `Bool' + , `Word64' , getVarBuffer `Buffer DecodedNote'& } -> `()' diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index f737622..a108eaa 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -121,8 +121,9 @@ decodeSaplingOutputEsk :: -> ShieldedOutput -> ZcashNet -> Scope + -> Integer -> Maybe DecodedNote -decodeSaplingOutputEsk key out znet scope = +decodeSaplingOutputEsk key out znet scope pos = case a_value decodedAction of 0 -> Nothing _ -> Just decodedAction @@ -134,6 +135,7 @@ decodeSaplingOutputEsk key out znet scope = (serializeShieldedOutput out) (znet == MainNet) (scope == External) + (fromIntegral pos) -- | Attempts to obtain a sapling SpendingKey using a HDSeed genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey From deacf373e117b9eb3093846e8d676848e66a6d15 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Sat, 13 Apr 2024 18:27:59 -0400 Subject: [PATCH 110/149] rvv040 - Add new function to decode a Transparent Address in HRF The function retunrs a TransparentAddress object. --- CHANGELOG.md | 7 ++++ src/ZcashHaskell/Orchard.hs | 10 +++--- src/ZcashHaskell/Transparent.hs | 60 ++++++++++++++++++++++++++------- src/ZcashHaskell/Types.hs | 14 +++++--- test/Spec.hs | 13 ++++--- 5 files changed, 79 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80e6446..8247aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.0] + +- Added unction to decode Transparent Address in Human Readable Format +- TransparentAddress type refactored +- TransparentReceiver added to replace old TransparentAddress +- sha256 Functionmoved outside of encodeTransparentReceiver + ## [0.5.4.0] - Function to decode Orchard actions with a spending key diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 76bbad2..8f57ad5 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -95,9 +95,9 @@ isValidUnifiedAddress str = then Just $ SaplingReceiver (raw_s x) else Nothing) (if not (BS.null (raw_t x)) - then Just $ TransparentAddress P2PKH (fromRawBytes $ raw_t x) + then Just $ TransparentReceiver P2PKH (fromRawBytes $ raw_t x) else if not (BS.null (raw_to x)) - then Just $ TransparentAddress P2SH (fromRawBytes $ raw_to x) + then Just $ TransparentReceiver P2SH (fromRawBytes $ raw_to x) else Nothing) -- | Encode a 'UnifiedAddress' per [ZIP-316](https://zips.z.cash/zip-0316) @@ -113,9 +113,9 @@ encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b case t_rec ua of Nothing -> BS.empty Just t -> - case ta_type t of - P2SH -> packReceiver 0x01 $ Just $ toBytes $ ta_bytes t - P2PKH -> packReceiver 0x00 $ Just $ toBytes $ ta_bytes t + case tr_type t of + P2SH -> packReceiver 0x01 $ Just $ toBytes $ tr_bytes t + P2PKH -> packReceiver 0x00 $ Just $ toBytes $ tr_bytes t sReceiver = packReceiver 0x02 $ getBytes <$> s_rec ua oReceiver = packReceiver 0x03 $ getBytes <$> o_rec ua padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 140bdbb..5e49ad8 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -20,11 +20,12 @@ import Crypto.Hash import Crypto.Secp256k1 import qualified Data.ByteArray as BA import qualified Data.ByteString as BS -import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58) +import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58, decodeBase58) import Data.HexString import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word +import Data.Char (chr) import Haskoin.Address (Address(..)) import qualified Haskoin.Crypto.Hash as H import Haskoin.Crypto.Keys.Extended @@ -34,6 +35,7 @@ import ZcashHaskell.Types , Scope(..) , Seed(..) , ToBytes(..) + , TransparentReceiver(..) , TransparentAddress(..) , TransparentSpendingKey(..) , TransparentType(..) @@ -42,21 +44,23 @@ import ZcashHaskell.Types , getValue ) --- | Encodes a `TransparentAddress` into the human-readable format per the Zcash Protocol section 5.6.1.1 -encodeTransparent :: +-- | Required for `TransparentReceiver` encoding and decoding +sha256 :: BS.ByteString -> BS.ByteString +sha256 bs = BA.convert (hash bs :: Digest SHA256) + +-- | Encodes a `TransparentReceiver` into the human-readable format per the Zcash Protocol section 5.6.1.1 +encodeTransparentReceiver :: ZcashNet -- ^ The network, `MainNet` or `TestNet` - -> TransparentAddress -- ^ The address to encode + -> TransparentReceiver -- ^ The address to encode -> T.Text -encodeTransparent zNet t = - encodeTransparent' (getTransparentPrefix zNet (ta_type t)) $ - toBytes $ ta_bytes t +encodeTransparentReceiver zNet t = + encodeTransparent' (getTransparentPrefix zNet (tr_type t)) $ + toBytes $ tr_bytes t where encodeTransparent' :: (Word8, Word8) -> BS.ByteString -> T.Text encodeTransparent' (a, b) h = E.decodeUtf8 $ encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum where - sha256 :: BS.ByteString -> BS.ByteString - sha256 bs = BA.convert (hash bs :: Digest SHA256) digest = BS.pack [a, b] <> h checksum = sha256 $ sha256 digest @@ -78,7 +82,7 @@ genTransparentReceiver :: Int -- ^ The index of the address to be created -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses -> XPrvKey -- ^ The transparent private key - -> IO TransparentAddress + -> IO TransparentReceiver genTransparentReceiver i scope xprvk = do ioCtx <- createContext let s = @@ -90,6 +94,38 @@ genTransparentReceiver i scope xprvk = do let childPubKey = deriveXPubKey ioCtx childPrvKey let x = xPubAddr ioCtx childPubKey case x of - PubKeyAddress k -> return $ TransparentAddress P2PKH $ fromBinary k - ScriptAddress j -> return $ TransparentAddress P2SH $ fromBinary j + PubKeyAddress k -> return $ TransparentReceiver P2PKH $ fromBinary k + ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j _anyOtherKind -> throwIO $ userError "Unsupported transparent address type" + +-- } decode a Transparent Address in HRF and return a TransparentAddress object +decodeTransparentAddress :: BS.ByteString -> Maybe TransparentAddress +decodeTransparentAddress taddress = do + if BS.length taddress < 34 + then Nothing -- Not a valid transparent address + else do + let maybeDecoded = decodeBase58 bitcoinAlphabet taddress + case maybeDecoded of + Nothing -> Nothing + Just decoded -> do + let digest = BS.take 22 decoded + let chksum = BS.drop 22 decoded + let chksumd = BS.take 4 (sha256 $ sha256 digest) + if chksum /= chksum + then Nothing -- Invalid address ( invalid checksum ) + else do + -- build the TransparentAddress Object + let addressType = BS.take 2 digest + let transparentReceiver = BS.drop 2 digest + let fb = BS.index addressType 0 + let sb = BS.index addressType 1 + case fb of + 28 -> case sb of + 189 -> Just $ TransparentAddress MainNet $ TransparentReceiver P2SH (fromRawBytes digest) + 186 -> Just $ TransparentAddress TestNet $ TransparentReceiver P2SH (fromRawBytes digest) + 184 -> Just $ TransparentAddress MainNet $ TransparentReceiver P2PKH (fromRawBytes digest) + _ -> Nothing + 29 -> if sb == 37 + then Just $ TransparentAddress TestNet $ TransparentReceiver P2PKH (fromRawBytes digest) + else Nothing + _ -> Nothing diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 0320479..8d310bc 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -94,7 +94,7 @@ data ZcashNet type AccountId = Int --- | Function to get the Base58 prefix for encoding a 'TransparentAddress' +-- | Function to get the Base58 prefix for encoding a 'TransparentReceiver' getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8) getTransparentPrefix n t = case t of @@ -422,10 +422,16 @@ data TransparentType -- | Type for transparent spending key type TransparentSpendingKey = XPrvKey +-- | Type to represent a transparent Zcash addresses +data TransparentReceiver = TransparentReceiver + { tr_type :: !TransparentType + , tr_bytes :: !HexString + } deriving (Eq, Prelude.Show, Read) + -- | Type to represent a transparent Zcash addresses data TransparentAddress = TransparentAddress - { ta_type :: !TransparentType - , ta_bytes :: !HexString + { ta_network :: !ZcashNet + , ta_receiver :: !TransparentReceiver } deriving (Eq, Prelude.Show, Read) -- | Wrapper types for transparent elements @@ -541,7 +547,7 @@ data UnifiedAddress = UnifiedAddress { ua_net :: !ZcashNet , o_rec :: !(Maybe OrchardReceiver) , s_rec :: !(Maybe SaplingReceiver) - , t_rec :: !(Maybe TransparentAddress) + , t_rec :: !(Maybe TransparentReceiver) } deriving (Prelude.Show, Eq, Read) -- | Helper type for marshalling UAs diff --git a/test/Spec.hs b/test/Spec.hs index 2333a69..5abff0a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -77,6 +77,7 @@ import ZcashHaskell.Types , Seed(..) , ShieldedOutput(..) , ToBytes(..) + , TransparentReceiver(..) , TransparentAddress(..) , TransparentBundle(..) , TransparentType(..) @@ -513,7 +514,7 @@ main = do case isValidUnifiedAddress ua of Nothing -> "Bad UA" Just u -> - maybe "No transparent" (encodeTransparent (ua_net u)) $ + maybe "No transparent" (encodeTransparentReceiver (ua_net u)) $ t_rec u msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" it "Recover UA from YWallet" $ @@ -807,7 +808,7 @@ main = do BS.drop 3 $ (\(TxOut v s) -> s) (head (tb_vout myTb')) pkHash `shouldBe` - maybe "" (hexBytes . ta_bytes) (t_rec addr) + maybe "" (hexBytes . tr_bytes) (t_rec addr) myTb `shouldNotBe` Nothing it "Sapling component is read" $ do case t of @@ -856,7 +857,11 @@ main = do let sr = getSaplingFromUA "u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" it "Try to extract sapling address from invalid UA" $ do sr `shouldBe` Nothing - + describe "Decode a Transparent Address" $ do + let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" + it "Try to decode a valid Transparent Address" $ do + print ta + ta `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property @@ -930,7 +935,7 @@ prop_TransparentReceiver s coinType scope (NonNegative i) (NonNegative j) = ioProperty $ do k <- genTransparentPrvKey s coinType i r <- genTransparentReceiver j scope k - return $ ta_type r == P2PKH + return $ tr_type r == P2PKH -- | Generators genOrcArgs :: Gen (CoinType, Int, Int) From e781ed6bd06a4fa463ed3b7bddc0563680380bb2 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 14 Apr 2024 06:57:36 -0500 Subject: [PATCH 111/149] Add test to for transparent decoding --- CHANGELOG.md | 11 +++++-- src/ZcashHaskell/Transparent.hs | 55 ++++++++++++++++++++------------- test/Spec.hs | 35 ++++++++++++++++----- zcash-haskell.cabal | 2 +- 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8247aeb..fa7d323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.5.5.0] +### Added + - Added unction to decode Transparent Address in Human Readable Format -- TransparentAddress type refactored -- TransparentReceiver added to replace old TransparentAddress -- sha256 Functionmoved outside of encodeTransparentReceiver + +### Changed + +- `TransparentAddress` type refactored +- `TransparentReceiver` added to replace old `TransparentAddress` +- `sha256` Function moved outside of `encodeTransparentReceiver` ## [0.5.4.0] diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 5e49ad8..6689c08 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -20,12 +20,12 @@ import Crypto.Hash import Crypto.Secp256k1 import qualified Data.ByteArray as BA import qualified Data.ByteString as BS -import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58, decodeBase58) +import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) +import Data.Char (chr) import Data.HexString import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word -import Data.Char (chr) import Haskoin.Address (Address(..)) import qualified Haskoin.Crypto.Hash as H import Haskoin.Crypto.Keys.Extended @@ -35,8 +35,8 @@ import ZcashHaskell.Types , Scope(..) , Seed(..) , ToBytes(..) - , TransparentReceiver(..) , TransparentAddress(..) + , TransparentReceiver(..) , TransparentSpendingKey(..) , TransparentType(..) , ZcashNet(..) @@ -101,31 +101,44 @@ genTransparentReceiver i scope xprvk = do -- } decode a Transparent Address in HRF and return a TransparentAddress object decodeTransparentAddress :: BS.ByteString -> Maybe TransparentAddress decodeTransparentAddress taddress = do - if BS.length taddress < 34 + if BS.length taddress < 34 then Nothing -- Not a valid transparent address - else do - let maybeDecoded = decodeBase58 bitcoinAlphabet taddress - case maybeDecoded of - Nothing -> Nothing - Just decoded -> do + else do + let maybeDecoded = decodeBase58 bitcoinAlphabet taddress + case maybeDecoded of + Nothing -> Nothing + Just decoded -> do let digest = BS.take 22 decoded let chksum = BS.drop 22 decoded let chksumd = BS.take 4 (sha256 $ sha256 digest) - if chksum /= chksum + if chksum /= chksumd then Nothing -- Invalid address ( invalid checksum ) - else do -- build the TransparentAddress Object + else do let addressType = BS.take 2 digest let transparentReceiver = BS.drop 2 digest let fb = BS.index addressType 0 - let sb = BS.index addressType 1 + let sb = BS.index addressType 1 case fb of - 28 -> case sb of - 189 -> Just $ TransparentAddress MainNet $ TransparentReceiver P2SH (fromRawBytes digest) - 186 -> Just $ TransparentAddress TestNet $ TransparentReceiver P2SH (fromRawBytes digest) - 184 -> Just $ TransparentAddress MainNet $ TransparentReceiver P2PKH (fromRawBytes digest) - _ -> Nothing - 29 -> if sb == 37 - then Just $ TransparentAddress TestNet $ TransparentReceiver P2PKH (fromRawBytes digest) - else Nothing - _ -> Nothing + 28 -> + case sb of + 189 -> + Just $ + TransparentAddress MainNet $ + TransparentReceiver P2SH (fromRawBytes digest) + 186 -> + Just $ + TransparentAddress TestNet $ + TransparentReceiver P2SH (fromRawBytes digest) + 184 -> + Just $ + TransparentAddress MainNet $ + TransparentReceiver P2PKH (fromRawBytes digest) + _ -> Nothing + 29 -> + if sb == 37 + then Just $ + TransparentAddress TestNet $ + TransparentReceiver P2PKH (fromRawBytes digest) + else Nothing + _ -> Nothing diff --git a/test/Spec.hs b/test/Spec.hs index 5abff0a..60625e0 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -77,9 +77,9 @@ import ZcashHaskell.Types , Seed(..) , ShieldedOutput(..) , ToBytes(..) - , TransparentReceiver(..) , TransparentAddress(..) , TransparentBundle(..) + , TransparentReceiver(..) , TransparentType(..) , UnifiedAddress(..) , UnifiedFullViewingKey(..) @@ -846,22 +846,41 @@ main = do print tb show tb `shouldNotBe` "" describe "Extract Sapling Address - UA Valid" $ do - let sr = getSaplingFromUA "u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" + let sr = + getSaplingFromUA + "u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" it "Extract sapling address" $ do - case sr of - Nothing -> assertFailure "UA invalid or does not contain a Sapling receiver" + case sr of + Nothing -> + assertFailure "UA invalid or does not contain a Sapling receiver" Just t -> do - print t - t `shouldBe` "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" + print t + t `shouldBe` + "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" describe "Extract Sapling Address - UA Invalid" $ do - let sr = getSaplingFromUA "u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" + let sr = + getSaplingFromUA + "u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" it "Try to extract sapling address from invalid UA" $ do - sr `shouldBe` Nothing + sr `shouldBe` Nothing describe "Decode a Transparent Address" $ do let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" it "Try to decode a valid Transparent Address" $ do print ta ta `shouldNotBe` Nothing + it "Encode and decode should be the same" $ do + let ua = + "u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x" + case isValidUnifiedAddress ua of + Nothing -> assertFailure "Bad UA" + Just u -> do + let tAdd = + maybe + "No transparent" + (encodeTransparentReceiver (ua_net u)) $ + t_rec u + (ta_receiver <$> decodeTransparentAddress (E.encodeUtf8 tAdd)) `shouldBe` + t_rec u -- | Properties prop_PhraseLength :: Property diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 2e0cae2..8d16f53 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.4.0 +version: 0.5.5.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 15b2f98f1dc9dddbfb056a92fc5f5189cb7f72ce Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Sun, 14 Apr 2024 08:37:19 -0400 Subject: [PATCH 112/149] rvv040 - Fix transparent address decoding - Function was returning the "digest" instead the "transparentReceiver" --- src/ZcashHaskell/Transparent.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 6689c08..366dfe4 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -125,20 +125,20 @@ decodeTransparentAddress taddress = do 189 -> Just $ TransparentAddress MainNet $ - TransparentReceiver P2SH (fromRawBytes digest) + TransparentReceiver P2SH (fromRawBytes transparentReceiver) 186 -> Just $ TransparentAddress TestNet $ - TransparentReceiver P2SH (fromRawBytes digest) + TransparentReceiver P2SH (fromRawBytes transparentReceiver) 184 -> Just $ TransparentAddress MainNet $ - TransparentReceiver P2PKH (fromRawBytes digest) + TransparentReceiver P2PKH (fromRawBytes transparentReceiver) _ -> Nothing 29 -> if sb == 37 then Just $ TransparentAddress TestNet $ - TransparentReceiver P2PKH (fromRawBytes digest) + TransparentReceiver P2PKH (fromRawBytes transparentReceiver) else Nothing _ -> Nothing From 1b98120376447dfba235399cc1392e929bede15f Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Sun, 14 Apr 2024 17:35:49 -0500 Subject: [PATCH 113/149] Add nullifier field to `DecodedNote` --- src/ZcashHaskell/Types.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index c6cff22..f36bc89 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -616,6 +616,7 @@ 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. + , a_nullifier :: !HexString -- ^ The calculated nullifier } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) From 5402fcc07284e21457d526319041228e0eb43560 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 15 Apr 2024 19:58:26 -0500 Subject: [PATCH 114/149] Update Sapling commitment tree reader --- librustzcash-wrapper/src/lib.rs | 36 ++++++++++++++++++++++---------- src/ZcashHaskell/Sapling.hs | 2 +- test/Spec.hs | 37 +++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 81f7235..3a59f00 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1179,23 +1179,37 @@ pub extern "C" fn rust_wrapper_read_sapling_commitment_tree( ){ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); let tree_reader = Cursor::new(tree_in); - let mut ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); - - let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); - let n = Node::from_cmu(&SaplingNoteCommitment::from_bytes(&to_array(node_in)).unwrap()); - ct.append(n); - let mut out_bytes: Vec = Vec::new(); - let result = write_commitment_tree(&ct, &mut out_bytes ); - match result { - Ok(()) => { - let h = Hhex { bytes: out_bytes}; - marshall_to_haskell_var(&h, out, out_len, RW); + let mut ct = read_commitment_tree::>, SAPLING_DEPTH>(tree_reader); + match ct { + Ok(mut comm_tree) => { + let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); + let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(node_in)); + if sap_note_comm.is_some().into() { + let n = Node::from_cmu(&sap_note_comm.unwrap()); + comm_tree.append(n); + let mut out_bytes: Vec = Vec::new(); + let result = write_commitment_tree(&comm_tree, &mut out_bytes ); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } + } else { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } }, Err(_e) => { let h0 = Hhex { bytes: vec![0]}; marshall_to_haskell_var(&h0, out, out_len, RW); } } + } #[no_mangle] diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index a108eaa..52f8689 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -133,8 +133,8 @@ decodeSaplingOutputEsk key out znet scope pos = rustWrapperSaplingDecodeEsk (getBytes key) (serializeShieldedOutput out) - (znet == MainNet) (scope == External) + (znet == MainNet) (fromIntegral pos) -- | Attempts to obtain a sapling SpendingKey using a HDSeed diff --git a/test/Spec.hs b/test/Spec.hs index 3f66d1d..df54d4f 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -47,6 +47,7 @@ import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) import ZcashHaskell.Orchard import ZcashHaskell.Sapling ( decodeSaplingOutput + , decodeSaplingOutputEsk , genSaplingInternalAddress , genSaplingPaymentAddress , genSaplingSpendingKey @@ -911,6 +912,42 @@ main = do t_rec u (ta_receiver <$> decodeTransparentAddress (E.encodeUtf8 tAdd)) `shouldBe` t_rec u + describe "Decode Sapling Output with spending key" $ do + let tree = + SaplingCommitmentTree $ + hexString + "01fef9aa4cfdc8c26eb2693907e96eccaacd61ed04ed7860f1d83260e6e383b936001001bdd6575663c970df26d8eb84b0be6411f65337912c90b1a0a8ba2b9303326d350000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" + let sk = + SaplingSpendingKey + "\ETX\189\201\190\128\NUL\NUL\NUL\128$q\CAN+P\196\229VM\129:+\SI\171W\248)\216\196\215\141\229\239\141*Y\175}\253\232s\156(K\179\GS\139\232\246\ETB\181`l\226i\156\211#\tdNT\248\138i\220\136\209>U\150cl\f+\201i\132\250\220\208\137\178\196\238\141\\,\208\194*\245\145b\FS\244\164\230\194C`\ACK\140\220\194\ACK\190\224u\FS\167CK\181\DC2\r\US\152\237\238\137\209S\238\240D'\248 \255\164F\254)n\a\231r\128!\177\168\210\141\234\STX5\189\b=\191\ETX:\USgvB\155 \162\234\200\DC1$z\252\177C+\252" + let so1 = + ShieldedOutput + (hexString + "c84c0f8f7de5248ce38861de909f90eec765603c590c3b6c95c56efabe43748e") + (hexString + "58f8ee8f4246f6a90f2e3d49d2ecbb116b9cde55ff4c7682b78d10bb510c43fa") + (hexString + "bf2dda6cd8ea626bfef2b8ba806227147b5649b2d814d02a07f6c72b8e2522d3") + (hexString + "1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d8") + (hexString + "6459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa083") + (hexString + "85f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62") + it "Sap output 1" $ do + let pos + --getSaplingNotePosition <$> + = + updateSaplingCommitmentTree + tree + (fromText + "58f8ee8f4246f6a90f2e3d49d2ecbb116b9cde55ff4c7682b78d10bb510c43fa") + pos `shouldNotBe` Nothing + {-case pos of-} + {-Nothing -> assertFailure "couldn't get note position"-} + {-Just p -> do-} + {-let dn = decodeSaplingOutputEsk sk so1 TestNet External p-} + {-dn `shouldNotBe` Nothing-} -- | Properties prop_PhraseLength :: Property From 99d772d2c7466b0c6053ee411c80d8f04e276e05 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 16 Apr 2024 09:43:00 -0500 Subject: [PATCH 115/149] Test Sapling decoding --- librustzcash-wrapper/src/lib.rs | 4 +- test/Spec.hs | 114 +++++++++++++++++++++++++++----- 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 3a59f00..6f03090 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -741,8 +741,8 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( let note_input: Vec = 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 main_domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000)); + let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000)); let scope = if external { SaplingScope::External } else { diff --git a/test/Spec.hs b/test/Spec.hs index df54d4f..f1cc5a9 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -75,6 +75,7 @@ import ZcashHaskell.Types , RawTxOut(..) , RawTxResponse(..) , RawZebraTx(..) + , SaplingBundle(..) , SaplingCommitmentTree(..) , SaplingReceiver(..) , SaplingSpendingKey(..) @@ -913,6 +914,17 @@ main = do (ta_receiver <$> decodeTransparentAddress (E.encodeUtf8 tAdd)) `shouldBe` t_rec u describe "Decode Sapling Output with spending key" $ do + let p = + Phrase + "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest" + let seed = getWalletSeed p + let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0 + let txHex = + hexString + "050000800a27a726b4d0d6c20000000005bd2500000001efdca98904a8b34a0fcfefc47240249a18a530289f2ff6a15bb9885abf6e541aa6c9a851b63df5cbd7473b5b5bc5efbc4a3130c15ce18b92b87b67de2cc72846babecb9030390f0ab02d54960636633695c0c1631f9e87625b92976b12492fcb02d260bd68e7c21cc8ffaad1c3ea5182dee524a09f7b5babcd7d6d85270d6d0d3d48aa620513b632cc17eca91e237b38b1212739e8df6ddcb85c2314cd76c52e26791e2d99fd7ea468b2fb4df3e9bce602daa6a6ad8a57fcee27ce5eee3da998902b5ecf4651adaed9bfd17d0d6de3f4d8c1d391f91c571a341f105e3afd913c25012c623f274406cccda52535a0b4ab883b763d1c7405e6aac941575a965ef0b9b0114588d9d7a2be82a3eb11c662c1fc85a31e8f07fa2c2a9dcb295d5530e893b2aa43c04a508eb182257c791aa4f75d6f5d77a8fbace89666ee80b572015ae4c33b8510e88a8a338da305237d1fc5d267a4ea6914d6f9cacd1a5e158a1acd2722284ff71fd870d92143872def753e2ecde9c04c3b80b513609d47a523243508548c8873426c52eaab76b9cf2b90e63fe9c5485eddfcacac65ca811c634df22a01610b22770ba3d294f566de7e0764641985b62ce2cd71735999111bb323b14d87e482201798672881da255062dc68f912343b911154f7c96442a01be25219f0315bc78c1c3693f80e9a9cd062eee2de8a37cf073f505d936f1be9ef9a78f95e8f70ef20e0e23c2c59c37cd94cfdc5c0d0d14849aea4aa6b6a7b33a0fc57a2a91a5950ba92fec1465db65b02e36fff82d2cacb4cc9038d1a8f30559a6211c52507f2dba723d54e08e82b127cd63fad97e44292e90aa87a1e798504d0f67f242561eecc3ab40911472d75457afecaf9a026e9e0ebcd07c7e78c0bd2963674fa51d6d2c157791c5d60574047a0a4ef496f1465117ed6b9408a107cd77daa0501bb49f548b97d6eb45ace8c0620b0361031f6031af5ff53015c6755fd3c76e9e45c8694d0f38722aa9bd31987fb503a0fef5f3f97033a19212e9d549b7f7de12c2dd3f1b5db353cb40c486a0ab00ecd80d1bc6ef96ba9855bbae57c6bb9965e1ac01d4d2cccd150a44a1e511bf8f9956e7472339306f7c14d90e61e4b01d6e3b98a6b5008ce102b4646d31fc11af90d4834dd8e0005fdc8ca2310fd6cdd4180def97074fedfe645e3c9f399ca9bd282b7743d943c95e4c76a7deae192be69b80f71f7f3dd9ff5199f7c82f743030ffdd69fe88463ef6efd62d329662ab48325d4d67bed52bcdf16d1991adf5fdeadb02e81bf627d5c58d013277f1ab04fec34985917a7495d4056d47986ebcdd18c08cd9f6ba3a1487f646c1a8d99714db89d3d7708cb3455d72943a609f3db3097b7ab295316c2bcf88c73ca159e995acddcc149fd186bd82cd34f02af8e4b68ad39db8621ae92db4d41f53d2567948a423ed2a7fe38b1ff77c0b1cf46d90077bd062b3c2edb9caab921b01c546866a376d29feeaee6ff8e7583c8b4a4f631c4cb339172e5b217ee75793f7608ea6d600c680f86ce63a89a5963ec5e20ade9464f3142c0100c8ce4426b34f887b68c9658cad2dbf8a1c5da4eb7b576bab32b5af129437cd34e3c8b8d17bece85cb661ed20224539ed3eb1d61eec727deb79585f8630a807acd44978dd6ec190312d1c8dacc4cf02783136d730ab0216f7105a9f11d090c5a6e69eb726ba5e785de2f99dc7a8051250544cc661b56913715fdd31cbb5fff3f0b5000a325db631ff60ad241051511dd32e322900df8f5707a59bbe8c1ac85f4be97cfe6700a9193458fa23a70b6639f0bdfd879493520d8e4e593002f5cd9d4bb60aad67610cfb03142a5fc9cefe08cf76a23706288779cefa08ba26e33b9ca58109e0e8abf150dceec584ceabe74e3c838f86a4c3920a37d8dac148317a3c3e132ecaa551ddb7085a8702dc970b5dee5264ee1bf8bc89e92912ff1f3b4e5d10627f3e55b7f6216c0249cb2aabd1267762cad1a88b6c81bc42ac47b6f53e5598ea1dce228165f0e748c0af90eaa421813dc38a521daf83e688c298ddc0a1adac1418f04a7739a6d4f7fdfae64320d8d8107f5bacb87596b7f777a85002d9dcf66c930bfa427a6508198d8cf625279b3c6b438e098f04453d9607bb83271b2ce1f11bcb4c59f0edc4135cf4d5bade66c422c654f9dff4fa720589975d05ae7b10b1d6d2ed72c26534be10a229605f4e946c21abdfd629b1ec18e5819a1be5c89fbf446c65b39fdb32ad5cd9079beee1b73bb3b3a37c3cc10270000000000007a2aed01922521f492d80b45e7d267745f3423c1760982418b9f69390660fb12a6ec696bed9fb392633c496f576a910a0c4d5e37ad76839d951d4895f76948c72fcd580590e014c9b0b3a091ec19ab40a5d59131024055636725f98c29303bab5718006e7154379227c58d18b04938b43c8fa4fe793db395754c3270d71f90dd0fd907c39c848446f99de6efefebe414f981c29f2eb448d1d15ff314ee0c763b66dbf6917e44e18310e0fa1340069512867157552c696a3f89c909668e0698e83825b1b0d73a38a2d99531a8c053a48acdcf9fa9bafcd8ce1f3c3b784bbe23775be45176a5b0a42978008ba5c60020714ba3c0174c9a068b45081267609f7ca31e32d7367cdb0ffb88c7cb762e55b48e4edc639da5ec739a1a5ae0d7b9a16000adb66352c377e3a316a18c66f3c994c0e62b81b3ebc485e4e3438426f1a32a3a875483def774eb45e13df7a14c9ac4f9ac22bcebf3515a6dafe9aad745451708180fadcce8870302e65d90954fd9cae32e3374c8734385c6b4bc3581d997e0f20e6db537a39c8f9b5e54e0a2bcc41e260387e4faabc2f09e7613e26c472d295ad56ee97affb96021f6e882eed25481628f5fab8bb4705cb7df163eb73ab45323dfaebf8e2108fb8c444833d640203455338459c1a68a0a8314f476ab6b230547ade1d2f2df907bb9fb0d2b6b0361e4d8a20118d8328274c71f84ee6aeb761c36fdae537fa747ab9a251fae846197d356b90913b00440e2c034c7cce498a01c5495dbda102d9151fb8f84bc55728a72d2fb5edf660d7df5a155212f6c10e12649028b172f8e4ad32e9f2898cf8e352c7898a2bdb30ff22e903371ec4fc47c49f1b56f0bae3975d80063f251203a5e15afa9af7bfd278e7af0cb5fc3d522639e8f4e3db7d8d5c483dfdc4207c36f0fa72df98f8970abeb8d872cd04a4113e6863db9dd9c72ce08ac8bb01da342fd43ac27a02a4b2db787efe8c98fc55a7730e43f98a85a13942a95545f630191fe4e879b515999c0a63f9eb318d6083de8bed00600" + let txHex2 = + hexString + "0400008085202f8900000000000045b52a00e80300000000000001638ca17ebf9c8fa40eb74be6cc0f702c8a009695b0ae1debc87f4623baa2fdb3f7d7639e3fc8c696cf5ee19d3efccf50e38546de089c4d95a4a3a4a5dd53ef4465bf1d0f111e8a87f1f499ba1d7acaa523f344046ed86c1a42d412fdfb6803cf8e155df11d1beec2d11aecc2cda622b4028db0691fd61d74969a96021bec1f45855f06c8e2c1e02aed41c4549f2e4227572f873847542e1e29077269460e2885cc0a1775378a20e7b1ef40d1e358020ea3c24991ea5bc150932d538b126ea8cdbe5fef9728efedf40912b75634c4ba8288a2ab6f09ad4b8081d395bb4dc49f221011891a8b4af3cc831bf5bdf013950d6a4c4442d314f62d18913a2e18f041cac9535ca8a973e38c3e9796f4eac80d4d88a39c701b17aaf452c19410fe45188d86d91fdee3799ba7bce793c3d0bfba9139c006eb4b279b48dbed04ff75564a068d4b022ac523d40f0a4c65d955873e7498df63bbd87b2518aaac084c8bd8d44d620a07d994ee09ae8e1792d47393b6b570ae1a9122dbf7bcc1f494120d20ca0c028e7443befa6ec5956c3b0c593c6065c7ee909f90de6188e38c24e57d8f0f4cc8fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858d322258e2bc7f6072ad014d8b249567b14276280bab8f2fe6b62ead86cda2dbf1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d86459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa08385f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62d283384bb097120b47b780b4a60b5c04d53e90c9ff2e781ac9b039384fbd48d1d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843e007aea2e07f40f139e24d0e497eac26727a2b9aad4f223887cd423b9fd6cf58353f0b011ea73b2a9843c25529df7b88f2357fba75353218012d1ba4b89b45d50564e9e7f16101c69c4232d2de683bd0960cc87a016dce7bb088720f8f6c092bdf2f2954e937168b780b2f8a7828d49f5b0d8809f25d74e6374065c4596c5a8ef2d2c4a18a0bbbb2ae9cb6d0f6332614270c4438ef07effd094df92b74cbb828305debaac629cb01a98e32d16e04d0b8c52c8a9a2c526481fd627d453e97bf33ea717775dc06abb09f0b95a435daf64e41d198ef6c686baffe891e9a1da8df943b12d1b5f192387b5b367db75593ace3399439621ec9d4195c188be12781cb165e559161381482bc513bb9a35d3746c30d88cc475642d63016c8a75347c1bb706bb3276218e6269e6e1a1c8912b077b5e82235ba837899c6c23bec2917457442f9f607d3d63b8c8ea5f065ca5e7a4ec037665a40003d9a0c95d97234ac9d50efa385cadfc79820a017abfd3f678ba5d7a8b890f5ca6c10ffbea407c6ac18638bde1806f739b7fd6ed49c665884f8cd4cdf77a9c02cad0ffa8971cdbe664ea7539521c87d9658808b112d1ea22b155208dfd693a48d5dd703279192ae5d9acab817c53193e17542db27d63764daccefb0be38b2bbdbd01345919e2f08811f99fb54a33033f6abc76c5ab4ebc9056f0e8d3005f80ffaf403ea744ff86a30fdc23f319972c4ccc81b3f6a23ad9fb00e480cd0b7d138e9db6e7cd25e424d29205782b06e4b0c5b932820ee2035fe657ec36489f60829d8c31dfe36accbc0fb78176508b6024b60909dba12747745d454c150f54945ef6a9a085e3a6b6539ace61f767e1e5fb1054355bf26622674d58d4b5037457138e50abfefe92b515c74c608fcd2a5af50eab0bc85d57ba3bda52fa00c9c15d44a24586b2cacc6be00209ae49202430645d4a5022703b1cace87829dc783d5092d97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e889200fa5344b9491cd5afeb2dcc03364b8a9380fa11a5c4ea09bb60d13aa217acc38fe3e83b5569bf75576dacb84cfc28cec75ee724fcb8b5d4fdb12948b1098ebc07" let tree = SaplingCommitmentTree $ hexString @@ -923,31 +935,101 @@ main = do let so1 = ShieldedOutput (hexString - "c84c0f8f7de5248ce38861de909f90eec765603c590c3b6c95c56efabe43748e") + "8e7443befa6ec5956c3b0c593c6065c7ee909f90de6188e38c24e57d8f0f4cc8") (hexString - "58f8ee8f4246f6a90f2e3d49d2ecbb116b9cde55ff4c7682b78d10bb510c43fa") + "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858") (hexString - "bf2dda6cd8ea626bfef2b8ba806227147b5649b2d814d02a07f6c72b8e2522d3") + "d322258e2bc7f6072ad014d8b249567b14276280bab8f2fe6b62ead86cda2dbf") (hexString "1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d8") (hexString "6459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa083") (hexString "85f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62") + let so2 = + ShieldedOutput + (hexString + "d283384bb097120b47b780b4a60b5c04d53e90c9ff2e781ac9b039384fbd48d1") + (hexString + "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843") + (hexString + "e007aea2e07f40f139e24d0e497eac26727a2b9aad4f223887cd423b9fd6cf58") + (hexString + "353f0b011ea73b2a9843c25529df7b88f2357fba75353218012d1ba4b89b45d50564e9e7f16101c69c4232d2de683bd0960cc87a016dce7bb088720f8f6c092bdf2f2954e937168b780b2f8a7828d49f5b0d8809f25d74e6374065c4596c5a8ef2d2c4a18a0bbbb2ae9cb6d0f6332614270c4438ef07effd094df92b74cbb828305debaac629cb01a98e32d16e04d0b8c52c8a9a2c526481fd627d453e97bf33ea717775dc06abb09f0b95a435daf64e41d198ef6c686baffe891e9a1da8df943b12d1b5f192387b5b367db75593ace3399439621ec9d4195c188be12781cb165e559161381482bc513bb9a35d3746c30d88cc475642d63016c8a75347c1bb706bb3276218e6269e6e1a1c8912b077b5e82235ba837899c6c23bec2917457442f9f607d3d63b8c8ea5f065ca5e7a4ec037665a40003d9a0c95d97234ac9d50efa385cadfc79820a017abfd3f678ba5d7a8b890f5ca6c10ffbea407c6ac18638bde1806f739b7fd6ed49c665884f8cd4cdf77a9c02cad0ffa8971cdbe664ea7539521c87d9658808b112d1ea22b155208dfd693a48d5dd703279192ae5d9acab817c53193e17542db27d63764daccefb0be38b2bbdbd01345919e2f08811f99fb54a33033f6abc76c5ab4ebc9056f0e8d3005f80ffaf403ea744ff86a30fdc23f319972c4ccc81b3f6a23ad9fb00e480cd0b7d138e9db6e7cd25e424d29205782b06e4b0c5b932820ee2035fe657ec36489f60829d8c31dfe36accbc0fb78176508b6024b60909dba12747745d454c150f54945ef6a9a085e3a6b6539ace61f767e1e5fb1") + (hexString + "054355bf26622674d58d4b5037457138e50abfefe92b515c74c608fcd2a5af50eab0bc85d57ba3bda52fa00c9c15d44a24586b2cacc6be00209ae49202430645d4a5022703b1cace87829dc783d5092d") + (hexString + "97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e8892") it "Sap output 1" $ do - let pos - --getSaplingNotePosition <$> - = - updateSaplingCommitmentTree - tree - (fromText - "58f8ee8f4246f6a90f2e3d49d2ecbb116b9cde55ff4c7682b78d10bb510c43fa") - pos `shouldNotBe` Nothing - {-case pos of-} - {-Nothing -> assertFailure "couldn't get note position"-} - {-Just p -> do-} - {-let dn = decodeSaplingOutputEsk sk so1 TestNet External p-} - {-dn `shouldNotBe` Nothing-} + let pos = + getSaplingNotePosition <$> + (getSaplingWitness =<< + updateSaplingCommitmentTree + tree + (fromText + "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")) + case pos of + Nothing -> assertFailure "couldn't get note position" + Just p -> do + let dn = decodeSaplingOutputEsk sk so1 TestNet External p + dn `shouldBe` Nothing + it "Sap output 2" $ do + case readZebraTransaction txHex2 of + Nothing -> assertFailure "Failed to read Tx" + Just tx -> do + let sBundle = fromRawSBundle $ zt_sBundle tx + case sBundle of + Nothing -> assertFailure "Failed to get sapling bundle" + Just sB -> do + let sOuts = sbOutputs sB + let pos = + getSaplingNotePosition <$> + (getSaplingWitness =<< + updateSaplingCommitmentTree + tree + (fromText + "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) + case pos of + Nothing -> assertFailure "couldn't get note position" + Just p -> do + let dn = + decodeSaplingOutputEsk + sk + (head . tail $ sOuts) + TestNet + External + p + dn `shouldBe` Nothing + it "Decode Sapling Output from Zingo" $ do + case readZebraTransaction txHex of + Nothing -> assertFailure "Failed to read Tx" + Just tx -> do + case sK of + Nothing -> assertFailure "Failed to generate spending key" + Just sK' -> do + let sBundle = fromRawSBundle $ zt_sBundle tx + case sBundle of + Nothing -> assertFailure "Failed to get sapling bundle" + Just sB -> do + let sOuts = sbOutputs sB + let pos = + getSaplingNotePosition <$> + (getSaplingWitness =<< + updateSaplingCommitmentTree + tree + (fromText + "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) + case pos of + Nothing -> assertFailure "couldn't get note position" + Just p -> do + let dn = + decodeSaplingOutputEsk + sK' + (head . tail $ sOuts) + MainNet + External + p + dn `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property From cb985d26cd88af7fb896356bd7b574d99db27c02 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 16 Apr 2024 11:19:53 -0500 Subject: [PATCH 116/149] Add debugging for Sapling decode --- librustzcash-wrapper/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6f03090..34533c0 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -766,19 +766,19 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( marshall_to_haskell_var(&hn, out, out_len, RW); }, None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 1000, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 2000, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 3000, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } From 382284a8ca3a67eca8f029091f336e82e0d0a373 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Tue, 16 Apr 2024 14:39:56 -0400 Subject: [PATCH 117/149] rvv040 - decodeSaplingAddress Function added SaplingAddress data type added --- CHANGELOG.md | 16 ++++++++++- librustzcash-wrapper/src/lib.rs | 48 +++++++++++++++++++++++++++++++++ src/C/Zcash.chs | 7 +++++ src/ZcashHaskell/Sapling.hs | 25 +++++++++++++++++ src/ZcashHaskell/Types.hs | 6 +++++ test/Spec.hs | 13 +++++++++ 6 files changed, 114 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ae53c..9f35ce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.1] + +### Added + +- Added unction to decode a Sappling Address in Human Readable Format + `decodeSaplingAddress` returns 43 byte array containing +- Added a new Datatype `SaplingAddress` +- Added a new FFI function `rust_wrapper_decode_sapling_address` to haskell-rust interface + +### Changed + +- `TransparentAddress` type refactored +- `TransparentReceiver` added to replace old `TransparentAddress` +- `sha256` Function moved outside of `encodeTransparentReceiver` + ## [0.5.5.0] ### Added @@ -16,7 +31,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `TransparentAddress` type refactored - `TransparentReceiver` added to replace old `TransparentAddress` - `sha256` Function moved outside of `encodeTransparentReceiver` - ## [0.5.4.1] ### Added diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 81f7235..fb89971 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -90,6 +90,7 @@ use zcash_address::{ ZcashAddress }; +use zcash_client_backend::encoding::decode_payment_address; use zcash_client_backend::keys::sapling::{ spending_key, ExtendedFullViewingKey, @@ -1242,3 +1243,50 @@ pub extern "C" fn rust_wrapper_read_sapling_position( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_decode_sapling_address( + sapling: *const u8, + sapling_len: usize, + out: *mut u8, + out_len: &mut usize){ + let sapling_address_from_haskell : Vec = marshall_from_haskell_var(sapling, sapling_len, RW); + let sapling_address = std::str::from_utf8(&sapling_address_from_haskell).unwrap(); + + let mut netid = 0; + match sapling_address.find("1") { + Some(ix) => { + let netstr = &sapling_address[0..ix]; + if netstr == "zs" { + netid = 1 + } else { + if netstr == "ztestsapling" { + netid = 2 + } + } + match decode_payment_address(netstr, sapling_address) { + Ok( t )=> { + let address_to_bytes = t.to_bytes(); + let mut out_bytes_temp : [u8;44] = [0;44]; + out_bytes_temp[0] = netid; + let mut iy = 1; + for ix in 0..43 { + out_bytes_temp[iy] = address_to_bytes[ix]; + iy += 1; + } + let out_bytes: Vec = out_bytes_temp.to_vec(); + marshall_to_haskell_var(&out_bytes, out, out_len, RW); + } + Err(e) => { + let h = vec![0]; + marshall_to_haskell_var(&h, out, out_len, RW); + } + } + } + None => { + let h = vec![0]; + marshall_to_haskell_var(&h, out, out_len, RW); + } + } +} + diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 4b08f78..239bc3e 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -232,3 +232,10 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_decode_sapling_address as rustWrapperDecodeSaplingAddress + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} + diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index a108eaa..d80abf8 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -22,6 +22,7 @@ import C.Zcash , rustWrapperReadSaplingCommitmentTree , rustWrapperReadSaplingPosition , rustWrapperReadSaplingWitness + , rustWrapperDecodeSaplingAddress , rustWrapperSaplingCheck , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingDecodeEsk @@ -45,6 +46,7 @@ import ZcashHaskell.Types , DecodedNote(..) , RawData(..) , RawTxResponse(..) + , SaplingAddress(..) , SaplingCommitmentTree(..) , SaplingReceiver(..) , SaplingSpendingKey(..) @@ -205,3 +207,26 @@ getSaplingWitness tree = getSaplingNotePosition :: SaplingWitness -> Integer getSaplingNotePosition = fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit + +getNetId:: [Word8] -> ZcashNet +getNetId [x] = do + case x of + 1 -> MainNet + 2 -> TestNet + + +-- | decode a Sapling address +decodeSaplingAddress :: BS.ByteString -> Maybe SaplingAddress +decodeSaplingAddress sapling_address = do + if BS.length sa > 1 + then do + let sa0 = BS.unpack sa + Just $ SaplingAddress (getNetId (take 1 sa0)) (BS.pack (drop 1 sa0)) + else Nothing + where + sa = + withPureBorshVarBuffer $ + rustWrapperDecodeSaplingAddress sapling_address + + + diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index c6cff22..5bc66f4 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -477,6 +477,12 @@ newtype SaplingReceiver = instance ToBytes SaplingReceiver where getBytes (SaplingReceiver s) = s +data SaplingAddress = SaplingAddress + { + net_type :: !ZcashNet + , address :: !BS.ByteString + } deriving (Eq, Prelude.Show, Read) + -- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method data ShieldedSpend = ShieldedSpend { sp_cv :: !HexString diff --git a/test/Spec.hs b/test/Spec.hs index 3f66d1d..0f10226 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -57,6 +57,7 @@ import ZcashHaskell.Sapling , isValidShieldedAddress , matchSaplingAddress , updateSaplingCommitmentTree + , decodeSaplingAddress ) import ZcashHaskell.Transparent import ZcashHaskell.Types @@ -94,6 +95,7 @@ import ZcashHaskell.Types , fromRawSBundle , fromRawTBundle , getValue + , SaplingAddress(..) ) import ZcashHaskell.Utils @@ -911,6 +913,17 @@ main = do t_rec u (ta_receiver <$> decodeTransparentAddress (E.encodeUtf8 tAdd)) `shouldBe` t_rec u + describe "Decode a Sapling Address (MainNet)" $ do + let sa = decodeSaplingAddress "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" + it "Try to decode a valid MainNet Sapling Address" $ do + print sa + sa `shouldNotBe` Nothing + describe "Decode a Sapling Address (TestNet)" $ do + let sa = decodeSaplingAddress "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5" + it "Try to decode a valid TestNet Sapling Address " $ do + print sa + sa `shouldBe` Nothing + -- | Properties prop_PhraseLength :: Property From 058bbfe3f29f154b237d55bf0e1572cc63b83b49 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Tue, 16 Apr 2024 14:48:36 -0400 Subject: [PATCH 118/149] rvv040 - Added a decode Testnet Sapling Address test --- test/Spec.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Spec.hs b/test/Spec.hs index 0f10226..37a45c5 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -922,7 +922,7 @@ main = do let sa = decodeSaplingAddress "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5" it "Try to decode a valid TestNet Sapling Address " $ do print sa - sa `shouldBe` Nothing + sa `shouldNotBe` Nothing -- | Properties From 4f2b88974c15bcd9768425bd057537c443bb175e Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 16 Apr 2024 15:13:41 -0500 Subject: [PATCH 119/149] Remove debugging --- librustzcash-wrapper/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 34533c0..6f03090 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -766,19 +766,19 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( marshall_to_haskell_var(&hn, out, out_len, RW); }, None => { - let hn0 = Hnote { note: 1000, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 2000, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e) => { - let hn0 = Hnote { note: 3000, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } From 06aff8c7879bceed47393b4ca3a23354b3937d7d Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Tue, 16 Apr 2024 18:51:14 -0400 Subject: [PATCH 120/149] rvv040 - Add encodeSaplingAddress function Added new tests for encodeSaplingAddress and decodeSaplingAddress --- CHANGELOG.md | 10 +++++++ src/ZcashHaskell/Sapling.hs | 60 +++++++++++++++++++++---------------- src/ZcashHaskell/Types.hs | 4 +-- test/Spec.hs | 24 +++++++++++---- 4 files changed, 66 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f35ce1..ecf9c65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.2] + +### Added + +- Added unction to encode a Sappling Address in Human Readable Format Using a SaplingReceiver + `encodeSaplingAddress` a zcash sapling address is returned or Nothing if the function fails +- Added decoding and encoding test + + + ## [0.5.5.1] ### Added diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index d80abf8..2b971a2 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -34,32 +34,35 @@ import C.Zcash ) import Data.Aeson import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as C +import qualified Data.Text as T import Data.HexString (HexString(..), fromText, hexString, toBytes, toText) import Data.Word import Foreign.Rust.Marshall.Variable ( withPureBorshVarBuffer , withPureBorshVarBuffer ) -import ZcashHaskell.Types - ( AccountId - , CoinType - , DecodedNote(..) - , RawData(..) - , RawTxResponse(..) - , SaplingAddress(..) - , SaplingCommitmentTree(..) - , SaplingReceiver(..) - , SaplingSpendingKey(..) - , SaplingWitness(..) - , Scope(..) - , Seed(..) - , ShieldedOutput(..) - , ToBytes(..) - , ZcashNet(..) - , decodeHexText - , getValue - ) -import ZcashHaskell.Utils (decodeBech32) +import ZcashHaskell.Types +--import ZcashHaskell.Types +-- ( AccountId +-- , CoinType +-- , DecodedNote(..) +-- , RawData(..) +-- , RawTxResponse(..) +-- , SaplingAddress(..) +-- , SaplingCommitmentTree(..) +-- , SaplingReceiver(..) +-- , SaplingSpendingKey(..) +-- , SaplingWitness(..) +-- , Scope(..) +-- , Seed(..) +-- , ShieldedOutput(..) +-- , ToBytes(..) +-- , ZcashNet(..) +-- , decodeHexText +-- , getValue +-- ) +import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m) -- | Check if given bytesting is a valid encoded shielded address isValidShieldedAddress :: BS.ByteString -> Bool @@ -208,25 +211,32 @@ getSaplingNotePosition :: SaplingWitness -> Integer getSaplingNotePosition = fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit +-- | Encode a SaplingReceiver into HRF text +encodeSaplingAddress :: ZcashNet -> SaplingReceiver -> Maybe T.Text +encodeSaplingAddress net sr = do + case net of + MainNet -> + Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sr) + TestNet -> + Just $ encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sr) + +-- | Helper to get de Nework Id from FFI response getNetId:: [Word8] -> ZcashNet getNetId [x] = do case x of 1 -> MainNet 2 -> TestNet - -- | decode a Sapling address decodeSaplingAddress :: BS.ByteString -> Maybe SaplingAddress decodeSaplingAddress sapling_address = do if BS.length sa > 1 then do let sa0 = BS.unpack sa - Just $ SaplingAddress (getNetId (take 1 sa0)) (BS.pack (drop 1 sa0)) + Just $ SaplingAddress (getNetId (take 1 sa0)) + $ SaplingReceiver (BS.pack (drop 1 sa0)) else Nothing where sa = withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address - - - diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 5bc66f4..08da042 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -480,9 +480,9 @@ instance ToBytes SaplingReceiver where data SaplingAddress = SaplingAddress { net_type :: !ZcashNet - , address :: !BS.ByteString + , sa_receiver :: !SaplingReceiver } deriving (Eq, Prelude.Show, Read) - + -- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method data ShieldedSpend = ShieldedSpend { sp_cv :: !HexString diff --git a/test/Spec.hs b/test/Spec.hs index 37a45c5..4f5fd1e 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -58,6 +58,7 @@ import ZcashHaskell.Sapling , matchSaplingAddress , updateSaplingCommitmentTree , decodeSaplingAddress + , encodeSaplingAddress ) import ZcashHaskell.Transparent import ZcashHaskell.Types @@ -916,14 +917,27 @@ main = do describe "Decode a Sapling Address (MainNet)" $ do let sa = decodeSaplingAddress "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" it "Try to decode a valid MainNet Sapling Address" $ do - print sa - sa `shouldNotBe` Nothing + case sa of + Nothing -> assertFailure "Failed to decode MainNet SaplingAddress" + Just s -> do -- Sapling address decoded succesfully + let sh = encodeSaplingAddress (net_type s) (sa_receiver s) + case sh of + Nothing -> assertFailure "Failed to encode MainNet SaplingAddress" + Just zsh -> do + print zsh + zsh `shouldBe` "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" describe "Decode a Sapling Address (TestNet)" $ do let sa = decodeSaplingAddress "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5" it "Try to decode a valid TestNet Sapling Address " $ do - print sa - sa `shouldNotBe` Nothing - + case sa of + Nothing -> assertFailure "Failed to decode TestNet SaplingAddress" + Just s -> do -- Sapling address decoded succesfully + let sh = encodeSaplingAddress (net_type s) (sa_receiver s) + case sh of + Nothing -> assertFailure "Failed to encode TestNet SaplingAddress" + Just zsh -> do + print zsh + zsh `shouldBe` "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5" -- | Properties prop_PhraseLength :: Property From a56a4b1e15b0ff355dd691fb5a44d2ecd2dc960b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 17 Apr 2024 09:21:47 -0500 Subject: [PATCH 121/149] Add Orchard commitment tree functionality --- librustzcash-wrapper/src/lib.rs | 90 +++++++++++++++++++++++++++++++++ src/C/Zcash.chs | 21 ++++++++ src/ZcashHaskell/Orchard.hs | 37 +++++++++++++- src/ZcashHaskell/Types.hs | 10 ++++ test/Spec.hs | 23 +++++++++ 5 files changed, 180 insertions(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6f03090..4806512 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -111,6 +111,7 @@ use orchard::{ note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, + tree::MerkleHashOrchard, value::ValueCommitment }; @@ -1256,3 +1257,92 @@ pub extern "C" fn rust_wrapper_read_sapling_position( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_read_orchard_commitment_tree( + tree: *const u8, + tree_len: usize, + node: *const u8, + node_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); + let tree_reader = Cursor::new(tree_in); + let ct = read_commitment_tree::>, 32>(tree_reader); + match ct { + Ok(mut comm_tree) => { + let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); + let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(node_in)); + if orchard_note_comm.is_some().into() { + let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); + comm_tree.append(n); + let mut out_bytes: Vec = Vec::new(); + let result = write_commitment_tree(&comm_tree, &mut out_bytes ); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } + } else { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} + +#[no_mangle] +pub extern "C" fn rust_wrapper_read_orchard_witness( + tree: *const u8, + tree_len: usize, + out: *mut u8, + out_len: &mut usize + ){ + let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); + let tree_reader = Cursor::new(tree_in); + let ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); + let inc_wit = IncrementalWitness::from_tree(ct); + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&inc_wit, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} + + +#[no_mangle] +pub extern "C" fn rust_wrapper_read_orchard_position( + wit: *const u8, + wit_len: usize, + ) -> u64 { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let path = iw.path(); + match path { + Some(p) => { + let pos = p.position(); + return u64::from(pos); + }, + None => { + return 0; + } + } +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 4b08f78..8eef4ad 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -232,3 +232,24 @@ import ZcashHaskell.Types -> `()' #} +{# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_read_orchard_witness as rustWrapperReadOrchardWitness + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun pure unsafe rust_wrapper_read_orchard_position as rustWrapperReadOrchardPosition + { toBorshVar* `BS.ByteString'& + } + -> `Word64' +#} + diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 8f57ad5..7e6fc6c 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -23,12 +23,15 @@ import C.Zcash , rustWrapperOrchardCheck , rustWrapperOrchardNoteDecode , rustWrapperOrchardNoteDecodeSK + , rustWrapperReadOrchardCommitmentTree + , rustWrapperReadOrchardPosition + , rustWrapperReadOrchardWitness , rustWrapperUADecode , rustWrapperUfvkDecode ) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C -import Data.HexString (fromRawBytes, toBytes) +import Data.HexString (HexString(..), fromRawBytes, toBytes) import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word @@ -184,3 +187,35 @@ decryptOrchardActionSK sk scope oa = decodedAction = withPureBorshVarBuffer $ rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External) + +-- | Update a Orchard commitment tree +updateOrchardCommitmentTree :: + OrchardCommitmentTree -- ^ the base tree + -> HexString -- ^ the new note commitment + -> Maybe OrchardCommitmentTree +updateOrchardCommitmentTree tree cmx = + if BS.length (hexBytes updatedTree) > 1 + then Just $ OrchardCommitmentTree updatedTree + else Nothing + where + updatedTree = + withPureBorshVarBuffer $ + rustWrapperReadOrchardCommitmentTree + (hexBytes $ orchTree tree) + (hexBytes cmx) + +-- | Get the Orchard incremental witness from a commitment tree +getOrchardWitness :: OrchardCommitmentTree -> Maybe OrchardWitness +getOrchardWitness tree = + if BS.length (hexBytes wit) > 1 + then Just $ OrchardWitness wit + else Nothing + where + wit = + withPureBorshVarBuffer $ + rustWrapperReadOrchardWitness (hexBytes $ orchTree tree) + +-- | Get the Sapling note position from a witness +getOrchardNotePosition :: OrchardWitness -> Integer +getOrchardNotePosition = + fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index f36bc89..f0daaab 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -611,6 +611,16 @@ instance FromJSON OrchardAction where a <- obj .: "spendAuthSig" pure $ OrchardAction n r c ephKey encText outText cval a +-- | Type for a Orchard note commitment tree +newtype OrchardCommitmentTree = OrchardCommitmentTree + { orchTree :: HexString + } deriving (Eq, Prelude.Show, Read) + +-- | Type for a Sapling incremental witness +newtype OrchardWitness = OrchardWitness + { orchWit :: HexString + } deriving (Eq, Prelude.Show, Read) + -- | Type to represent a decoded note data DecodedNote = DecodedNote { a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_. diff --git a/test/Spec.hs b/test/Spec.hs index f1cc5a9..b1001ff 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -66,7 +66,9 @@ import ZcashHaskell.Types , CoinType(..) , DecodedNote(..) , OrchardAction(..) + , OrchardCommitmentTree(..) , OrchardSpendingKey(..) + , OrchardWitness(..) , Phrase(..) , RawData(..) , RawOutPoint(..) @@ -877,6 +879,27 @@ main = do getSaplingNotePosition <$> (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1) p `shouldBe` Just 129405 + describe "Orchard commitment trees" $ do + let tree = + OrchardCommitmentTree $ + hexString + "01d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d01bb83047c72eb4f71813d00dee37082169546df2d7097bf7fd187ef6a93063b281f015e710ed46b53b48b12733652e150f9dcbc7e7b571cf64f294cf903864c78882f01cac32bc901f501f714a028f7ebe44c1dd8b42661be1c96730066a6fa6ede653600000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000" + let cmx = + hexString + "d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d" + it "Commitment tree is updated correctly" $ do + let t1 = updateOrchardCommitmentTree tree cmx + t1 `shouldNotBe` Nothing + it "Incremental witness is generated" $ do + let t1 = updateOrchardCommitmentTree tree cmx + case t1 of + Nothing -> assertFailure "Failed to append node to tree" + Just t -> getOrchardWitness t `shouldNotBe` Nothing + it "Position of note is obtained" $ do + let p = + getOrchardNotePosition <$> + (getOrchardWitness =<< updateOrchardCommitmentTree tree cmx) + p `shouldBe` Just 39432 describe "Extract Sapling Address - UA Valid" $ do let sr = getSaplingFromUA From 4efae180f5466b6efdff76c938ef17b886dc92bd Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 17 Apr 2024 11:18:56 -0500 Subject: [PATCH 122/149] Fix Rust binding for Sapling decode --- librustzcash-wrapper/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 5b71bc6..39bd98b 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1348,6 +1348,7 @@ pub extern "C" fn rust_wrapper_read_orchard_position( } } +#[no_mangle] pub extern "C" fn rust_wrapper_decode_sapling_address( sapling: *const u8, sapling_len: usize, From e75175852b1247f111bdfdcc57e417decfc481fd Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 17 Apr 2024 11:21:03 -0500 Subject: [PATCH 123/149] Version bump --- CHANGELOG.md | 4 +--- zcash-haskell.cabal | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf9c65..a387817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added unction to encode a Sappling Address in Human Readable Format Using a SaplingReceiver +- Added function to encode a Sappling Address in Human Readable Format Using a SaplingReceiver `encodeSaplingAddress` a zcash sapling address is returned or Nothing if the function fails - Added decoding and encoding test - - ## [0.5.5.1] ### Added diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 8d16f53..c7e5406 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.5.0 +version: 0.5.5.2 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From e6d16267ee2000c4ef70087fd19db746e8e0ae95 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Wed, 17 Apr 2024 20:36:04 -0400 Subject: [PATCH 124/149] rvv040 - Functions to create and decode an ExchangeAddress added New DataType ExchangeAddress added. --- src/ZcashHaskell/Sapling.hs | 19 --------- src/ZcashHaskell/Transparent.hs | 70 ++++++++++++++++++++++++++------- src/ZcashHaskell/Types.hs | 6 +++ test/Spec.hs | 16 ++++++++ 4 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index fb62a7a..6e3c7b0 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -43,25 +43,6 @@ import Foreign.Rust.Marshall.Variable , withPureBorshVarBuffer ) import ZcashHaskell.Types ---import ZcashHaskell.Types --- ( AccountId --- , CoinType --- , DecodedNote(..) --- , RawData(..) --- , RawTxResponse(..) --- , SaplingAddress(..) --- , SaplingCommitmentTree(..) --- , SaplingReceiver(..) --- , SaplingSpendingKey(..) --- , SaplingWitness(..) --- , Scope(..) --- , Seed(..) --- , ShieldedOutput(..) --- , ToBytes(..) --- , ZcashNet(..) --- , decodeHexText --- , getValue --- ) import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m) -- | Check if given bytesting is a valid encoded shielded address diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 366dfe4..a76be58 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -22,7 +22,8 @@ import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) import Data.Char (chr) -import Data.HexString +import qualified Data.ByteString.Char8 as BC +import Data.HexString import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word @@ -30,19 +31,20 @@ import Haskoin.Address (Address(..)) import qualified Haskoin.Crypto.Hash as H import Haskoin.Crypto.Keys.Extended import ZcashHaskell.Types - ( AccountId - , CoinType(..) - , Scope(..) - , Seed(..) - , ToBytes(..) - , TransparentAddress(..) - , TransparentReceiver(..) - , TransparentSpendingKey(..) - , TransparentType(..) - , ZcashNet(..) - , getTransparentPrefix - , getValue - ) +-- ( AccountId +-- , CoinType(..) +-- , Scope(..) +-- , Seed(..) +-- , ToBytes(..) +-- , TransparentAddress(..) +-- , TransparentReceiver(..) +-- , TransparentSpendingKey(..) +-- , TransparentType(..) +-- , ZcashNet(..) +-- , getTransparentPrefix +-- , getValue +-- ) +import ZcashHaskell.Utils( encodeBech32m, decodeBech32 ) -- | Required for `TransparentReceiver` encoding and decoding sha256 :: BS.ByteString -> BS.ByteString @@ -142,3 +144,43 @@ decodeTransparentAddress taddress = do TransparentReceiver P2PKH (fromRawBytes transparentReceiver) else Nothing _ -> Nothing + +-- | Encode an Exchange Addresss into HRF from TransparentReceiver +encodeExchangeAddress:: ZcashNet -> TransparentReceiver -> Maybe T.Text +encodeExchangeAddress net tr = do + case (tr_type tr) of + P2PKH -> do + case net of + MainNet -> do + let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr)) + Just vhash + TestNet -> do + let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr)) + Just vhash + _ -> Nothing + +-- | Decode an Exchange Address into a TransparentAddress +decodeExchangeAddress:: T.Text-> Maybe TransparentAddress +decodeExchangeAddress ex = do + if (T.length ex ) > 1 + then do + let rawd = decodeBech32 (E.encodeUtf8 ex) + let tMain = BS.unpack (BC.pack "tex") + let tTest = BS.unpack (BC.pack "textest") + let tFail = BS.unpack (BC.pack "fail") + let hr = BS.unpack (hrp rawd) + if hr /= tFail + then do + let transparentReceiver = bytes rawd + if hr == tMain + then Just $ + TransparentAddress MainNet $ + TransparentReceiver P2PKH (fromRawBytes transparentReceiver) + else do + if hr == tTest + then Just $ + TransparentAddress TestNet $ + TransparentReceiver P2PKH (fromRawBytes transparentReceiver) + else Nothing + else Nothing + else Nothing \ No newline at end of file diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index c6d9c34..54da7aa 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -434,6 +434,12 @@ data TransparentAddress = TransparentAddress , ta_receiver :: !TransparentReceiver } deriving (Eq, Prelude.Show, Read) +-- | Type to represent a TEX Zcash addresses +data ExchangeAddress = ExchangeAddress + { ex_network :: !ZcashNet + , ex_address :: !TransparentReceiver + } deriving (Eq, Prelude.Show, Read) + -- | Wrapper types for transparent elements data RawTxIn = RawTxIn { rti_outpoint :: !RawOutPoint diff --git a/test/Spec.hs b/test/Spec.hs index 5931df8..245b824 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1068,6 +1068,22 @@ main = do p dn `shouldNotBe` Nothing + describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do + let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" + it "Try to generate valid ExchangeAddress from Transparent Address" $ do + case ta of + Nothing -> assertFailure "Failed to decode transparent address" + Just t -> do + case (tr_type (ta_receiver t) ) of + P2SH -> assertFailure "P2SH not supported for ExchengeAddress generation" + P2PKH -> do + let exch = encodeExchangeAddress (ta_network t) (ta_receiver t) + case exch of + Nothing -> assertFailure "Failed to encode Exchange address" + Just addr -> do + let tadr = decodeExchangeAddress addr + tadr `shouldNotBe` Nothing + -- | Properties prop_PhraseLength :: Property prop_PhraseLength = From 1b2c6dce2486b656f2fa7a6324d180d1d74e8c77 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Wed, 17 Apr 2024 20:51:12 -0400 Subject: [PATCH 125/149] rvv040 - Update CHANGE.LOG file --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf9c65..42449e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.3] + +### Added + +- Added unction to generate an ExchangeAddress in Human Readable Format Using a TransparentAddress in HRF + `encodeExchangeAddress` a function to create a ExchangeAddress in HRF + `decodeExchangeAddress` a function to obtaina a TransparentAddress object from an ExchangeAddress in HRF +- Added new type ExchangeAddress + ## [0.5.5.2] ### Added @@ -13,8 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `encodeSaplingAddress` a zcash sapling address is returned or Nothing if the function fails - Added decoding and encoding test - - ## [0.5.5.1] ### Added From 7d20edd08db75894cb8d5928c7f5584cd196bcf8 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 17 Apr 2024 20:14:03 -0500 Subject: [PATCH 126/149] Fix Orchard nullifier calculation --- CHANGELOG.md | 6 ++++++ librustzcash-wrapper/src/lib.rs | 2 +- zcash-haskell.cabal | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a387817..f7d844c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.3] + +### Fixed + +- Orchard note nullifier calculation + ## [0.5.5.2] ### Added diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 39bd98b..e42f374 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -908,7 +908,7 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk( 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(), nullifier: vec![0]}; + let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec()}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index c7e5406..be2387f 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.5.2 +version: 0.5.5.3 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From ebfac8438f8daacb8156c40b8694dcc7412c34d9 Mon Sep 17 00:00:00 2001 From: "Rene V. Vergara" Date: Thu, 18 Apr 2024 08:30:14 -0400 Subject: [PATCH 127/149] rvv040 - decodeExchangeAddress modified to return an ExcahngeAddress object --- src/ZcashHaskell/Transparent.hs | 8 ++++---- test/Spec.hs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index a76be58..4bdc46d 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -159,8 +159,8 @@ encodeExchangeAddress net tr = do Just vhash _ -> Nothing --- | Decode an Exchange Address into a TransparentAddress -decodeExchangeAddress:: T.Text-> Maybe TransparentAddress +-- | Decode an Exchange Address into a ExchangeAddress +decodeExchangeAddress:: T.Text-> Maybe ExchangeAddress decodeExchangeAddress ex = do if (T.length ex ) > 1 then do @@ -174,12 +174,12 @@ decodeExchangeAddress ex = do let transparentReceiver = bytes rawd if hr == tMain then Just $ - TransparentAddress MainNet $ + ExchangeAddress MainNet $ TransparentReceiver P2PKH (fromRawBytes transparentReceiver) else do if hr == tTest then Just $ - TransparentAddress TestNet $ + ExchangeAddress TestNet $ TransparentReceiver P2PKH (fromRawBytes transparentReceiver) else Nothing else Nothing diff --git a/test/Spec.hs b/test/Spec.hs index 245b824..7c868e7 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1081,8 +1081,8 @@ main = do case exch of Nothing -> assertFailure "Failed to encode Exchange address" Just addr -> do - let tadr = decodeExchangeAddress addr - tadr `shouldNotBe` Nothing + let eadr = decodeExchangeAddress addr + eadr `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property From 3296909c82ce20c587c8919646d96b01cc10203e Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 24 Apr 2024 11:57:00 -0500 Subject: [PATCH 128/149] Fix Sapling spend parsing --- CHANGELOG.md | 1 + librustzcash-wrapper/src/lib.rs | 16 ++++++++-------- test/Spec.hs | 26 +++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d844c..e077efd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Orchard note nullifier calculation +- Sapling spend field parsing ## [0.5.5.2] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index e42f374..ab7c934 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -401,9 +401,9 @@ impl ToHaskell for HSBundle { impl HSBundle { pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { - let s = Cursor::new(Vec::new()); - sb.authorization().binding_sig.write(s.clone()).unwrap(); - return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s.into_inner() } + let mut s: Vec = Vec::new(); + sb.authorization().binding_sig.write(&mut s).unwrap(); + return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s } } } @@ -428,11 +428,11 @@ impl Hspend { pub fn pack(sp: &[SpendDescription]) -> Vec { let mut r = Vec::new(); for s in sp { - let rk = Cursor::new(Vec::new()); - let authsig = Cursor::new(Vec::new()); - s.rk().write(rk.clone()).unwrap(); - s.spend_auth_sig().write(authsig.clone()).unwrap(); - r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk.into_inner()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig.into_inner()}}); + let mut rk = Vec::new(); + let mut authsig = Vec::new(); + s.rk().write(&mut rk).unwrap(); + s.spend_auth_sig().write(&mut authsig).unwrap(); + r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig}}); } return r } diff --git a/test/Spec.hs b/test/Spec.hs index 49e985c..be1b49a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -798,7 +798,11 @@ main = do let h = hexString "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" + let h1 = + hexString + "0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000" let t = readZebraTransaction h + let t1 = readZebraTransaction h1 it "Transparent output is identified" $ do case t of Nothing -> assertFailure "Couldn't decode" @@ -820,7 +824,27 @@ main = do (\(TxOut v s) -> s) (head (tb_vout myTb')) pkHash `shouldBe` maybe "" (hexBytes . tr_bytes) (t_rec addr) - myTb `shouldNotBe` Nothing + it "Transparent input is identified" $ do + case t1 of + Nothing -> assertFailure "Couldn't decode" + Just t1' -> do + let tb1 = zt_tBundle t1' + print tb1 + let txInHex = + HexString + "Km\237=.\228>T\DC3\\\EOT\249\163\ENQ\180s\215\215A\187\230\243\131\170cn\ETX\233Hp^\r" + toText txInHex `shouldBe` "Blank" + it "Sapling component is read" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let sb = zt_sBundle t' + fromRawSBundle sb `shouldNotBe` Nothing + describe "Raw transaction with Sap spend" $ do + let h = + hexString + "050000800a27a726b4d0d6c200000000c3c02a0000000111ac7f9600d034fec892f50600006b09ad88c990a8f3c5bffb6856beebd0ce356c5d1413c63a9a88db71c3f41dc12cd60197ee742fc75b217215e7144db48bd3fa55de211e0c8d7da70aba0efe17124b5250fc9fa095c8e50b53168142eb7ec402289a84ab762ddafb0f972fc07c61eb45249922752dfac17fd704a3b6156a6955286c04587f37fc781a9a0b28e92664dde2300c6321dd21d367c16b4c2b510e5d0a4358624e1ceb816da155e588726f783ed6bf396eaecada3d3bde7a0722419014c509a7fe264003af5388c5c8cdf2076137b26cf66383b41dea2af415f3d16767124616609b96789e2811f7bc8b36c0b746d225c0aa2c98b370fd39b2015eea43d523e5fbccc7ca127e59bc92ac8ad32d05243d278a26f07f33f45df23c3e8dd40d72af35a70068cc3f8445478c9cd3a56d3515e207a1b95c39c9d8b45e66827cf63e6018bf1012a47aa23ec0174735949f62036df3f677f92d3aa10365e2d358d92f77e9a6294c245eb8536493e8480f5215cdf7c863623727efa188fd8679f95318d0072915dc2be8e77b5778c48d3a2ea614990984c29dfaf7c582d269f0e9ffe6e3c475c99dbf5f8756c334b3c7b5954d68549c65fc2648bb389ba63397525829c48f7f32e43e866dfbc8ad86f54a867e592524f9c43b007328b78220cab4537c2d65f64c819ecc19ae09e17b3c84208cc169c2eaf8831d452dc83527a966b288aacce6f261aa8ed23e2c0613c48161c1eb0199915ee9c1f9cf0e7e5445539f5016b3ff701408b45557154a53e48998b49a2d149a67202caa6b585348b42fbeda770e084128760c742537743ba8cec4f997fe575db9cb370d89278c607a598ff06f1ece047855ea0a3d808d84272e22bcdeb2eb5bbe5754947be5d6fe42038d26f13313e039adf7e1091a901a6daa24b247b13c810eb5b50cd6040463c6bfa209a792d77eafb0f063167b11777d97ad05ca7eec1f1a45698e33f8e8b11a0ab0ad16ec10e4abba6de69255e0e00c7e712503a107012a46fcb9c5186a1fc5be96dcc279fa3dd18e69ee4a8ba75edaff105716b4751717aa818d514620ddd13a6716729417571a342dcc8a6f341d05db7614e729491095e4bcc6fbdec2908b6f74d7db81106ab2e1c0ee6217ba3cbb174232a42f2e6aba7e92eb2934dcd91c5a36eae1aac5160bf6076a39baeea84b8bad043c565eb8c4206a74b68c9342f9d7b1b31eec86bb9c5c73549313dd2cbe3bce55ecfdd952f3eb4dd5fa7c2000727b62a0faeb0fac1cf05246221c37560359c44610ee8f93cdd1179fc35fbd808152f507e790eb2140faccb2f6c9897a3a219d8752388e846c7277d82dc80850aeef2de46eb2d81563d53714d39e3553f5133abeae6c487d10664e1b10a90a5319adfa7471aa429095bd272ff4d3df227bdc6075e0cafba54201bddc44edcdfd2317f682f40a25e582273c4845b550b17724eca81e89268eafef9e98533b85bb2c54cdcb6beb4d6f2fc0459a2b36cbb43fcbab720678553fc930063c64a09445ff637c12d0ade4acdef1e19e8481a0b7c2dd968a2ceb7f50efbb76fe21f49d8829d8a26b10453626593a694586a828705c5ab02cccd60e351171aea3e1aaeda07cd960e0f5205019766ff3afe124a47f4ef16f0e671e1968e662c7974ee54872538c7019cc5396f165013f4c482d400ed4ff5fb34189c75e92d336cb25563867215dc3aa195f736e06657c9e27f1468acd580ee51ca44220be83c215c1223758a7676d376ea0d0aa74b73032b85a45f69e6f8d4ec3efb6861d5adc35f57e6112dfa7334ba6fbd64b14498a89d8596423125b6486d9208937e9aa010831edcc168be8b7abe7ac21cae4794fe5f019abe1715d5d8601b646767d396e1b7fb8ff50aa43d88a2426545270093ce80be1aaa849dd2edc0310e3cb0bc13a23108776a44e3250fe5aa89b6d62159206377e03e302e3dabff9ecd8675870c7d55e33cb915d7e73b765d1c61f9ba0d504429fe99516df0534cf6a5ad53424b9be39e4835dfb301ae5cbd9edd628f0cdb6c54bd4986a9fba65f60e833bf17b461746ffcc312d9584a55c55c42c8e760d017be56f54a4e1423648c3877f8a44e3a56c21bf2bf85550f3d8a8f040c8c6687aadb2c4d1e1613626a11d30fb25340f5f14b6a7752c5e6c7b866b10f1155360f35ddc8f07ed591fa3366948fbcde1a16c35a589457ded90b69675bad01a1fa9386e68dd2001498d41033a6effe882d9e864ea7991e0c71b3e65c158cba64014f800000000003b38927526d5fc5a1b2f16addcd4aa162272f280e977087de02ec2969f10d25ea3de9a5d3d3975b4b683897bf828be631813bd5b949ddf9c3c5b103b69aa483f5fc46485bc7a5dd51a47543f57d346e680e12d9429ef7c21cacf407656779235d205dffae23666e01d92fb18c063d1bd7cd00935f029149c7e20b3d72b1b951117f0629b1ef1e49047532a59fd8bc1817343d45e832078806eb10304b8e034fdce70464d33cf6c66e857fbda170573a7965f8eed58e6cb7edec4e9f8a666fbca0aeeca2d4d161caae23005f6e0adf6859c8f1bece409aa5d44d285a8e8e9fe86365af4e92384dce318724443e8a95cc790df47cdd3e8aa9ccf19acc5eb19b4cb517c3f63e59512c037a66312786cb6cd2bcd4480e0a21ac63a83b7b9c84d3207a6410aa51a60d15b5526e3ffb4cbb02ee9f43f7ce316ef01dd75544224481d4b8ad2a8944928e2b4432812d1b20aeae3b51808028321588952ea70afeef1709eff5a75463a2201299484ecc421c8452e1700ca9f4932b0aba6b5d46f36e80f2f0297eaf7f44a79cbd21046ca53073ee60d5f2ffe2352b2538326e3e7ed851e8a62612a9b974ceb5757835b974892b049a7a0c531390a4455657b5b5afc5f0f09f8bf838313f403c96c4c2c72c4ced6e642e86b0eff220c1cd3388ffc902af68f87a1b7e8920d7f014e6cd2694b926fcbef23dc645cd75b051add0fc2f99c0346d45c2521b27a00a13dc9754306222f54a2b4f3058e80e0821d6c78e5e80a509525631f34c343567513636855d1c318c49debc651212938c487e069c390868ce700c4d19e1e52eb7b536e1568d03421c3769b920224113bea4d345510bd0393efa1ec22b87e973c663be75c84e7278108ada31af0311796de927257f59823f1ee9955f0f39b618054476658c165ef443d169afe46007ac80a0efa3d678c2707a7a001812f02dac999796872d3328eeaab4b61cadb8be9c4ee0ff9104d417ba71076054735c12fabd5d78f6de83a57f46b8d071e3c273c60a226fa6d555fa3580002cf3b6f3e50a8f5616eef66e7bb28523858315b3cde381003ab885ece89973aa4e228cd5da0d24ebd876097e7664a2b903430963517bb895435703029ea30081a874d15f4997dec3b89cd728075a0b21d838a57052f02fad03c1a3f74427d149c6225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631585288b7ac6c67346b1d9ab1ee7d24e8cb7e35f2d2b5401a89af6a1648d19f8fcd14973c4c2db543d772b81bc363e707bc534f00b872df0e3d4c73e9bc513b2e6c49e07d4475f251c21d60cf8dc2c9aed2922e4efbe8cad80db14531a63e629f4cfb01df3f61fc470123a7caa141f104fb37d2712628648eec56759bd59cff1da5e7cac043539a2040b6d364f1449ec104bec6f487dd1f784dfe377d070fe704bcfaf4ca99cabf23f4ac1f58afdcd7c8a58ede5023034cb324722e349135c0125f49a7b4fcb22d81d641c06dd0cad8d0bcadd5c4ab7c2015ad7f98b1e4db7b89dd2aa3d057b13456f4ba3b0d52d3d0c4efb35deae8d1bfa39f593be4cd8311f6e89ac462a116ab4776576c1f87b7aff41ddc728fbc601aac334148307d9a3be7c11c4b122045ef2ff913eea3af62bff2b71433f3b65f8ea939fdc19877e98a70c8cce6b94eb2bf4d696392183f8224132b004a29db1fc0702b3c84e0d345d82cd622ff9fcf567b7dd88163ca21a32122f7c2c98c8d9d0861c02548a831e5e99a064b2be13774f76669b80f90cf8c1c0749dd646dabd68d18bb6e6f84dd8ee32acc2edce1c7d02b3b145f416b54b7bbeece8087eb03f75dd317caacfe8cb2c4573f4ff9beaa2c5ddc75b427db079c7951b88acdb8a7f4dbde154cd399cf579aa683e30bb430533b3241c8703bc6576666ffd299fb4d623f277b1f8be372f705ffe594644eed3ef2bb7070e843851b11252ae8aa8414ba17c94e63685815376246361ddc46301c995a2a6857004376bdee9c92f908595977109a0074ea3a8ea89131454429deb02d6431e3514e50632000117106dba12cae3925f260d527a7bb2cd6851cc91e5d4162a2b66b88540c1110cee4672b23b32a9269eeb86063df9d076ece42a9c7938c6fd5d037a4e4ac46942c365c75b75aa08c403690de155f96ddd23a3183653a9db614f2bf069620be62842947f6aa8abc340140aea22fa3b1fa7fb552a4f0a43fc89b3790cd3721967624451f94983d90d65063d4d9c60bad78f7bb11bdb8e6af9f806bcb34d7fdb8a2da4f6c3bc27ce4a48ad529f6a5b6e055f35c344a1dd2d6a085d1fa5bbfe9fcf6bda46b320e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e7f01c4200214e2969fb6c0fab8dc4ddb8475e28a21eb6b902d542dba5e4ad0b3b89b76ea777e6d9152feab5b0dd55f4f2d3db5f4e4013e8eb6f637f661f4f5b6ea8a73437437d14190bab1fc44df4fa78558c2b53e23283f4553101daf28f7c4ec5d9d93c47ace2ccf6d0514e0d33ea597b07f88ba4c272def6ba368f0239ba59003728fc20e9cc3e3c367a04f8faefb5a9725f0c8fa298aae306089908168cd53ea4fe769175863360f8838dbb3a185612d35b97aabd00c4fcc40245349196190a01f3b74fe6ac7f1dc854be928108bb23c67742fe4845b79b6286d99771d85bd9145b20d94b38ad9b273ccbad0ddeb5b7ba1ac322a20619b4d1ca9476ce741a0cca4c90b3b783f3b3d5064a7c15f72b97836db164d77661d415b12df263b1ad2e2efe92c5ccc50ba10c2d5d2debf89132649aa3163a23648f9d53bf14f183b920d0379ea04897a6b9245aa22ed2e4d913bace308307ea05d31753a43f62968f2653a6aad5fb483e4e78bbc42b4b853d92c81bb9c4c03934dafc8daf9ad16b3252d29028a9e5ea4278e928570c1bd112d6900df3da90a8eea441fcd1a73a1d982b010e6e7f73023dc36fbff9600f788da14b1410b31fff4ab2cd895f318aa9cc7baf9aaff1066ae4be169f663b6bdca53fa462174b56c6883e1b4b84df12b1f246de72e1af85f913000407ac30410149d1e4255ce94bc26b6ae62223f238be472f0dc9c812d137d88a7fa3fc0808693f9add9db0827d40d3dfca3dac9000a12b56d8803aff5517682e9cda163128fe166033397b79afee3f74528ccc31e3c5289812ee730cf5011ae15b0c86740644806048ac2282b7ddaed2e469c289cc70689a385e2c0459affae17a3e6d3815433363be215da2c110369a6ae12325d3c40340df4d77d09106f75d51d0c481bcf8228a4152c4dc45026f4e8de1270e04b60ac57ba0bf116fa003cc65d8ecc210e757365287203582608ffffffffffbc221f01e2f8306f906e82bb3b0952ca54890e06591275b726215e27c828761bfd601ce344fd29dfbfb9b37d62c86723a0d6a75fa6d2d8bd0c6ce822109d0ce816c8add2ea6343855d5e9ad63f3a730020eb46909af6d606ee3b5811cb807b9bfe2b2033d9ac8732b2b0eb9b28bec9eb6133f6c20b4c05e1dfa77155d289bbaa3ea6042e65326d32224e00b96f4a7af04461e1dd662284d566d6a9de2e435caca9c51fee6b665d6a1f641509b176cb29ff3fc67401e766f5ca1fab7584b36396eaba2f05e5ad0cbbba29c5372d5443f9afb4e2411f1284da03f9cbd2827ca725afe395024bc9436910053f8f8c4cef37acfc78cc05d7de0c77fc1492ef46469d93c49aadd42036c5fd6ef8d0c70d7c57e0e0e9f75ac94bbfaac9373ddc202f3393bc0f37b0a7769fca64300cd3594a29536692e575c9ef4e5dd544e108cad4ff895b24b431a86f1da264ede7b4b452e600035a38c902557b1ebb2abead1783765e778e7bec298ac9e021c82fbca82acf80094093afaa499f55a164237303d2ddfd0f8726320a0260dc2d59961c33c2d89ba40e65d46075b2d3206d76b27d00575a963489d2f168a53a0b5f04227d4cf935a2e82917c018270b7714b5598c838ed82e9ddfbdaa4448858db050cd14c500ac0e5fd566610e1d4ae2534565715cc96e499e5c190be37eea9d8fd23814658038fcc761b887568097da83232ee39b47e73c9ac854b8e70f38f6999e6f889a16ade97b9b206eae237f597f8ba0d2f2bf8f3794ea782f2264e9688a717f20431734d1bb989bfb4e40d3f4e8ccf1b8e938802a27402bd3a02d3d073e555acc5c4d941d5fbb3fef890b2e59a6a745b2e966c1903a988d586b53f63bc2a9253c7afb73f204fd3aa4a82332b0b010da159f29176ba146769d340399225d61bfad40ee21f765c641552d41b46bf5113de89318a6b0ab66ce02aea59ee302d8db4b2b1b4c71693b5159fd53627844f3b24288f585f536a52a3dcc4c71227b3a1489a5d31fc37e5a708bd724a889efec04c0c57730352493c5aead0eeb412b03a7514c24f4f3e659d529796218bb825c901da92869bc2b93afd359e24c8d55006d883e2380be6aa77d55218e831a7d5c4d566e293e4110b02ccc0508902a3d876dfd31f83b7b51b7d0d82d5838f6ed96ebb7dacf438526387a0b5053b522db03e5241b8069e9d43042922d4cb2a8c1112b05ccaae37e23511cdc90c6895713f7574f615df37c425d7cfa368ec72703a464b1d347c6db13d6496ffbb868594a330c213fd222a0ac618f1b312f4d6f5dd078ceafa20691aec35fd3b8d6479a63b82bff06a6eb619ae87ade1a5542b9582b1effde9e5d1db3c619458fab459ea2f91232089b716afa2de4ac9830830ce49284de17556de89400e2555960fee44db6379085636a5e4b938b1cd067f8d5bece171d463b432225a1290c306727187e1d2d482662c783736700be93e3f9304e0113d244e8fe4e1b38be3ec342a5a5c062cf1e20f3dc60f519947156221ee244b7e14c39f495720e4b727daf2e39c2e352b0a247660550ca8f82796db4e424be367d3d1ab70eb9b37649d9634c456d889354be44a45c61c37b5def297fc36a76d9dd676c2f643d997ab97b7735f914fbdd38f86508406a8de7dac15a1cb5ac5d9f90eb1f8b2301906c52d5c21674fe09e7f3b9c5990250efb5a890c420aeb1c5fe137755416d69aecc7549168876eb00f71865d04c946f11976cae4085eebd4e73be71ed8505470b0bfb446d735fe1afa33380ceeece76a8347e8fe2f58e34a579fd57468968d712218cbc99a68c3377a6ac542888542fc36fef2cf55c0a99266766abe6ecdbd00675c0935afe8cf8d938c1df6f6f4c849d72f354da1f09b6e7fc9b497544cf6930050d6ab8980e09feb965eebd50e91efebdb234e533372a67445de2088803328f429a8d6923899b849fb7f0af2132808544cb6bf99d0b18abe3df119bb5fdf5818e836064e5a0b329339c35ec7481628a71d35a7f5021001f72bc2b98a15aaabb4c5c462b44db100efb3a3589ec4b9f5e7900dc293443d9e655c8a50e51a00980be62f3821174c918500f9f06a25e109d3f7d63d588271a2a502e89a2d084350af0089aa4ca95be15133fac659f79df3bdde5384c2c25cdc4f94cffccfc125aba0fcdbfaa5fa9a18821f17a807aae39e4307f3b6d207a45f81877a988615473bbab0a580ae41e911fc54edd3b0cfa2391ba4c88945d2fdfd63c9a9905c2f847beb833d429af910fecd4a301e09244743d21439a66ec026ccf1ef1fd5f89d8600eaa5e07328c315205c005ed4caa4514df692a59db7b218e7543499323d4f90004398e425f77dccb3e35374bdbfc1daf35a4eed54758a1cdbe35a52d597f9cf0b3414d00d2dd5cf8dcfe51b2af90ddb2c0c59b391bead7b8fa2073061b4dd04406f2e7cda52ff000bc15dc02bf2bf22838e0ef641845bc4ce6c143d56e9a3b833e5cf4186978bf7c0e13b40e5e7f1d0ef28a3b388fa16619c13bb47d5eab57fb1c4ecc0b2ebfbf26200075b0cf5137e8f13703b7fdebd72218f32b8a0050c6633b5f1a3b9c62c83e81fc1c55741e759c1274d77a029eb7af4c3fd689331b69f80343332f541d1026a66c49fc5bd7012847d4d48be2b41064293785105872300f2ef8cd8f8b9892e96309456ecebfc893404da002867264d0a3caab84867488bb269e1ecc346d1ef7c1c76b6d16882dc5ea9dbee597dfa2d3be8e79091236dd750ad10a50e2f9f6adc1722f6131cc4986b41cd4acbe479f6ece8eb0892345662a2a0064ed74cd74957e796c4fac9c7145b0103e60750bc351f9d6d72faf2858ef23b107c07caff3940696ccf74e41001f17aa29edb068eeb6ec0b0bff10fab2571356ff5a3cff63767cad097f9dd4a5919700bd7e24182acdabf903171788800f17d5005dc07d6d5bed1dec81e13bb33196852dbcf978b45932bc28e60a83900502b11f09ff61feb208682500654cc9d2b42b60ecf99724bec398dd6829bd44a30151ab5d05c39284be657e7dde539668b5dd0b8ddcb331c4fcacb7b9c9520cc01c421c9955d7b168a9416e36c6e436e6d328d9e0fadd441fbd0d1d5bdbebbe9c19a3607746d06282c0d440ac8a243c47c27f04ac2d24620ee83fa6b1319fb4a137d1c82c436676f8d75f1b9d99b01c6f9cb4a91027631cc5717d07961179bac417ef8ae8bec969828872794cdda7b6f2e7472c50497a35782ca5841d5419ef3805c9fb30b8205f8fad2226e67048a4923de049ff3ae1f804b3ed33637c81e7530e970e2f51f9bc435e2d4ce3ebc8aea207115a4ec369a4caa7a3fc814224e8fa0e9a85f1c928ba24507d2ba16e176ad8cac7c125b5edef7d8efe1423c857949a1768748377a988d68098502720e75e138abda3f73eb9b2afdd50ef2f8d20ed5f36343fddeb59a8a591582d2d4c66ec0a89dccab3bc6cdc5aaf93a7518dd1c3a92ca486b6833bed70294d6d04b02803e4841a7faf9ab06a9c05ef672ba2b61c5e004bdfd6e490da458ccfa58be7dfef9dcc0dc79179f14659d8c4456045e48ce0039982580b9d7240209d4e5767b0bb391ef1f8929bf06d5e3c8226ecbdf8844902eab10f22b9f23381ba1b5f5c241a614b0a3cde02559669e0520ba5116f58e12fca16603e129e2e5315d581acc0d2c6ff9caaa7742b6c3e28d37f0766ed08a33f39cff6919af762d4ad2168614a190700aa87a95759951364d8445e680cc9ad0b75827810546a9510926d97835d62a65a3e5a375150b6e9a96fdca74a59269b2dd788320b96d92186f3226031684c45ab69c26cb754296a2a40c0026eb95c4f0b64db9872710d731d42c7510922dc4459600886d44586099fb957f0b6158ad703d11569699664e839d8acff3a5261f8b72cc43763f02568dea203b65be9cb06161a09d17203f168cd8469d805475dccc7c76ae2ef216e068f39ec7344f05c3d2ad794cacca891b3d0bfed335c1519203d289f74b896cb6c33c15ca0791ffeaf11c9d91b95438be68769f91d08997b77905d5a89460a3619dd44d5d92b5f88542521763c815ce15bf4f34c018297d777a602e27276021619a82a21be68cb5f4b321bca1baa7befd5cdffc80121a1fa379004f441148057d6f1f66863921c3bdd3117bb5d9ad52c45ac9a45391ec83cee3c6dcdfc40ddb3ed25a87f793430dc440a6d33218a04426b5bb7031d33b2439f01ab2fc2f64a43d2a482b1428ff5417d2d33c818166c20effbf46811d50d9d05f7ef1a37d249f64689cb37d8cbca2dd7086e141caea4c00b35286c9601f5f1cb6186452405c4a975e1c13cc10cf33e5908287f5f3fe36af89f374d172403dadf01bd06055582753b0a0a387361b91f233e6beb112b9b1eaf0a835b4d14c82477ef46868d09d03601a88f4da2dc7cef2a04a62e61311cc0284e8e48c571f622bf5ee95992ef425227d6b9272b6727b10005c53778ae7a6b155f3a7f00086e1e952527cee0d40ddd5e0caefdb19a082ea43d85dec0168d5641b570fbcbbe9058e49ed4dc546fec49d1c2041b6343dee68a09beb95df54eacdfe2792307c88b20408692eef15bb3e370ac877e927cf58ace1931e2dde4ddbd228de70fc718a5bb30d68e0196bd2d00c1a3ef138adf1640403c513c07a0e8aefdca2bfd3a9404f4c9af683bf52980c43d48912ac5347c438100eb50613a082270e1bdb9a1a2241310fcf422611ff6b67cca4c9988489d344215b9b8f8b6247903edbd5689733427c555656a916d106fe6ebba66c25c1c659d27f86b712bfac6705301f03584da09b9da428092504dae0bda0b95cb7efbed8224349462ddd2a6966cffc455ddf78d3964d1b320dddd31e3e7edd4aa40afbe0d1d01ef0b1b2eab7a266201b3ed078199c33167da82795a3dca83aa06e921a4802fc34ff1e66bf31b2e047734ae2eaf765184498e127a296c5226af7a7a03ab5815efd8ee2660a4ae67a26b88d90665f5143b503998374a0175f2e2f6ac5bd25f21fc62ff01bf96ea49a15b1585441c91bde7e634ced455dec7d3a6ba115ff3b12bcfe04cbecf6af5afb4fc5b98392d2fc2deb203ad0c5f3b7ffc2fe116d7a4c40487f5d56421b031f17ef5592e9d2f052fbd8e62dc004b9caf7ced9204fc49cb2ba40ab94bffebc09851f785230ff2137e992a04aa7f63305c9d2e3b6f07f94a09b17fb79eb6cbd5f76f84ec738e80b75a229df1215931a14bc22f14a7d85a2f2f9ba9e7cb01be437e6117178c7640b766eed27820449bb3a477d3bc9f75acd433701d2a01d2dd0d7d29deaafce62cc16b83e0fa72229c045af4f16d9cf33e1e3e8a137e7da245a9906e0968fde8037a6c6a6de0ca425650c10ce8311fc1290f02ad70256bec8a70501866c727c1abd2ec9e38bee97010e2f6727888ec24d15235f58458f6436545aee826d06569824efc250d8064406cbeb735e6e2a5c0c04b2fb267952091813646018655d1bc38828d4ff512fa4c127245ee0436ad4f39272cf0024eacfeefc8adabb6947d7ca619a5fdf16e45cce1bab1b93881eba5827332e4f6449871e1281e94b61e8e4a0827314b502af91e4dc372034dc4a76beabd158014fe0909c4218308ec23258a09de7c2761bb90c07736faa900e44c1d46252fdeaa69dff7de1b235950537043d28b8f55faac068f7cc307716524d691d11d2f9243ed14b10686e7ad6507d91ba599f76e4b82b8b7b3aeb91355cae7595b913b280b6e2063544a350d855e06732d72bbeae44c4aeb3c2617b137ae71ec54f0073b0aea31ddc7f9cc230542410be56f25ff12bfad25a02753a051bdbb793fc1388ef1a294b525afb5d1f00909d9b267ae4ccc7e62cb6418edb56a9752619ada13cf24c7c383c68f33daf425bca04b9b991cafaa21932e248ede5f0ba666ddc92dfc894627ef08847726002572e51a952e48c875a9f3daf5529b80734622d2ff33c1361dbcf2db76b5a76408eaf5060428763de6150cb883997488cd2936b1f92ff9bbe5eea42c55f090e11affba3154c01261267b65ab13ceebd58ed5676ccd1340e922109ec3a5b7dc115511486c308db7de52b192f642f2e9f5344b66e57d0f380b2bd318c435a9c095db5133ed53c9dde09879743b172c3f3464a0526cc3083d04c8da3b467eb9197d696e3888e3cd59bdd01261b529eb7da5ac57143c130195f5b5d7c0cf75c2bdbfc9e98b56b449f8e9b4eec249a140a3bf22498bcba318c35a7394acd36f79d67942147df440b6c249ad6f4a650ad12479fc7dbd474b34415fe6ed5b1ae62116f2d0738e84cb74814365c2eaae75d75c1e97ed02e65012e3f78505e42017786cbbaa290db6104da5df3ce5eb84dbe2f4cf2a0a36e5391814e4d05b2fb4a876636367e9c44fc975e77e4f56e62edc1cecfe6bc75550c00f2a0d3629f3b2314cb8dbda80b03437fd88eff616a3a4a03000ba3936b2d33229b09823de81235d67577d92e61a9bc8f31f5656db69ff54c97c1e410d2281c40e8ea799a3a8fe19383398856c12fa95693d545567d3df118494c401c9d77236045bd62a651af5c4842f0b119996312a2b555cb4799bec2890ba4a4624ce3eaf38e988510a7f5bf9573611d0b9b88b8606cfd1b7e57355a28110f9f0a8b46a2f235f9470ba45f0063556084640f79d837c8071b34408ff7bb78d08ffcac535fa3de0685b5daa94a89ced1a1c6d32ab63bbbb1b50c1d6033e9e4c377d5eccd0533b53587f4728291d7bba94c9dc15c64df469f29bdfd5cc621fee43bb33b5969111de0aa7cd65dcf0c927edf37ca242bc64f810c9c7c6ea9652f876d05219ea660688d07d7e0e85625e47afd3652b4bf13d5e28dc94076b12f7640edfe303bd5a1dd07ddfcd29675632f62843b8268a3291f38c53fb115f5325f8e71b5f728319296d7181d8dd5df7524b1d0908edc699c48fa5868f5e7f6015f649b6d82998d43e061cf94fbfc3562c72dccfd085fd0842be56d141c081d4431d7efe66c3c9581e68f74c739bec99b003890c5f9e37497353d6937da0a355128c4ba0d657177518157e811454c0000a340b6ea94d644fc967a3b1934806e4ffb2b6c44fd68f0d0771854cd92db6a4cacb4a51cba72e98077b8de6867885151c9d5a6875764e230f2ca135d546f10057a2f243c0227c92ce23cb1c219cc8225ced38a987ec5c80148bb8fcd36c5fe12d1b097cca1c669739ed04641d14a5614852b71e600f39f100b48bc384befa6e9184eda4515f30e48f2d8e7da182994a2d8039a5f167743e3a83ae225772c31dc15ed64829b285dce99d29d8ffc28b77bae6a5db570b676327eaca9a081330b3d950b25cf9b0199dbd9d734407a7b6788eb1c7b190b326161f86133f7061573dab25df10bdb9ad0327bcd8e6376d60b011265bb48603bf2730e8737fd3675bb1fbacf0971ac3818eaef2e48d264d80b74033992b6dccaa3309b771cacbabc31f1b5df9c5ca982c8cdc813956b9c7afa7651e781f31161bb61e68141b0bc9bb46de37e468170cdeaea20c730b81eaef4a7d51506a6e8d40e6355aeb1e3b51d43cd39ff4eeba80cdb70a6a0d85dbadcd64a80898df76cedb8506902f2e63206225147df250d4226fd9cf1c6f5cea87167a81630d7234e4048216a7758da572a26702d5083b1e1906281bf92eedfc2f988ec2828dd7079430bd2f0111edcac208beea9c4bb01ad70123a7d335d191fabfd6c637e9899fbf50262addadf5a3cf4a138954738b456bb9c8605625b2aae94128991e91c0e62828ad204e522ba6a55ab1c3064d56514da5c46943a1d8421fac70d584daea333df201203be96e5b6effe1725e541deedbedfb1c99d73d857321b5c79bc7e5078d34763f5ecdf30bf8ce3509263ff8516cc11b0d4e7892442f6756e23f05a98759de5209589c86c716d28ec30376b1aa2603a35615d9f712aa112c19b4364cdd55947c12dd8704ed1e6d804a6f3947fedb1c22e6b5d9ec4182ce82248484f1c077d8fb18ea2633caeff87daa396089a27078c0670fcdaba1d61091e2565950799be18b329f57e4cf4c80010c48ce4d970bffb525ba672ef04cf1ccd1feaf4d91a7f4b60011945364a4888d78e0538abfc2c85c4079e0895718a5802098fbca9570219d11d695164cc61e58502be4f8f3a3a2f4b5765d870ff98bb237b86180fef9029d0093b7fe806893d1a25440698b239a1ff3db8f7c8a687739f3d180d81fbe75bb323585016d69348c8d20c9704547fedfbcb90f82bfd3337e280f81fa7cefc7fc2b71645de84bb5e75a52227b773ed01aa743182c1716e3b8fb62984300696c2c29a14cdeb8033ff2952105b54d50e4a2bfb590f59a794dc6e3cd293106f806e838451e7a45d953a214dc1d630563b895880aabfce34ce9261c87a6be925eb37316382a0f8c5440254be1aad69e7685730d42bbcd0576627c38848bdc92b16ef61366fb6632202f483f25032043f06f85009b4e13015865a387bfdd7c6aa4436b32ab04041848b968bb97e0d19a0c0e0c0899c1f463547e9250ba8d13109dc52e216b614f1193a6f44d58959e8b4acd4511d9d1c4adedb9aa9c3cdaaa5e65c6641b8194bca4a574ea98a907b79f0e10b1d729c9a1e42a87b8df7abf1d73cb835a3df2469b47696d9512576d9aef15ae943e48cb86c535bee7370589f9b288f1062f9c16fd7d5e9033b8a1efe20773a313c68ee4219f9d449fbd9e67f55d276f6a21c305edddfeefae11444635ef64f2a9e69e6de505fa9dc82822033265a1d86a1a301695d0ae9310d9e29b253f31af878631b6e750e150004b1aea96be32d6a4380e2952c0b67ccdbc2d363cac6cb66bde674cc0e413a195459503b78c9ab8a606da64a170c63479a96dd3051b1e97153be047747cb20fccaeac1512d28b8126136b95a4379f59382bef28ff51f6b04ff085ee6762b7772b81dcdb4efcb95b52175d52325c8e954c6864096e93b87ea0cbf575ac028eccc698a5fc6f4a07ceea0ade09f88abd662057f7fb3a520d2c473cccb2a7047fc28f797936c16d993f2011e9524fca9d46832acdcd6ef1a8027bbe285d916bd31617d59ed53c7e1289661106b25a937c68e755d23cfa9491056c2c3e12c503c4d2c72ed942e55f2359243c85569cc240b7d9a81eb5e7836431cb4b5c5e26fd627e24a9e7d1487c4e7f64232ca1f23048158092016ef5e7647e0ae4d313a6422be0e998446fbf79cb7d70b33962e0f6c2ceea814a8ce8d8ea72186fec4f5dcc63ef82a577f5a8e84579623f6993b5b6c76e4a5390b5557d7b8a085229827c08f2ee968dfa49997e59ed068d4118b0339605305b8a2b1ba3a54cd9eb915acf75eeb0748da73e2b20c81de83de1b74181574e8648315a12a39276361845bbeaad5604229d7f4e3a08af4a9eaf93fbb8db90b0015992587e6520cc603516374320ab70b61c9ac5c1e8e81c97b1fe15916f7791b7f657c27f5fa5c0db41245a9615a4b50d3802fbdad494b6eb22abc3bcf030622c7e4067dc6941b961ec685fb554acde232580c42608d4eb9c0dd0659567348696cb99e33efc5063455cc0820f886e4b3b2cce680c0f77dd77098eb87d7f35474f1e5f2dba3329dccba450c6f97f5d2298056afd4218aca1ee835621896838a791e2e04d17f0b4f7f3a3f30a0fb717e054bf11f775e20d7a5aac4841c31cb8d3570855504be775c4582963bb2538189e204a8bfdfa59955a2bac2705dd78a6f8c9f3259326febcf70a337166ee934856fc8234a2d61b044578b940179b590b40363159538658a9b39ce9d39d51aface156c9149f47912f50dfb5b8e241c2e60f78bfa45e2996a981bf51afe1989bb2ad6a8cd6ecab3424893eb760aa4ac1ad724b15d7859915bcd9d625624570e454b5e1eb69b441c729d42a2755bfc3fc8d7e7a5c38fc07b576ae9904eae0104652be03e99b8e98183b175596708f40aced3e814c940b590a26d34206808438a41d169a6a33b6b47f3d3c4a2c8ae643d8cf184833cd6c9a0de44070557be274e0cbe89991ee768df8a98bcf180159187fafc44d9a91e3fabbd81e2d66ea303b963059c215562aba48b8e76c215b06757bbe70d2452fc57d003f4a5081fc4c4849bccf0ac8cf9504f34fb2c383eebad5a6d105c16d83a902b1adbd347dff749bec214d209cfddc171b6ee5ea3b201c07c622d2b7e4ecaf989ff8ecf7b4682b83b0c84d0766547e77ea1d63cb8e45e86b6df0e912b4ac70c3e15784484d714f8873e91ef724ec0b5396e2700d35607cd73847cb966bd95a18ce5b3d32456a76cb1a96172034f8391f2b63c305132e745cbe5426c5c31a30e77108459507674ac0aa22c2e6df15f11285529ddf11e1865d4b3616fe0f04aa186eb8d9cfcce8440fac9fee9cf0fc1872f4a0ad28236f3e66d8123d80d08590d5ad6d31dd69ea10b762b3698ece119d190d53f004b65c947d46b3967db026d76b9460bd78f67077eb78e8828f2d6a2fe2452ccbe006f201977461bf38e3b136e04293b65b07bb1f08070bbd8cf284b76ee4b21faa01c880ee6376b9746f03c9ad034fefce1fd5df27ad9073299966771be915e68636" + let t = readZebraTransaction h it "Sapling component is read" $ do case t of Nothing -> assertFailure "Couldn't decode" From b74cd5f35467f84088b9b982736306d6ccfc5791 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 24 Apr 2024 16:04:56 -0500 Subject: [PATCH 129/149] Update note position calculation --- librustzcash-wrapper/src/lib.rs | 12 ++---------- test/Spec.hs | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index ab7c934..67a7447 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1247,16 +1247,8 @@ pub extern "C" fn rust_wrapper_read_sapling_position( let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); - let path = iw.path(); - match path { - Some(p) => { - let pos = p.position(); - return u64::from(pos); - }, - None => { - return 0; - } - } + let pos = iw.witnessed_position(); + return u64::from(pos); } #[no_mangle] diff --git a/test/Spec.hs b/test/Spec.hs index 5c6f3a5..19ca7a4 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1113,23 +1113,25 @@ main = do MainNet External p - dn `shouldNotBe` Nothing - + dn `shouldBe` Nothing describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" it "Try to generate valid ExchangeAddress from Transparent Address" $ do - case ta of - Nothing -> assertFailure "Failed to decode transparent address" - Just t -> do - case (tr_type (ta_receiver t) ) of - P2SH -> assertFailure "P2SH not supported for ExchengeAddress generation" + case ta of + Nothing -> assertFailure "Failed to decode transparent address" + Just t -> do + case (tr_type (ta_receiver t)) of + P2SH -> + assertFailure + "P2SH not supported for ExchengeAddress generation" P2PKH -> do - let exch = encodeExchangeAddress (ta_network t) (ta_receiver t) + let exch = + encodeExchangeAddress (ta_network t) (ta_receiver t) case exch of - Nothing -> assertFailure "Failed to encode Exchange address" - Just addr -> do + Nothing -> assertFailure "Failed to encode Exchange address" + Just addr -> do let eadr = decodeExchangeAddress addr - eadr `shouldNotBe` Nothing + eadr `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property From af9806d72df6c551b5e61eb19d7a605011d32b1d Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 25 Apr 2024 19:56:29 -0500 Subject: [PATCH 130/149] Add witness update functionality --- CHANGELOG.md | 15 ++++++-- librustzcash-wrapper/src/lib.rs | 68 +++++++++++++++++++++++++++++++++ src/C/Zcash.chs | 16 ++++++++ src/ZcashHaskell/Orchard.hs | 7 ++++ src/ZcashHaskell/Sapling.hs | 42 +++++++++++--------- zcash-haskell.cabal | 2 +- 6 files changed, 127 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70317db..7c13f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.4] + +### Added + +- Functions to update Sapling witnesses. +- Functions to update Orchard witnesses. + ## [0.5.5.3] ### Added -- Added unction to generate an ExchangeAddress in Human Readable Format Using a TransparentAddress in HRF - `encodeExchangeAddress` a function to create a ExchangeAddress in HRF - `decodeExchangeAddress` a function to obtaina a TransparentAddress object from an ExchangeAddress in HRF -- Added new type ExchangeAddress +- Added function to generate an `ExchangeAddress` in Human Readable Format Using a `TransparentAddress` in HRF +- `encodeExchangeAddress` a function to create a `ExchangeAddress` in HRF +- `decodeExchangeAddress` a function to obtain a `TransparentAddress` object from an `ExchangeAddress` in HRF +- Added new type `ExchangeAddress` ### Fixed diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 67a7447..985c3c4 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1251,6 +1251,40 @@ pub extern "C" fn rust_wrapper_read_sapling_position( return u64::from(pos); } +#[no_mangle] +pub extern "C" fn rust_wrapper_update_sapling_witness( + wit: *const u8, + wit_len: usize, + cmus: *const u8, + cmus_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let cmu: Vec> = marshall_from_haskell_var(cmus, cmus_len, RW); + for c in cmu { + let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(c)); + if sap_note_comm.is_some().into() { + let n = Node::from_cmu(&sap_note_comm.unwrap()); + iw.append(n); + } + } + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&iw, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_commitment_tree( tree: *const u8, @@ -1340,6 +1374,40 @@ pub extern "C" fn rust_wrapper_read_orchard_position( } } +#[no_mangle] +pub extern "C" fn rust_wrapper_update_orchard_witness( + wit: *const u8, + wit_len: usize, + cm: *const u8, + cm_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let cmu: Vec> = marshall_from_haskell_var(cm, cm_len, RW); + for c in cmu { + let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(c)); + if orchard_note_comm.is_some().into() { + let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); + iw.append(n); + } + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&iw, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_decode_sapling_address( sapling: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index a4bfb9d..0300865 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -259,3 +259,19 @@ import ZcashHaskell.Types } -> `Word64' #} + +{# fun unsafe rust_wrapper_update_sapling_witness as rustWrapperUpdateSaplingWitness + { toBorshVar* `BS.ByteString'& + , toBorshVar* `[BS.ByteString]'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_update_orchard_witness as rustWrapperUpdateOrchardWitness + { toBorshVar* `BS.ByteString'& + , toBorshVar* `[BS.ByteString]'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 7e6fc6c..d7c3e38 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -28,6 +28,7 @@ import C.Zcash , rustWrapperReadOrchardWitness , rustWrapperUADecode , rustWrapperUfvkDecode + , rustWrapperUpdateOrchardWitness ) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C @@ -219,3 +220,9 @@ getOrchardWitness tree = getOrchardNotePosition :: OrchardWitness -> Integer getOrchardNotePosition = fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit + +updateOrchardWitness :: OrchardWitness -> [HexString] -> OrchardWitness +updateOrchardWitness wit cmus = + OrchardWitness $ + withPureBorshVarBuffer $ + rustWrapperUpdateOrchardWitness (toBytes $ orchWit wit) (map toBytes cmus) diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 6e3c7b0..1b4e3bc 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -18,11 +18,11 @@ module ZcashHaskell.Sapling where import C.Zcash - ( rustWrapperIsShielded + ( rustWrapperDecodeSaplingAddress + , rustWrapperIsShielded , rustWrapperReadSaplingCommitmentTree , rustWrapperReadSaplingPosition , rustWrapperReadSaplingWitness - , rustWrapperDecodeSaplingAddress , rustWrapperSaplingCheck , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingDecodeEsk @@ -31,18 +31,19 @@ import C.Zcash , rustWrapperSaplingSpendingkey , rustWrapperSaplingVkDecode , rustWrapperTxParse + , rustWrapperUpdateSaplingWitness ) import Data.Aeson import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C -import qualified Data.Text as T import Data.HexString (HexString(..), fromText, hexString, toBytes, toText) +import qualified Data.Text as T import Data.Word import Foreign.Rust.Marshall.Variable ( withPureBorshVarBuffer , withPureBorshVarBuffer ) -import ZcashHaskell.Types +import ZcashHaskell.Types import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m) -- | Check if given bytesting is a valid encoded shielded address @@ -192,32 +193,37 @@ getSaplingNotePosition :: SaplingWitness -> Integer getSaplingNotePosition = fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit +updateSaplingWitness :: SaplingWitness -> [HexString] -> SaplingWitness +updateSaplingWitness wit cmus = + SaplingWitness $ + withPureBorshVarBuffer $ + rustWrapperUpdateSaplingWitness (toBytes $ sapWit wit) (map toBytes cmus) + -- | Encode a SaplingReceiver into HRF text encodeSaplingAddress :: ZcashNet -> SaplingReceiver -> Maybe T.Text -encodeSaplingAddress net sr = do +encodeSaplingAddress net sr = do case net of - MainNet -> - Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sr) + MainNet -> Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sr) TestNet -> Just $ encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sr) -- | Helper to get de Nework Id from FFI response -getNetId:: [Word8] -> ZcashNet -getNetId [x] = do - case x of +getNetId :: [Word8] -> ZcashNet +getNetId [x] = do + case x of 1 -> MainNet - 2 -> TestNet + 2 -> TestNet -- | decode a Sapling address decodeSaplingAddress :: BS.ByteString -> Maybe SaplingAddress -decodeSaplingAddress sapling_address = do +decodeSaplingAddress sapling_address = do if BS.length sa > 1 then do let sa0 = BS.unpack sa - Just $ SaplingAddress (getNetId (take 1 sa0)) - $ SaplingReceiver (BS.pack (drop 1 sa0)) + Just $ + SaplingAddress (getNetId (take 1 sa0)) $ + SaplingReceiver (BS.pack (drop 1 sa0)) else Nothing - where - sa = - withPureBorshVarBuffer $ - rustWrapperDecodeSaplingAddress sapling_address + where + sa = + withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index be2387f..e8bff8c 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.5.3 +version: 0.5.5.4 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 8901d97c64559047700ede55d44849360efb4bb7 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 29 Apr 2024 10:27:45 -0500 Subject: [PATCH 131/149] Upgrade of Zcash Rust crates --- CHANGELOG.md | 16 ++++ librustzcash-wrapper/Cargo.lock | 157 ++++++++++++++++++++++++++++---- librustzcash-wrapper/Cargo.toml | 7 +- librustzcash-wrapper/src/lib.rs | 142 +++++++++++++++-------------- src/C/Zcash.chs | 6 +- src/ZcashHaskell/Sapling.hs | 6 +- src/ZcashHaskell/Types.hs | 9 +- test/Spec.hs | 56 +++++++++++- zcash-haskell.cabal | 2 +- 9 files changed, 297 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c13f62..f1a6082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.0.0] + +### Added + +- Rust crates: + - `sapling-crypto` 0.1.3 + +### Changed + +- Modified handling of `ShieldedOutput`s based on new Rust crates +- Upgraded Rust crates: + - `orchard` to 0.7.1 + - `zcash_primitives` to 0.14.0 + - `zcash_client_backend` to 0.11.1 + + ## [0.5.5.4] ### Added diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 6a172d0..afcddf1 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -88,6 +88,27 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +[[package]] +name = "bellman" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afceed28bac7f9f5a508bca8aeeff51cdfa4770c0b967ac55c621e2ddfd6171" +dependencies = [ + "bitvec", + "blake2s_simd", + "byteorder", + "crossbeam-channel", + "ff", + "group", + "lazy_static", + "log", + "num_cpus", + "pairing", + "rand_core", + "rayon", + "subtle", +] + [[package]] name = "bincode" version = "1.3.3" @@ -468,6 +489,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.8.1" @@ -711,9 +741,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361c467824d4d9d4f284be4b2608800839419dccc4d4608f28345237fe354623" +checksum = "eb1872810fb725b06b8c153dde9e86f3ec26747b9b60096da7a869883b549cbe" dependencies = [ "either", ] @@ -807,6 +837,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "log" version = "0.4.19" @@ -935,9 +971,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d31e68534df32024dcc89a8390ec6d7bef65edd87d91b45cfb481a2eb2d77c5" +checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" dependencies = [ "aes", "bitvec", @@ -959,6 +995,8 @@ dependencies = [ "subtle", "tracing", "zcash_note_encryption", + "zcash_spec", + "zip32", ] [[package]] @@ -1218,6 +1256,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "redjubjub" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" +dependencies = [ + "rand_core", + "reddsa", + "serde", + "thiserror", + "zeroize", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1288,6 +1339,7 @@ dependencies = [ "nonempty", "orchard", "proc-macro2", + "sapling-crypto", "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", @@ -1295,6 +1347,38 @@ dependencies = [ "zip32", ] +[[package]] +name = "sapling-crypto" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f4270033afcb0c74c5c7d59c73cfd1040367f67f224fe7ed9a919ae618f1b7" +dependencies = [ + "aes", + "bellman", + "bitvec", + "blake2b_simd", + "blake2s_simd", + "bls12_381", + "byteorder", + "document-features", + "ff", + "fpe", + "group", + "hex", + "incrementalmerkletree", + "jubjub", + "lazy_static", + "memuse", + "rand", + "rand_core", + "redjubjub", + "subtle", + "tracing", + "zcash_note_encryption", + "zcash_spec", + "zip32", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1356,9 +1440,9 @@ dependencies = [ [[package]] name = "shardtree" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19f96dde3a8693874f7e7c53d95616569b4009379a903789efbd448f4ea9cc7" +checksum = "dbf20c7a2747d9083092e3a3eeb9a7ed75577ae364896bebbc5e0bdcd4e97735" dependencies = [ "bitflags 2.4.2", "either", @@ -1701,24 +1785,27 @@ dependencies = [ [[package]] name = "zcash_client_backend" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a382af39be9ee5a3788157145c404b7cd19acc440903f6c34b09fb44f0e991" +checksum = "001ec65dc2828ee648dc6d29f0944d7a877fe68ad06e001a203c11770ab1b3d4" dependencies = [ "base64", "bech32 0.9.1", "bls12_381", "bs58 0.5.0", "crossbeam-channel", + "document-features", "group", "hex", "incrementalmerkletree", "memuse", "nom", - "orchard", + "nonempty", "percent-encoding", "prost", + "rand_core", "rayon", + "sapling-crypto", "secrecy", "shardtree", "subtle", @@ -1728,8 +1815,10 @@ dependencies = [ "which", "zcash_address 0.3.1", "zcash_encoding", + "zcash_keys", "zcash_note_encryption", "zcash_primitives", + "zip32", ] [[package]] @@ -1742,6 +1831,29 @@ dependencies = [ "nonempty", ] +[[package]] +name = "zcash_keys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f22d3407fdd6992b49f037f23862ab376be6013be6f2d0bc85948a635edc1f5" +dependencies = [ + "bech32 0.9.1", + "bls12_381", + "bs58 0.5.0", + "document-features", + "group", + "memuse", + "nonempty", + "rand_core", + "sapling-crypto", + "subtle", + "tracing", + "zcash_address 0.3.1", + "zcash_encoding", + "zcash_primitives", + "zip32", +] + [[package]] name = "zcash_note_encryption" version = "0.4.0" @@ -1757,17 +1869,15 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d17e4c94ca8d69d2fcf2be97522da5732a580eb2125cda3b150761952f8df8e6" +checksum = "9070e084570bb78aed4f8d71fd6254492e62c87a5d01e084183980e98117092d" dependencies = [ "aes", "bip0039", - "bitvec", "blake2b_simd", - "blake2s_simd", - "bls12_381", "byteorder", + "document-features", "equihash", "ff", "fpe", @@ -1775,17 +1885,30 @@ dependencies = [ "hex", "incrementalmerkletree", "jubjub", - "lazy_static", "memuse", "nonempty", "orchard", "rand", "rand_core", + "redjubjub", + "sapling-crypto", "sha2 0.10.6", "subtle", + "tracing", "zcash_address 0.3.1", "zcash_encoding", "zcash_note_encryption", + "zcash_spec", + "zip32", +] + +[[package]] +name = "zcash_spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" +dependencies = [ + "blake2b_simd", ] [[package]] @@ -1810,9 +1933,9 @@ dependencies = [ [[package]] name = "zip32" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22" +checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e" dependencies = [ "blake2b_simd", "memuse", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 73ee84c..5ae9b85 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -11,10 +11,11 @@ f4jumble = "0.1" zcash_address = "0.2.0" borsh = "0.10" bech32 = "0.11" -orchard = "0.6.0" +orchard = "0.7.1" zcash_note_encryption = "0.4.0" -zcash_primitives = "0.13.0" -zcash_client_backend = "0.10.0" +zcash_primitives = "0.14.0" +zcash_client_backend = "0.11.1" +sapling-crypto = "0.1.3" zip32 = "0.1.0" proc-macro2 = "1.0.66" nonempty = "0.7.0" diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 985c3c4..4c43d75 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -6,8 +6,7 @@ use std::{ marker::PhantomData, io::{ Write, - Cursor, - Error + Cursor }, }; @@ -29,7 +28,40 @@ use incrementalmerkletree::{ witness::IncrementalWitness }; -use zip32; +use zip32::{ + Scope as SaplingScope, + ChildIndex +}; + + +use sapling_crypto::{ + NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, + Node, + MerklePath, + PaymentAddress, + value::ValueCommitment as SaplingValueCommitment, + note::ExtractedNoteCommitment as SaplingNoteCommitment, + keys::{ + PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, + ExpandedSpendingKey, + FullViewingKey as SaplingFullViewingKey + }, + note_encryption::{ + SaplingDomain, + Zip212Enforcement + }, + bundle::{ + GrothProofBytes, + OutputDescription, + SpendDescription, + Authorized as SaplingAuthorized, + Bundle as SaplingBundle + }, + zip32::{ + sapling_find_address, + DiversifierKey + } +}; use zcash_primitives::{ merkle_tree::{ @@ -38,12 +70,6 @@ use zcash_primitives::{ read_incremental_witness, write_incremental_witness }, - zip32::{ - Scope as SaplingScope, - ChildIndex, - sapling_find_address, - sapling::DiversifierKey - }, zip339::{Count, Mnemonic}, transaction::components::{ amount::Amount, @@ -53,28 +79,8 @@ use zcash_primitives::{ TxOut, OutPoint, Authorized - }, - sapling::{ - GrothProofBytes, - OutputDescription, - SpendDescription, - Authorized as SaplingAuthorized, - Bundle as SaplingBundle } }, - sapling::{ - Node, - MerklePath, - NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, - PaymentAddress, - note::ExtractedNoteCommitment as SaplingNoteCommitment, - keys::{ - PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, - ExpandedSpendingKey, - FullViewingKey as SaplingFullViewingKey - }, - note_encryption::SaplingDomain - }, transaction::Transaction, consensus::{ BranchId::Nu5, @@ -99,7 +105,6 @@ use zcash_client_backend::keys::sapling::{ }; use zcash_primitives::zip32::DiversifierIndex; -use zcash_primitives::block::BlockHeader; use orchard::{ Bundle as OrchardBundle, @@ -161,7 +166,7 @@ impl ToHaskell for HrawTx { #[derive(BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { - cv: Hhex, + pub cv: Hhex, cmu: Hhex, eph_key: Hhex, enc_txt: Hhex, @@ -194,6 +199,13 @@ impl HshieldedOutput { } return r } + pub fn to_output_description(&mut self) -> Result> { + let cv = SaplingValueCommitment::from_bytes_not_small_order(&to_array(self.cv.bytes.clone())).unwrap(); + let cmu = SaplingNoteCommitment::from_bytes(&to_array(self.cmu.bytes.clone())).unwrap(); + let eph_key = zcash_note_encryption::EphemeralKeyBytes::from(to_array(self.eph_key.bytes.clone())); + let x = OutputDescription::from_parts(cv, cmu, eph_key, to_array(self.enc_txt.bytes.clone()), to_array(self.out_txt.bytes.clone()), to_array(self.proof.bytes.clone())); + return Ok(x) + } } #[derive(BorshSerialize, BorshDeserialize)] @@ -400,10 +412,9 @@ impl ToHaskell for HSBundle { } impl HSBundle { - pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { - let mut s: Vec = Vec::new(); - sb.authorization().binding_sig.write(&mut s).unwrap(); - return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s } + pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { + let sig = <[u8; 64]>::from(sb.authorization().binding_sig); + return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: sig.to_vec()} } } @@ -428,11 +439,9 @@ impl Hspend { pub fn pack(sp: &[SpendDescription]) -> Vec { let mut r = Vec::new(); for s in sp { - let mut rk = Vec::new(); - let mut authsig = Vec::new(); - s.rk().write(&mut rk).unwrap(); - s.spend_auth_sig().write(&mut authsig).unwrap(); - r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig}}); + let rk = s.rk().clone(); + let sig = s.spend_auth_sig().clone(); + r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: <[u8; 32]>::from(rk).to_vec()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes: <[u8; 64]>::from(sig).to_vec()}}); } return r } @@ -654,10 +663,10 @@ pub extern "C" fn rust_wrapper_svk_check_address( let sa = PaymentAddress::from_bytes(&to_array(address_input)).unwrap(); match svk { Ok(k) => { - let (div_index, def_address) = k.default_address(); + let (_div_index, def_address) = k.default_address(); sa == def_address } - Err(e) => { + Err(_e) => { false } } @@ -740,11 +749,10 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( out_len: &mut usize ){ let sk: Vec = marshall_from_haskell_var(key, key_len, RW); - let note_input: Vec = marshall_from_haskell_var(note,note_len,RW); - let mut note_reader = Cursor::new(note_input); + let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW); let esk = ExtendedSpendingKey::from_bytes(&sk); - let main_domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000)); - let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000)); + let main_domain = SaplingDomain::new(Zip212Enforcement::On); + //let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000)); let scope = if external { SaplingScope::External } else { @@ -752,15 +760,14 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( }; match esk { Ok(k) => { - let action = OutputDescription::read(&mut note_reader); - match action { + let action = note_input.to_output_description(); + 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)}; + let result = zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2); match result { Some((n, r, m)) => { let nullifier = n.nf(&nk, pos); @@ -796,13 +803,12 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2( out_len: &mut usize ){ let evk: Vec = marshall_from_haskell_var(key, key_len, RW); - let note_input: Vec = marshall_from_haskell_var(note,note_len,RW); - let mut note_reader = Cursor::new(note_input); + let mut 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 action2 = OutputDescription::read(&mut note_reader); + let domain = SaplingDomain::new(Zip212Enforcement::On); + let action2 = note_input.to_output_description(); match action2 { Ok(action3) => { let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External); @@ -976,22 +982,14 @@ pub extern "C" fn rust_wrapper_tx_parse( out_len: &mut usize ){ let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW); - let tx_bytes: Vec = tx_input.clone(); let mut tx_reader = Cursor::new(tx_input); - let s_o = false; - let o_a = false; let parsed_tx = Transaction::read(&mut tx_reader, Nu5); match parsed_tx { Ok(t) => { let s_bundle = t.sapling_bundle(); match s_bundle { Some(b) => { - let mut s_output = Vec::new(); - for s_each_out in b.shielded_outputs().iter() { - let mut out_bytes = Vec::new(); - let _ = s_each_out.write_v4(&mut out_bytes); - s_output.push(out_bytes); - } + let s_output = HshieldedOutput::pack(b.shielded_outputs()); marshall_to_haskell_var(&s_output, out, out_len, RW); }, None => { @@ -1067,7 +1065,7 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress( let sp_key = ExtendedSpendingKey::from_bytes(&extspk); match sp_key { Ok(sp_key_x) => { - let (def_div, def_address) = sp_key_x.default_address(); + let (_def_div, def_address) = sp_key_x.default_address(); marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW); }, Err(_e) => { @@ -1102,14 +1100,14 @@ pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress( let extspku8 : &[u8] = &vexspkp; let extspk = match ExtendedSpendingKey::from_bytes(&extspku8) { Ok( k ) => k, - Err( e ) => { + Err( _e ) => { // error recovering ExtendedSpendingKey marshall_to_haskell_var(&vec![0], out, out_len, RW); return } }; let dfvk = extspk.to_diversifiable_full_viewing_key(); - let ( divIx, cPmtAddress ) = dfvk.change_address(); + let ( _divIx, cPmtAddress ) = dfvk.change_address(); marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW); } @@ -1123,7 +1121,7 @@ pub extern "C" fn rust_wrapper_derive_orchard_spending_key( out_len: &mut usize ){ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); - let sk = SpendingKey::from_zip32_seed(&s, coin_type, u32::from(zip32::AccountId::try_from(acc_id).unwrap())); + let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); match sk { Ok(key) => { marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW); @@ -1441,7 +1439,7 @@ pub extern "C" fn rust_wrapper_decode_sapling_address( let out_bytes: Vec = out_bytes_temp.to_vec(); marshall_to_haskell_var(&out_bytes, out, out_len, RW); } - Err(e) => { + Err(_e) => { let h = vec![0]; marshall_to_haskell_var(&h, out, out_len, RW); } @@ -1453,3 +1451,11 @@ pub extern "C" fn rust_wrapper_decode_sapling_address( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_create_transaction( + sapling: *const u8, + sapling_len: usize, + out: *mut u8, + out_len: &mut usize){ +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 0300865..6e0dff8 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -96,7 +96,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_note_decrypt_v2 as rustWrapperSaplingNoteDecode { toBorshVar* `BS.ByteString'& - , toBorshVar* `BS.ByteString'& + , toBorshVar* `ShieldedOutput'& , getVarBuffer `Buffer DecodedNote'& } -> `()' @@ -104,7 +104,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_esk_decrypt as rustWrapperSaplingDecodeEsk { toBorshVar* `BS.ByteString'& - , toBorshVar* `BS.ByteString'& + , toBorshVar* `ShieldedOutput'& , `Bool' , `Bool' , `Word64' @@ -138,7 +138,7 @@ import ZcashHaskell.Types #} {# fun unsafe rust_wrapper_tx_parse as rustWrapperTxParse { toBorshVar* `BS.ByteString'& - , getVarBuffer `Buffer [BS.ByteString]'& + , getVarBuffer `Buffer [ShieldedOutput]'& } -> `()' #} diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 1b4e3bc..5ba501e 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -50,7 +50,7 @@ import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m) isValidShieldedAddress :: BS.ByteString -> Bool isValidShieldedAddress = rustWrapperIsShielded -getShieldedOutputs :: HexString -> [BS.ByteString] +getShieldedOutputs :: HexString -> [ShieldedOutput] getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t serializeShieldedOutput :: ShieldedOutput -> BS.ByteString @@ -76,7 +76,7 @@ matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool matchSaplingAddress = rustWrapperSaplingCheck -- | Attempt to decode the given raw tx with the given Sapling viewing key -decodeSaplingOutput :: BS.ByteString -> BS.ByteString -> Maybe DecodedNote +decodeSaplingOutput :: BS.ByteString -> ShieldedOutput -> Maybe DecodedNote decodeSaplingOutput key out = case a_value decodedAction of 0 -> Nothing @@ -119,7 +119,7 @@ decodeSaplingOutputEsk key out znet scope pos = withPureBorshVarBuffer $ rustWrapperSaplingDecodeEsk (getBytes key) - (serializeShieldedOutput out) + out (scope == External) (znet == MainNet) (fromIntegral pos) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 25bb43f..91ccae3 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -259,7 +259,7 @@ data RawTxResponse = RawTxResponse { rt_id :: !HexString , rt_hex :: !HexString , rt_shieldedSpends :: ![ShieldedSpend] - , rt_shieldedOutputs :: ![BS.ByteString] + , rt_shieldedOutputs :: ![ShieldedOutput] , rt_orchardActions :: ![OrchardAction] , rt_blockheight :: !Integer , rt_confirmations :: !Integer @@ -483,10 +483,9 @@ newtype SaplingReceiver = instance ToBytes SaplingReceiver where getBytes (SaplingReceiver s) = s -data SaplingAddress = SaplingAddress - { - net_type :: !ZcashNet - , sa_receiver :: !SaplingReceiver +data SaplingAddress = SaplingAddress + { net_type :: !ZcashNet + , sa_receiver :: !SaplingReceiver } deriving (Eq, Prelude.Show, Read) -- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method diff --git a/test/Spec.hs b/test/Spec.hs index 19ca7a4..7b0aac0 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -25,6 +25,7 @@ import Control.Monad.IO.Class (liftIO) import Data.Aeson import Data.Bool (Bool(True)) import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as C import Data.Either (isRight) import Data.Foldable (sequenceA_) import Data.HexString @@ -60,6 +61,7 @@ import ZcashHaskell.Sapling , isValidShieldedAddress , matchSaplingAddress , updateSaplingCommitmentTree + , updateSaplingWitness ) import ZcashHaskell.Transparent import ZcashHaskell.Types @@ -68,6 +70,7 @@ import ZcashHaskell.Types , CoinType(..) , DecodedNote(..) , OrchardAction(..) + , OrchardBundle(..) , OrchardCommitmentTree(..) , OrchardSpendingKey(..) , OrchardWitness(..) @@ -84,6 +87,7 @@ import ZcashHaskell.Types , SaplingCommitmentTree(..) , SaplingReceiver(..) , SaplingSpendingKey(..) + , SaplingWitness(..) , Scope(..) , Seed(..) , ShieldedOutput(..) @@ -438,7 +442,8 @@ main = do let a = decodeSaplingOutput (bytes rawKey) (head x) it "amount should match" $ do maybe 0 a_value a `shouldBe` 10000 it "memo should match" $ do - maybe "" a_memo a `shouldBe` "Tx with Sapling and Orchard" + C.filter (/= '\NUL') (maybe "" a_memo a) `shouldBe` + "Tx with Sapling and Orchard" describe "fails with incorrect key" $ do let a = decodeSaplingOutput (bytes badKey) (head x) it "amount should not match" $ do maybe 0 a_value a `shouldNotBe` 10000 @@ -829,11 +834,11 @@ main = do Nothing -> assertFailure "Couldn't decode" Just t1' -> do let tb1 = zt_tBundle t1' - print tb1 let txInHex = HexString "Km\237=.\228>T\DC3\\\EOT\249\163\ENQ\180s\215\215A\187\230\243\131\170cn\ETX\233Hp^\r" - toText txInHex `shouldBe` "Blank" + toText txInHex `shouldBe` + "4b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d" it "Sapling component is read" $ do case t of Nothing -> assertFailure "Couldn't decode" @@ -868,6 +873,14 @@ main = do Just t' -> do let ob = zt_oBundle t' fromRawOBundle ob `shouldNotBe` Nothing + it "Orchard CMX is present" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let ob = zt_oBundle t' + case fromRawOBundle ob of + Nothing -> assertFailure "Couldn't open the orchard bundle" + Just s -> toBytes (cmx (head (obActions s))) `shouldNotBe` "" describe "Raw transaction with Transparent inputs" $ do let h = hexString @@ -1113,7 +1126,7 @@ main = do MainNet External p - dn `shouldBe` Nothing + dn `shouldNotBe` Nothing describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" it "Try to generate valid ExchangeAddress from Transparent Address" $ do @@ -1132,6 +1145,41 @@ main = do Just addr -> do let eadr = decodeExchangeAddress addr eadr `shouldNotBe` Nothing + describe "Witness updates" $ do + it "Sapling" $ do + let wit = + SaplingWitness $ + hexString + "01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e57ec972a9b9383dc9cb228980d2d7752bb2abebc4a604ca48c5457039d2e05b000301392bed8592185dde5ab7fc81aed75e98fcf041f1a3fda55ad0b0b139ba9380130001808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15" + let cmus = + [ hexString + "958ccdc752f2f593f6c1c8e2d7201348cd896e54c6d3c92200bdbe8b859eac44" + , hexString + "e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447" + , hexString + "0c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e18" + ] + updateSaplingWitness wit cmus `shouldBe` + SaplingWitness + (hexString + "01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447010c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e1803000121c06ee1f1584f79d50785797a694c742be2ded600367ab7d54f3ed49e3adf7201808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15") + it "Orchard" $ do + let wit = + OrchardWitness $ + hexString + "016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e000200019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301" + let cmxs = + [ hexString + "712ba86615ff4447e8d7c7b59f3873f03c03a173438b8e4c8d416756ed4fae10" + , hexString + "c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e" + , hexString + "ac20b8170b008888c19fc6e16f5e30a5ef1653e5219d0cd0c9353c3aa8f79823" + ] + updateOrchardWitness wit cmxs `shouldBe` + OrchardWitness + (hexString + "016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e0002010cfb50d8c877eb39e9c07082a032dd99d34be7c19fa7f30e9fecf5f14736240f019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301") -- | Properties prop_PhraseLength :: Property diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index e8bff8c..e739a59 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.5.4 +version: 0.6.0.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From ad9e2f074d76d5aa84a8c7a63a715b2537630020 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 30 Apr 2024 16:54:15 -0500 Subject: [PATCH 132/149] Implement transaction creation --- CHANGELOG.md | 21 ++ librustzcash-wrapper/Cargo.lock | 168 +++++++++++++ librustzcash-wrapper/Cargo.toml | 5 +- librustzcash-wrapper/src/lib.rs | 414 +++++++++++++++++++++++++++++--- src/C/Zcash.chs | 16 ++ src/ZcashHaskell/Transparent.hs | 65 ++--- src/ZcashHaskell/Types.hs | 49 ++++ zcash-haskell.cabal | 2 +- 8 files changed, 682 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a6082..6f9e53b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.1.0] + +### Added + +- Function to create a raw transaction +- New types for transaction creation: + - `Rseed` + - `TransparentTxSpend` + - `SaplingTxSpend` + - `OrchardTxSpend` + - `OutgoingNote` +- Rust crates: + - `secp256k1` + - `jubjub` + - `rand_core` + + +### Changed + +- `DecodedNote` type now includes a field for `rho` and one for `rseed` + ## [0.6.0.0] ### Added diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index afcddf1..53583a4 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -318,6 +318,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bumpalo" +version = "3.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" + [[package]] name = "byteorder" version = "1.4.3" @@ -703,6 +709,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "hdwallet" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7" +dependencies = [ + "lazy_static", + "rand_core", + "ring", + "secp256k1", + "thiserror", +] + [[package]] name = "heck" version = "0.4.1" @@ -796,6 +815,15 @@ dependencies = [ "either", ] +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "jubjub" version = "0.10.0" @@ -1313,6 +1341,30 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rustix" version = "0.37.20" @@ -1336,10 +1388,13 @@ dependencies = [ "f4jumble", "haskell-ffi", "incrementalmerkletree", + "jubjub", "nonempty", "orchard", "proc-macro2", + "rand_core", "sapling-crypto", + "secp256k1", "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", @@ -1385,6 +1440,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "secp256k1" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -1661,6 +1734,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "version_check" version = "0.9.4" @@ -1673,6 +1752,70 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.0" @@ -1684,6 +1827,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" @@ -1882,6 +2047,7 @@ dependencies = [ "ff", "fpe", "group", + "hdwallet", "hex", "incrementalmerkletree", "jubjub", @@ -1891,7 +2057,9 @@ dependencies = [ "rand", "rand_core", "redjubjub", + "ripemd", "sapling-crypto", + "secp256k1", "sha2 0.10.6", "subtle", "tracing", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 5ae9b85..b8b2b37 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -13,13 +13,16 @@ borsh = "0.10" bech32 = "0.11" orchard = "0.7.1" zcash_note_encryption = "0.4.0" -zcash_primitives = "0.14.0" +zcash_primitives = { version = "0.14.0", features = ["transparent-inputs"]} zcash_client_backend = "0.11.1" sapling-crypto = "0.1.3" zip32 = "0.1.0" proc-macro2 = "1.0.66" nonempty = "0.7.0" incrementalmerkletree = "0.5.0" +secp256k1 = "0.26.0" +jubjub = "0.10.0" +rand_core = { version = "0.6.4", features = ["getrandom"]} [features] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 4c43d75..6d98974 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -12,6 +12,8 @@ use std::{ use nonempty::NonEmpty; +use rand_core::OsRng; + use f4jumble; use borsh::{BorshDeserialize, BorshSerialize}; @@ -23,6 +25,10 @@ use haskell_ffi::{ FromHaskell, HaskellSize, ToHaskell }; +use secp256k1::SecretKey; + +use jubjub::Fr; + use incrementalmerkletree::{ frontier::CommitmentTree, witness::IncrementalWitness @@ -39,8 +45,13 @@ use sapling_crypto::{ Node, MerklePath, PaymentAddress, + Anchor as SaplingAnchor, value::ValueCommitment as SaplingValueCommitment, - note::ExtractedNoteCommitment as SaplingNoteCommitment, + note::{ + ExtractedNoteCommitment as SaplingNoteCommitment, + Note as SaplingNote, + Rseed + }, keys::{ PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, ExpandedSpendingKey, @@ -57,6 +68,11 @@ use sapling_crypto::{ Authorized as SaplingAuthorized, Bundle as SaplingBundle }, + value::NoteValue as SaplingNoteValue, + circuit::{ + SpendParameters, + OutputParameters + }, zip32::{ sapling_find_address, DiversifierKey @@ -70,18 +86,34 @@ use zcash_primitives::{ read_incremental_witness, write_incremental_witness }, + legacy::{ + Script, + TransparentAddress + }, zip339::{Count, Mnemonic}, - transaction::components::{ - amount::Amount, - transparent::{ - Bundle as TransparentBundle, - TxIn, - TxOut, - OutPoint, - Authorized + transaction::{ + Transaction, + fees::zip317::FeeRule, + builder::{ + Builder, + Error, + BuildConfig + }, + components::{ + amount::{ + Amount, + NonNegativeAmount + }, + transparent::{ + Bundle as TransparentBundle, + TxIn, + TxOut, + OutPoint, + Authorized + } } }, - transaction::Transaction, + memo::MemoBytes, consensus::{ BranchId::Nu5, MainNetwork, @@ -107,6 +139,7 @@ use zcash_client_backend::keys::sapling::{ use zcash_primitives::zip32::DiversifierIndex; use orchard::{ + Address as OrchardAddress, Bundle as OrchardBundle, bundle::{ Authorized as OrchardAuthorized, @@ -114,11 +147,18 @@ use orchard::{ }, Action, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, - note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, + note::{RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, - tree::MerkleHashOrchard, - value::ValueCommitment + tree::{ + MerklePath as OrchardMerklePath, + MerkleHashOrchard, + Anchor as OrchardAnchor + }, + value::{ + ValueCommitment, + NoteValue + } }; use bech32::{ @@ -255,7 +295,9 @@ pub struct Hnote { note: u64, recipient: Vec, memo: Vec, - nullifier: Vec + nullifier: Vec, + rho: Vec, + rseed: Hrseed } impl ToHaskell for Hnote { @@ -265,6 +307,27 @@ impl ToHaskell for Hnote { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Hrseed { + kind: u8, + bytes: Vec +} + + +impl FromHaskell for Hrseed { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = Hrseed::deserialize(buf)?; + Ok(x) + } +} + +impl ToHaskell for Hrseed { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hua { net: u8, @@ -363,6 +426,13 @@ pub struct HTxOut { script: Vec } +impl FromHaskell for HTxOut { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HTxOut::deserialize(buf)?; + Ok(x) + } +} + impl ToHaskell for HTxOut { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; @@ -374,6 +444,9 @@ impl HTxOut { pub fn pack(t: &TxOut) -> HTxOut { return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() } } + pub fn unpack(&self) -> TxOut { + TxOut { value: NonNegativeAmount::from_nonnegative_i64(self.amt).unwrap(), script_pubkey: Script(self.script.clone())} + } } #[derive(BorshSerialize, BorshDeserialize)] @@ -382,6 +455,13 @@ pub struct Houtpoint { index: u32 } +impl FromHaskell for Houtpoint { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = Houtpoint::deserialize(buf)?; + Ok(x) + } +} + impl ToHaskell for Houtpoint { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; @@ -393,6 +473,31 @@ impl Houtpoint { pub fn pack(o: &OutPoint) -> Houtpoint { return Houtpoint {hash: o.hash().to_vec() , index: o.n() } } + + pub fn unpack(&self) -> OutPoint { + OutPoint::new(to_array(self.hash.clone()), self.index) + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HtransparentInput { + sk: Vec, + utxo: Houtpoint, + coin: HTxOut +} + +impl FromHaskell for HtransparentInput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HtransparentInput::deserialize(buf)?; + Ok(x) + } +} + +impl ToHaskell for HtransparentInput { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } } #[derive(BorshSerialize, BorshDeserialize)] @@ -447,6 +552,52 @@ impl Hspend { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HsaplingInput { + sk: Vec, + note: Hnote, + iw: Vec +} + +impl FromHaskell for HsaplingInput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HsaplingInput::deserialize(buf)?; + Ok(x) + } +} + + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HorchardInput { + sk: Vec, + note: Hnote, + iw: Vec +} + +impl FromHaskell for HorchardInput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HorchardInput::deserialize(buf)?; + Ok(x) + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Houtput { + kind: u8, + ovk: Vec, + to: Vec, + amt: u64, + memo: Vec, + chg: bool +} + +impl FromHaskell for Houtput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = Houtput::deserialize(buf)?; + Ok(x) + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct HOBundle { empty: bool, @@ -771,23 +922,31 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( match result { Some((n, r, m)) => { let nullifier = n.nf(&nk, pos); - let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec() }; + let rseed = match n.rseed() { + Rseed::BeforeZip212(x) => { + Hrseed {kind: 1, bytes: x.to_bytes().to_vec()} + }, + Rseed::AfterZip212(y) => { + Hrseed {kind: 2, bytes: y.to_vec()} + } + }; + let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec(), rho: vec![0], rseed }; marshall_to_haskell_var(&hn, out, out_len, RW); }, None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -816,23 +975,31 @@ 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(), nullifier: vec![0]}; + let rseed = match n.rseed() { + Rseed::BeforeZip212(x) => { + Hrseed { kind: 1, bytes: x.to_bytes().to_vec()} + }, + Rseed::AfterZip212(y) => { + Hrseed { kind: 2, bytes: y.to_vec()} + } + }; + let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0], rho: vec![0], rseed}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } } Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -866,17 +1033,19 @@ 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(), nullifier: vec![0]}; + let rho = n.rho().to_bytes().to_vec(); + let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()}; + let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0], rho, rseed}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -914,11 +1083,13 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk( 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(), nullifier: n.nullifier(&fvk).to_bytes().to_vec()}; + let rho = n.rho().to_bytes().to_vec(); + let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()}; + let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec(), rho, rseed}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -1454,8 +1625,193 @@ pub extern "C" fn rust_wrapper_decode_sapling_address( #[no_mangle] pub extern "C" fn rust_wrapper_create_transaction( - sapling: *const u8, - sapling_len: usize, + sap_wit: *const u8, + sap_wit_len: usize, + orch_wit: *const u8, + orch_wit_len: usize, + t_input: *const u8, + t_input_len: usize, + s_input: *const u8, + s_input_len: usize, + o_input: *const u8, + o_input_len: usize, + out_list: *const u8, + out_list_len: usize, + sapspend: *const u8, + sapspend_len: usize, + sapoutput: *const u8, + sapoutput_len: usize, + net: bool, + bl_height: u32, out: *mut u8, out_len: &mut usize){ + let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); + let sap_wit_reader = Cursor::new(sap_wit_in); + let sap_iw: Option> = read_incremental_witness(sap_wit_reader).ok(); + let sap_anchor = match sap_iw { + Some(s_iw) => { + Some(SaplingAnchor::from(s_iw.root())) + }, + None => { + None + } + }; + let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); + let orch_wit_reader = Cursor::new(orch_wit_in); + let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); + let orch_anchor = match orch_iw { + Some(o_iw) => { + Some(OrchardAnchor::from(o_iw.root())) + }, + None => { + None + } + }; + let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; + let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); + let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); + let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); + for t_in in trans_input { + if t_in.sk.len() > 1 { + let k = SecretKey::from_slice(&t_in.sk).unwrap(); + if net { + match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { + Ok(()) => { + continue; + }, + Err(_e) => { println!("Error reading transparent input"); } + } + } else { + match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { + Ok(()) => { + continue; + }, + Err(_e) => { println!("Error reading transparent input"); } + } + } + } + } + let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); + for s_in in sap_input { + if s_in.sk.len() > 1 { + let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); + match sp_key { + Ok(sk) => { + let pay_addr = PaymentAddress::from_bytes(&to_array(s_in.note.recipient)).unwrap(); + let rseed = if s_in.note.rseed.kind == 1 { + Rseed::BeforeZip212(Fr::from_bytes(&to_array(s_in.note.rseed.bytes)).unwrap()) + } else { + Rseed::AfterZip212(to_array(s_in.note.rseed.bytes)) + }; + let note = SaplingNote::from_parts(pay_addr, SaplingNoteValue::from_raw(s_in.note.note), rseed); + let wit_reader = Cursor::new(s_in.iw); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let merkle_path = iw.path().unwrap(); + if net { + let _mb = main_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap(); + } else { + let _tb = test_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap(); + } + }, + Err(_e) => { + continue; + } + } + } + } + let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); + for o_in in orch_input { + if o_in.sk.len() > 1 { + let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); + let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap(); + let rho = Nullifier::from_bytes(&to_array(o_in.note.rho)).unwrap(); + let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap(); + let val = NoteValue::from_raw(o_in.note.note); + let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap(); + let wit_reader = Cursor::new(o_in.iw); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let merkle_path = OrchardMerklePath::from(iw.path().unwrap()); + if net { + let _mb = main_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap(); + } else { + let _tb = test_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap(); + } + } + } + let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); + for output in outputs { + match output.kind { + 1 => { + let recipient = TransparentAddress::PublicKeyHash(to_array(output.to)); + let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + if net { + let _mb = main_builder.add_transparent_output(&recipient, val); + } else { + let _tb = test_builder.add_transparent_output(&recipient, val); + } + }, + 2 => { + let recipient = TransparentAddress::ScriptHash(to_array(output.to)); + let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + if net { + let _mb = main_builder.add_transparent_output(&recipient, val); + } else { + let _tb = test_builder.add_transparent_output(&recipient, val); + } + }, + 3 => { + let ovk = Some(ExpandedSpendingKey::from_spending_key(&output.ovk).ovk); + let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap(); + let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + let memo = MemoBytes::from_bytes(&output.memo).unwrap(); + if net { + let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); + } else { + let _tb = test_builder.add_sapling_output::(ovk, recipient, val, memo); + } + }, + 4 => { + let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); + let ovk = if output.chg { + Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) + }else { + Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) + }; + let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); + let val = output.amt; + let memo = MemoBytes::from_bytes(&output.memo).unwrap(); + if net { + let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); + } else { + let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); + } + }, + _ => { + continue; + } + } + } + let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); + let spend_params_reader = Cursor::new(spend_params_in); + let spend_prover = SpendParameters::read(spend_params_reader, true).unwrap(); + let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); + let output_params_reader = Cursor::new(output_params_in); + let output_prover = OutputParameters::read(output_params_reader, true).unwrap(); + let result = if net { + main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + } else { + test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + }; + match result { + Ok(r) => { + let mut out_bytes: Vec = Vec::new(); + let _t = r.transaction().write_v5(&mut out_bytes); + 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); + } + } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 6e0dff8..8f27fa2 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -275,3 +275,19 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_create_transaction as rustWrapperCreateTx + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , toBorshVar* `[TransparentTxSpend]'& + , toBorshVar* `[SaplingTxSpend]'& + , toBorshVar* `[OrchardTxSpend]'& + , toBorshVar* `[OutgoingNote]'& + , toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , `Bool' + , `Word64' + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 4bdc46d..afe09e3 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -21,9 +21,9 @@ import Crypto.Secp256k1 import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) -import Data.Char (chr) import qualified Data.ByteString.Char8 as BC -import Data.HexString +import Data.Char (chr) +import Data.HexString import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word @@ -31,6 +31,7 @@ import Haskoin.Address (Address(..)) import qualified Haskoin.Crypto.Hash as H import Haskoin.Crypto.Keys.Extended import ZcashHaskell.Types + -- ( AccountId -- , CoinType(..) -- , Scope(..) @@ -44,7 +45,7 @@ import ZcashHaskell.Types -- , getTransparentPrefix -- , getValue -- ) -import ZcashHaskell.Utils( encodeBech32m, decodeBech32 ) +import ZcashHaskell.Utils (decodeBech32, encodeBech32m) -- | Required for `TransparentReceiver` encoding and decoding sha256 :: BS.ByteString -> BS.ByteString @@ -127,60 +128,70 @@ decodeTransparentAddress taddress = do 189 -> Just $ TransparentAddress MainNet $ - TransparentReceiver P2SH (fromRawBytes transparentReceiver) + TransparentReceiver + P2SH + (fromRawBytes transparentReceiver) 186 -> Just $ TransparentAddress TestNet $ - TransparentReceiver P2SH (fromRawBytes transparentReceiver) + TransparentReceiver + P2SH + (fromRawBytes transparentReceiver) 184 -> Just $ TransparentAddress MainNet $ - TransparentReceiver P2PKH (fromRawBytes transparentReceiver) + TransparentReceiver + P2PKH + (fromRawBytes transparentReceiver) _ -> Nothing 29 -> if sb == 37 then Just $ TransparentAddress TestNet $ - TransparentReceiver P2PKH (fromRawBytes transparentReceiver) + TransparentReceiver + P2PKH + (fromRawBytes transparentReceiver) else Nothing _ -> Nothing -- | Encode an Exchange Addresss into HRF from TransparentReceiver -encodeExchangeAddress:: ZcashNet -> TransparentReceiver -> Maybe T.Text -encodeExchangeAddress net tr = do +encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text +encodeExchangeAddress net tr = do case (tr_type tr) of - P2PKH -> do + P2PKH -> do case net of MainNet -> do let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr)) Just vhash - TestNet -> do + TestNet -> do let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr)) Just vhash - _ -> Nothing + _ -> Nothing -- | Decode an Exchange Address into a ExchangeAddress -decodeExchangeAddress:: T.Text-> Maybe ExchangeAddress -decodeExchangeAddress ex = do - if (T.length ex ) > 1 - then do - let rawd = decodeBech32 (E.encodeUtf8 ex) - let tMain = BS.unpack (BC.pack "tex") - let tTest = BS.unpack (BC.pack "textest") - let tFail = BS.unpack (BC.pack "fail") - let hr = BS.unpack (hrp rawd) +decodeExchangeAddress :: T.Text -> Maybe ExchangeAddress +decodeExchangeAddress ex = do + if (T.length ex) > 1 + then do + let rawd = decodeBech32 (E.encodeUtf8 ex) + let tMain = BS.unpack (BC.pack "tex") + let tTest = BS.unpack (BC.pack "textest") + let tFail = BS.unpack (BC.pack "fail") + let hr = BS.unpack (hrp rawd) if hr /= tFail then do let transparentReceiver = bytes rawd - if hr == tMain + if hr == tMain then Just $ ExchangeAddress MainNet $ TransparentReceiver P2PKH (fromRawBytes transparentReceiver) - else do - if hr == tTest + else do + if hr == tTest then Just $ ExchangeAddress TestNet $ - TransparentReceiver P2PKH (fromRawBytes transparentReceiver) - else Nothing + TransparentReceiver + P2PKH + (fromRawBytes transparentReceiver) + else Nothing else Nothing - else Nothing \ No newline at end of file + else Nothing diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 91ccae3..91f01b5 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -638,11 +638,60 @@ data DecodedNote = DecodedNote , a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver. , a_memo :: !BS.ByteString -- ^ The decoded shielded memo field. , a_nullifier :: !HexString -- ^ The calculated nullifier + , a_rho :: !BS.ByteString + , a_rseed :: !Rseed } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote +data Rseed = Rseed + { rs_kind :: !Word8 + , rs_bytes :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Rseed + +data TransparentTxSpend = TransparentTxSpend + { ts_sk :: !BS.ByteString + , ts_utxo :: !RawOutPoint + , ts_coin :: !RawTxOut + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct TransparentTxSpend + +data SaplingTxSpend = SaplingTxSpend + { ss_sk :: !BS.ByteString + , ss_note :: !DecodedNote + , ss_iw :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingTxSpend + +data OrchardTxSpend = OrchardTxSpend + { ss_sk :: !BS.ByteString + , ss_note :: !DecodedNote + , ss_iw :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardTxSpend + +data OutgoingNote = OutGoingNote + { on_kind :: !Word8 + , on_key :: !BS.ByteString + , on_recipient :: !BS.ByteString + , on_amt :: !Word64 + , on_memo :: !BS.ByteString + , on_chg :: !Bool + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OutgoingNote + -- * Classes -- | Class to represent types with a bytestring representation class ToBytes a where diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index e739a59..4730957 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.6.0.0 +version: 0.6.1.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 26a79443a78dce9a23cdd176311974c4757f5080 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 30 Apr 2024 19:14:30 -0500 Subject: [PATCH 133/149] Add Read instance to Rseed --- CHANGELOG.md | 6 ++++++ src/ZcashHaskell/Types.hs | 2 +- zcash-haskell.cabal | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9e53b..bee207c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.1.1] + +### Changed + +- Add `Read` instance for `Rseed` + ## [0.6.1.0] ### Added diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 91f01b5..259c080 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -648,7 +648,7 @@ data DecodedNote = DecodedNote data Rseed = Rseed { rs_kind :: !Word8 , rs_bytes :: !BS.ByteString - } deriving stock (Eq, Prelude.Show, GHC.Generic) + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Rseed diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 4730957..361d33e 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.6.1.0 +version: 0.6.1.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain From 787cf40629575abede6da5998fd69d8a9ba45f02 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 09:12:58 -0500 Subject: [PATCH 134/149] 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) From bc074d0386a1560cd923f6399a362739a2c9ca49 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 14:24:18 -0500 Subject: [PATCH 135/149] Add transparent key derivation --- CHANGELOG.md | 1 + src/ZcashHaskell/Transparent.hs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e315c8..cf7b6d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Type for transaction creation errors - Types for Sapling circuit parameters - Function to create transaction +- Function to derive distinct transparent spending keys ### Changed diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index afe09e3..878a7d8 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -101,7 +101,22 @@ genTransparentReceiver i scope xprvk = do ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j _anyOtherKind -> throwIO $ userError "Unsupported transparent address type" --- } decode a Transparent Address in HRF and return a TransparentAddress object +-- | Generate a transparent receiver +genTransparentSecretKey :: + Int -- ^ The index of the address to be created + -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses + -> XPrvKey -- ^ The transparent private key + -> IO TransparentSpendingKey +genTransparentSecretKey i scope xprvk = do + ioCtx <- createContext + let s = + case scope of + External -> 0 + Internal -> 1 + let path = Deriv :/ s :/ fromIntegral i :: DerivPath + return $ derivePath ioCtx path xprvk + +-- | decode a Transparent Address in HRF and return a TransparentAddress object decodeTransparentAddress :: BS.ByteString -> Maybe TransparentAddress decodeTransparentAddress taddress = do if BS.length taddress < 34 From bea4d2f07143f3a01562c595bb0eff3ad6fd8511 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 16:14:41 -0500 Subject: [PATCH 136/149] Fix typo in constructor --- src/ZcashHaskell/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index ed8cbb9..af60f0f 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -680,7 +680,7 @@ data OrchardTxSpend = OrchardTxSpend deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardTxSpend -data OutgoingNote = OutGoingNote +data OutgoingNote = OutgoingNote { on_kind :: !Word8 , on_key :: !BS.ByteString , on_recipient :: !BS.ByteString From 5fd33e7e43f8ef78c82d82655936ca24528903c1 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 07:22:12 -0500 Subject: [PATCH 137/149] Add debuggin for create transaction --- librustzcash-wrapper/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index c4f3316..f9a144d 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1656,6 +1656,7 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; + println!(sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); @@ -1667,16 +1668,19 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; + println!(orch_anchor); let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); + println!(trans_input); for t_in in trans_input { if t_in.sk.len() > 1 { let k = SecretKey::from_slice(&t_in.sk).unwrap(); if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { + println!("added t-input in main"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1684,6 +1688,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } else { match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { + println!("added t-input in test"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1692,6 +1697,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); + println!(sap_input); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1720,6 +1726,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); + println!(orch_input); for o_in in orch_input { if o_in.sk.len() > 1 { let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); @@ -1739,6 +1746,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); + println!(outputs); for output in outputs { match output.kind { 1 => { @@ -1782,8 +1790,10 @@ pub extern "C" fn rust_wrapper_create_transaction( let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); + println!("added o-input to main"); } else { let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); + println!("added o-input to test"); } }, _ => { @@ -1797,9 +1807,12 @@ pub extern "C" fn rust_wrapper_create_transaction( let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); let output_prover = OutputParameters::read(output_params_reader, true).unwrap(); + println!("loaded params"); let result = if net { + println!("started main build"); main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) } else { + println!("started test build"); test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) }; match result { From c144a6d62423edae7cc807c4d54cc0555cdbc2eb Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 07:31:01 -0500 Subject: [PATCH 138/149] Add more debugging to Rust --- librustzcash-wrapper/src/lib.rs | 60 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index f9a144d..821019f 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -170,7 +170,7 @@ use bech32::{ pub enum RW {} pub const RW: PhantomData = PhantomData; -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct RawData { hrp: Vec, bytes: Vec @@ -190,7 +190,7 @@ impl ToHaskell for RawData { //} //} -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HrawTx { bytes: Vec, s: bool, @@ -204,7 +204,7 @@ impl ToHaskell for HrawTx { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { pub cv: Hhex, cmu: Hhex, @@ -248,7 +248,7 @@ impl HshieldedOutput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hhex { bytes: Vec } @@ -261,7 +261,7 @@ impl ToHaskell for Hhex { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Haction { nf: Hhex, rk: Hhex, @@ -290,7 +290,7 @@ impl Haction { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hnote { note: u64, recipient: Vec, @@ -307,7 +307,7 @@ impl ToHaskell for Hnote { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hrseed { kind: u8, bytes: Vec @@ -328,7 +328,7 @@ impl ToHaskell for Hrseed { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hua { net: u8, o_rec: Vec, @@ -361,7 +361,7 @@ impl Hua { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Htx { txid: Vec, locktime: u32, @@ -379,7 +379,7 @@ impl ToHaskell for Htx { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTBundle { empty: bool, vin: Vec, @@ -400,7 +400,7 @@ impl HTBundle { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTxIn { outpoint: Houtpoint, script: Vec, @@ -420,7 +420,7 @@ impl HTxIn { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTxOut { amt: i64, script: Vec @@ -449,7 +449,7 @@ impl HTxOut { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Houtpoint { hash: Vec, index: u32 @@ -479,7 +479,7 @@ impl Houtpoint { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HtransparentInput { sk: Vec, utxo: Houtpoint, @@ -500,7 +500,7 @@ impl ToHaskell for HtransparentInput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HSBundle { empty: bool, spends: Vec, @@ -523,7 +523,7 @@ impl HSBundle { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hspend { cv: Hhex, anchor: Hhex, @@ -552,7 +552,7 @@ impl Hspend { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HsaplingInput { sk: Vec, note: Hnote, @@ -567,7 +567,7 @@ impl FromHaskell for HsaplingInput { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HorchardInput { sk: Vec, note: Hnote, @@ -581,7 +581,7 @@ impl FromHaskell for HorchardInput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Houtput { kind: u8, ovk: Vec, @@ -598,7 +598,7 @@ impl FromHaskell for Houtput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HOBundle { empty: bool, actions: Vec, @@ -623,7 +623,7 @@ impl HOBundle { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hflags { spends: bool, outputs: bool @@ -642,7 +642,7 @@ impl Hflags { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, orchard: Vec, @@ -671,7 +671,7 @@ impl Hufvk { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hsvk { vk: Vec, ovk: Vec @@ -795,7 +795,7 @@ pub extern "C" fn rust_wrapper_svk_decode( true } Err(e) => { - print!("{}", e); + print!("{:?}", e); false } } @@ -1656,7 +1656,7 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!(sap_anchor); + println!("{:?}", sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); @@ -1668,12 +1668,12 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!(orch_anchor); + println!("{:?}", orch_anchor); let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); - println!(trans_input); + println!("{:?}", trans_input); for t_in in trans_input { if t_in.sk.len() > 1 { let k = SecretKey::from_slice(&t_in.sk).unwrap(); @@ -1697,7 +1697,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); - println!(sap_input); + println!("{:?}", sap_input); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1726,7 +1726,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); - println!(orch_input); + println!("{:?}", orch_input); for o_in in orch_input { if o_in.sk.len() > 1 { let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); @@ -1746,7 +1746,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); - println!(outputs); + println!("{:?}", outputs); for output in outputs { match output.kind { 1 => { From 652e95c3618c1336edc1427f238e33a720fb0510 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 12:10:53 -0500 Subject: [PATCH 139/149] Improve error messaging --- librustzcash-wrapper/src/lib.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 821019f..2291f48 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1824,11 +1824,13 @@ pub extern "C" fn rust_wrapper_create_transaction( }, Err(e) => { match e { - Error::InsufficientFunds(_y) => { + Error::InsufficientFunds(y) => { + println!("{:?}", y); let x = Hhex {bytes: vec![0]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::ChangeRequired(_y1) => { + Error::ChangeRequired(y1) => { + println!("{:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, @@ -1836,27 +1838,27 @@ pub extern "C" fn rust_wrapper_create_transaction( let x = Hhex {bytes: vec![2]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::Balance(x) => { + Error::Balance(y3) => { let x = Hhex {bytes: vec![3]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::TransparentBuild(x) => { + Error::TransparentBuild(y4) => { let x = Hhex {bytes: vec![4]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::SaplingBuild(x) => { + Error::SaplingBuild(y5) => { let x = Hhex {bytes: vec![5]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::OrchardBuild(x) => { + Error::OrchardBuild(y7) => { let x = Hhex {bytes: vec![6]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::OrchardSpend(x) => { + Error::OrchardSpend(y8) => { let x = Hhex {bytes: vec![7]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::OrchardRecipient(x) => { + Error::OrchardRecipient(y9) => { let x = Hhex {bytes: vec![8]}; marshall_to_haskell_var(&x, out, out_len, RW); }, From 2d14ef6b22664bc85d5193cdc3d5837e37bd6b2b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 12:35:36 -0500 Subject: [PATCH 140/149] Update to use commitment trees for anchors --- librustzcash-wrapper/src/lib.rs | 12 ++++++------ src/ZcashHaskell/Utils.hs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 2291f48..6e2a5f5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1647,24 +1647,24 @@ pub extern "C" fn rust_wrapper_create_transaction( out_len: &mut usize){ let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); let sap_wit_reader = Cursor::new(sap_wit_in); - let sap_iw: Option> = read_incremental_witness(sap_wit_reader).ok(); + let sap_iw = read_commitment_tree::>, SAPLING_DEPTH>(sap_wit_reader); let sap_anchor = match sap_iw { - Some(s_iw) => { + Ok(s_iw) => { Some(SaplingAnchor::from(s_iw.root())) }, - None => { + Err(_e) => { None } }; println!("{:?}", sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); - let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); + let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); let orch_anchor = match orch_iw { - Some(o_iw) => { + Ok(o_iw) => { Some(OrchardAnchor::from(o_iw.root())) }, - None => { + Err(_e) => { None } }; diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 9c87f39..db001c8 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -123,8 +123,8 @@ readZebraTransaction hex = rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex createTransaction :: - Maybe SaplingWitness -- ^ to obtain the Sapling anchor - -> Maybe OrchardWitness -- ^ to obtain the Orchard anchor + Maybe SaplingCommitmentTree -- ^ to obtain the Sapling anchor + -> Maybe OrchardCommitmentTree -- ^ 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 @@ -156,10 +156,10 @@ createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oPa rustWrapperCreateTx (case sapAnchor of Nothing -> "0" - Just sA -> toBytes $ sapWit sA) + Just sA -> toBytes $ sapTree sA) (case orchAnchor of Nothing -> "0" - Just oA -> toBytes $ orchWit oA) + Just oA -> toBytes $ orchTree oA) tSpend sSpend oSpend From c7f2bca2c47fbc4e0c6ff8eedac66a574e861de9 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 12:48:30 -0500 Subject: [PATCH 141/149] Remove ovk from transaction creation --- librustzcash-wrapper/src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6e2a5f5..04c1139 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1779,12 +1779,13 @@ pub extern "C" fn rust_wrapper_create_transaction( } }, 4 => { - let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); - let ovk = if output.chg { - Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) - }else { - Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) - }; + //let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); + //let ovk = if output.chg { + //Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) + //} else { + //Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) + //}; + let ovk = None; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; let memo = MemoBytes::from_bytes(&output.memo).unwrap(); From d5e17afc99222a83eadb41db4e8de493f658a97a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 13:34:26 -0500 Subject: [PATCH 142/149] Debugging parameters --- librustzcash-wrapper/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 04c1139..fbaa5c6 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1804,10 +1804,10 @@ pub extern "C" fn rust_wrapper_create_transaction( } let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); let spend_params_reader = Cursor::new(spend_params_in); - let spend_prover = SpendParameters::read(spend_params_reader, true).unwrap(); + let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); - let output_prover = OutputParameters::read(output_params_reader, true).unwrap(); + let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); println!("loaded params"); let result = if net { println!("started main build"); From 31c5cda65aecb251fd37b996fe603309dd467031 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 13:38:12 -0500 Subject: [PATCH 143/149] Improve error message --- librustzcash-wrapper/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index fbaa5c6..0fe909d 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1831,7 +1831,7 @@ pub extern "C" fn rust_wrapper_create_transaction( marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("{:?}", y1); + println!("Change required {:#?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, From 6a632d45c1a6dea59d6c4b1e09057682cdc25d11 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 14:07:54 -0500 Subject: [PATCH 144/149] Change error messaging --- librustzcash-wrapper/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 0fe909d..64e2317 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1831,7 +1831,7 @@ pub extern "C" fn rust_wrapper_create_transaction( marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("Change required {:#?}", y1); + println!("Change required {:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, From c583451c835e097f53485735c38077b9e155f69b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 14:30:59 -0500 Subject: [PATCH 145/149] Update debugging --- librustzcash-wrapper/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 64e2317..4af2b56 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1826,12 +1826,10 @@ pub extern "C" fn rust_wrapper_create_transaction( Err(e) => { match e { Error::InsufficientFunds(y) => { - println!("{:?}", y); let x = Hhex {bytes: vec![0]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("Change required {:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, From 1e31bb73c228554b2f28e4758526c6f568858093 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 14:55:24 -0500 Subject: [PATCH 146/149] Update Rust debugging --- librustzcash-wrapper/src/lib.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 4af2b56..a027dc5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1668,12 +1668,10 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!("{:?}", orch_anchor); let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); - println!("{:?}", trans_input); for t_in in trans_input { if t_in.sk.len() > 1 { let k = SecretKey::from_slice(&t_in.sk).unwrap(); @@ -1697,7 +1695,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); - println!("{:?}", sap_input); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1726,7 +1723,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); - println!("{:?}", orch_input); for o_in in orch_input { if o_in.sk.len() > 1 { let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); @@ -1746,7 +1742,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); - println!("{:?}", outputs); for output in outputs { match output.kind { 1 => { @@ -1791,10 +1786,8 @@ pub extern "C" fn rust_wrapper_create_transaction( let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); - println!("added o-input to main"); } else { let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); - println!("added o-input to test"); } }, _ => { @@ -1808,12 +1801,9 @@ pub extern "C" fn rust_wrapper_create_transaction( let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); - println!("loaded params"); let result = if net { - println!("started main build"); main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) } else { - println!("started test build"); test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) }; match result { From 2108f46afa1b3af2d400846f17f88c46bbcf2514 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 7 May 2024 12:13:54 -0500 Subject: [PATCH 147/149] Remove debugging --- librustzcash-wrapper/src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index a027dc5..9956dde 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1656,7 +1656,7 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!("{:?}", sap_anchor); + //println!("{:?}", sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); @@ -1678,7 +1678,7 @@ pub extern "C" fn rust_wrapper_create_transaction( if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { - println!("added t-input in main"); + //println!("added t-input in main"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1686,7 +1686,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } else { match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { - println!("added t-input in test"); + //println!("added t-input in test"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1774,13 +1774,12 @@ pub extern "C" fn rust_wrapper_create_transaction( } }, 4 => { - //let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); - //let ovk = if output.chg { - //Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) - //} else { - //Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) - //}; - let ovk = None; + let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); + let ovk = if output.chg { + Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) + } else { + Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) + }; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; let memo = MemoBytes::from_bytes(&output.memo).unwrap(); From 37b485288d0cf8999e35c24382dd9c69a4a4a00a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 7 May 2024 15:19:54 -0500 Subject: [PATCH 148/149] Fix bug in Orchard witness calculation --- librustzcash-wrapper/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 9956dde..bc234f5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1562,17 +1562,17 @@ pub extern "C" fn rust_wrapper_update_orchard_witness( let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); iw.append(n); } - let mut out_bytes: Vec = Vec::new(); - let result = write_incremental_witness(&iw, &mut out_bytes); - match result { - Ok(()) => { - let h = Hhex { bytes: out_bytes}; - marshall_to_haskell_var(&h, out, out_len, RW); - }, - Err(_e) => { - let h0 = Hhex { bytes: vec![0]}; - marshall_to_haskell_var(&h0, out, out_len, RW); - } + } + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&iw, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); } } } From 148abd95d0fcce4b4a5a0569a9bedd55a17b8140 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 8 May 2024 13:49:39 -0500 Subject: [PATCH 149/149] Implement fee calculator --- librustzcash-wrapper/src/lib.rs | 145 ++++++++++++++++++-------------- src/C/Zcash.chs | 1 + src/ZcashHaskell/Utils.hs | 4 +- 3 files changed, 86 insertions(+), 64 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index bc234f5..7397401 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1643,6 +1643,7 @@ pub extern "C" fn rust_wrapper_create_transaction( sapoutput_len: usize, net: bool, bl_height: u32, + build: bool, out: *mut u8, out_len: &mut usize){ let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); @@ -1794,71 +1795,89 @@ pub extern "C" fn rust_wrapper_create_transaction( } } } - let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); - let spend_params_reader = Cursor::new(spend_params_in); - let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); - let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); - let output_params_reader = Cursor::new(output_params_in); - let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); - let result = if net { - main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) - } else { - test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) - }; - match result { - Ok(r) => { - let mut out_bytes: Vec = Vec::new(); - let _t = r.transaction().write_v5(&mut out_bytes); - let h = Hhex {bytes: out_bytes}; - marshall_to_haskell_var(&h, 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(y3) => { - let x = Hhex {bytes: vec![3]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::TransparentBuild(y4) => { - let x = Hhex {bytes: vec![4]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::SaplingBuild(y5) => { - let x = Hhex {bytes: vec![5]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardBuild(y7) => { - let x = Hhex {bytes: vec![6]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardSpend(y8) => { - let x = Hhex {bytes: vec![7]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardRecipient(y9) => { - 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); + if build { + let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); + let spend_params_reader = Cursor::new(spend_params_in); + let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); + let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); + let output_params_reader = Cursor::new(output_params_in); + let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); + let result = if net { + main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + } else { + test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + }; + match result { + Ok(r) => { + let mut out_bytes: Vec = Vec::new(); + let _t = r.transaction().write_v5(&mut out_bytes); + let h = Hhex {bytes: out_bytes}; + marshall_to_haskell_var(&h, 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(y3) => { + let x = Hhex {bytes: vec![3]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::TransparentBuild(y4) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuild(y5) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuild(y7) => { + let x = Hhex {bytes: vec![6]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardSpend(y8) => { + let x = Hhex {bytes: vec![7]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardRecipient(y9) => { + 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); + } } } } + } else { + let result = if net { + main_builder.get_fee(&FeeRule::standard()) + } else { + test_builder.get_fee(&FeeRule::standard()) + }; + match result { + Ok(r) => { + let x = Hhex {bytes: r.to_i64_le_bytes().to_vec()}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Err(e) => { + let x = Hhex {bytes: vec![2]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 8f27fa2..0f35be4 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -287,6 +287,7 @@ import ZcashHaskell.Types , toBorshVar* `BS.ByteString'& , `Bool' , `Word64' + , `Bool' , getVarBuffer `Buffer HexString'& } -> `()' diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index db001c8..59b38ed 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -133,8 +133,9 @@ createTransaction :: -> SaplingOutputParams -- ^ the Sapling circuit output parameters -> ZcashNet -- ^ the network to be used -> Int -- ^ target block height + -> Bool -- ^ True to build, False to estimate fee -> Either TxError HexString -createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh = +createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh build = if BS.length (hexBytes txResult) > 1 then Right txResult else case head (BS.unpack $ hexBytes txResult) of @@ -168,3 +169,4 @@ createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oPa (sapOParams oParams) (znet == MainNet) (fromIntegral bh) + build