Compare commits

...

6 commits

Author SHA1 Message Date
0d042d639d Implement Viewing Keys (#106)
This PR includes the functionality to derive and parse Viewing Keys, as well as the functionality to decode transactions using viewing keys.

Reviewed-on: #106
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2025-02-28 18:51:30 +00:00
a28edcb599 Dependency updates (#105)
This PR updates the addresses of the referenced libraries to the new Vergara Tech git server.

Co-authored-by: Rene Vergara <rene@vergara.network>
Reviewed-on: #105
2025-01-06 15:24:54 +00:00
cfa862ec94
Viewing Keys (#104)
This PR contains the code to generate Unified Full Viewing Keys and Unified Incoming Viewing Keys.

Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/104
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2025-01-02 18:36:21 +00:00
7d3ae36d2b
Rust dependencies update (#103)
This PR updates the Rust dependencies and updates the code for the latest version of `zcash_primitives`.

Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/103
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-12-19 15:23:12 +00:00
4289a9ded6
Support for Zebra 2.1.0 (#102)
This PR has the changes needed to support the new format of the response of `getblock` released on Zebra 2.1.0.

Reviewed-on: https://git.vergara.tech/Vergara_Tech/zcash-haskell/pulls/102
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-12-14 12:51:06 +00:00
d45bd7dcf3
Milestone 3 (#101)
This PR contains all the enhancements needed for the completion of Milestone 3 of the Zenith Full Node wallet.
- Implementation of Rust Sapling parameters
- Implementation of native Haskell commitment trees
- Optimization of transaction creation

Reviewed-on: https://git.vergara.tech///Vergara_Tech/zcash-haskell/pulls/101
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-11-21 14:19:43 +00:00
16 changed files with 3482 additions and 1024 deletions

View file

@ -5,6 +5,157 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.8.1.0]
### Fixed
- Producing nullifiers from decoding with viewing keys
## [0.8.0.0]
### Added
- `UnifiedIncomingViewingKey` type
- `ValidVk` type
- Address derivation from full viewing keys
- Address derivation from incoming viewing keys
- Orchard note decoding with full viewing key
- Orchard note decoding with incoming viewing key
- Sapling note decoding with full viewing key
- Sapling note decoding with incoming viewing key
### Fixed
- Transparent viewing key component generation
## [0.7.8.1]
### Changed
- Referenced libraries updated to use the new Vergara Tech git server
## [0.7.8.0]
### Added
- New `UnifiedIncomingViewingKey` type
- Functions to derive Orchard full viewing key
- Functions to derive Sapling full viewing key
- Functions to derive transparent "full viewing key"
- Functions to encode Unified Full Viewing Keys
- Functions to encode Unified Incoming Viewing Keys
## [0.7.7.0]
### Changed
- Updated Rust crates
## [0.7.6.0]
### Changed
- Removed workaround for missing `time` field in Zebra's `getblock` response.
## [0.7.5.0]
### Added
- Sapling commitment node functions
- Sapling Merkle path test
### Changed
- Upgraded Rust dependencies to latest versions:
- `zcash_primitives` 0.19.0
- `zcash_client_backend` 0.14.0
- `orchard` 0.10.0
- `sapling-crypto` 0.3.0
- `incrementalmerkletree` 0.7.0
- `zip32` 0.1.2
## [0.7.4.0]
### Added
- `MerklePath`
## [0.7.3.0]
### Added
- Function to create an Orchard hash from a note commitment
- Function to hash Orchard commitments
### Changed
- Modified frontiers to use `HexString` for ommers
- Optimized `createTransaction`
## [0.7.2.0]
### Changed
- Modified Sapling commitment trees to use Frontier
## [0.7.1.1]
### Added
- `ToJSON` instance for `BlockResponse`
### Changed
- Updated libraries:
- conduit
- data-fix
- happy
- happy-lib
- http-conduit
- iproute
- mono-traversable
- network
- secp256k1-haskell
- strict
- typed-process
## [0.7.1.0]
### Added
- Type `OrchardFrontier`
### Changed
- Modified Orchard commitment trees functions to use Frontier
## [0.7.0.2]
### Changed
- Modified witness update functions to skip the process if no commitments are present
## [0.7.0.1]
### Added
- New error type `PrivacyPolicyError`
## [0.7.0.0]
- Implement `wagyu-zcash-parameters` in Rust bindings
## [0.6.2.3]
### Fixed
- Decoding of unified addresses with no transparent receivers
## [0.6.2.2]
- Added JSON instances for `ZcashNet`
- Added JSON instances for `Transaction`
- Added `ValidAddress`
## [0.6.2.1] ## [0.6.2.1]
### Changed ### Changed

View file

@ -4,10 +4,10 @@ with-compiler: ghc-9.6.5
source-repository-package source-repository-package
type: git type: git
location: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git location: https://code.vergara.tech/Vergara_Tech/haskell-foreign-rust.git
tag: 335e804454cd30da2c526457be37e477f71e4665 tag: 335e804454cd30da2c526457be37e477f71e4665
source-repository-package source-repository-package
type: git type: git
location: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git location: https://code.vergara.tech/Vergara_Tech/haskell-hexstring.git
tag: 39d8da7b11a80269454c2f134a5c834e0f3cb9a7 tag: 39d8da7b11a80269454c2f134a5c834e0f3cb9a7

View file

@ -3,13 +3,13 @@ constraints: any.Cabal ==3.10.3.0,
any.Cabal-syntax ==3.10.3.0, any.Cabal-syntax ==3.10.3.0,
any.HUnit ==1.6.2.0, any.HUnit ==1.6.2.0,
any.OneTuple ==0.4.2, any.OneTuple ==0.4.2,
any.QuickCheck ==2.14.3, any.QuickCheck ==2.15.0.1,
QuickCheck -old-random +templatehaskell, QuickCheck -old-random +templatehaskell,
any.StateVar ==1.2.2, any.StateVar ==1.2.2,
any.aeson ==2.2.3.0, any.aeson ==2.2.3.0,
aeson +ordered-keymap, aeson +ordered-keymap,
any.alex ==3.5.1.0, any.alex ==3.5.2.0,
any.ansi-terminal ==1.1.1, any.ansi-terminal ==1.1.2,
ansi-terminal -example, ansi-terminal -example,
any.ansi-terminal-types ==1.1, any.ansi-terminal-types ==1.1,
any.appar ==0.1.8, any.appar ==0.1.8,
@ -25,7 +25,7 @@ constraints: any.Cabal ==3.10.3.0,
attoparsec -developer, attoparsec -developer,
any.attoparsec-aeson ==2.2.2.0, any.attoparsec-aeson ==2.2.2.0,
any.base ==4.18.2.1, any.base ==4.18.2.1,
any.base-orphans ==0.9.2, any.base-orphans ==0.9.3,
any.base16 ==1.0, any.base16 ==1.0,
any.base16-bytestring ==1.0.2.0, any.base16-bytestring ==1.0.2.0,
any.base58-bytestring ==0.1.0, any.base58-bytestring ==0.1.0,
@ -40,7 +40,7 @@ constraints: any.Cabal ==3.10.3.0,
any.blaze-builder ==0.4.2.3, any.blaze-builder ==0.4.2.3,
any.borsh ==0.3.0, any.borsh ==0.3.0,
any.byteorder ==1.0.4, any.byteorder ==1.0.4,
any.bytes ==0.17.3, any.bytes ==0.17.4,
any.bytestring ==0.11.5.3, any.bytestring ==0.11.5.3,
any.c2hs ==0.28.8, any.c2hs ==0.28.8,
c2hs +base3 -regression, c2hs +base3 -regression,
@ -52,38 +52,35 @@ constraints: any.Cabal ==3.10.3.0,
cereal -bytestring-builder, cereal -bytestring-builder,
any.character-ps ==0.1, any.character-ps ==0.1,
any.colour ==2.3.6, any.colour ==2.3.6,
any.comonad ==5.0.8, any.comonad ==5.0.9,
comonad +containers +distributive +indexed-traversable, comonad +containers +distributive +indexed-traversable,
any.conduit ==1.3.5, any.conduit ==1.3.6,
any.conduit-extra ==1.3.6, any.conduit-extra ==1.3.7,
any.containers ==0.6.7, any.containers ==0.6.7,
any.contravariant ==1.5.5, any.contravariant ==1.5.5,
contravariant +semigroups +statevar +tagged, contravariant +semigroups +statevar +tagged,
any.cookie ==0.5.0, any.cookie ==0.5.0,
any.crypton ==1.0.0, any.crypton ==1.0.1,
crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes, 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.4.1, any.crypton-connection ==0.4.3,
any.crypton-x509 ==1.7.7, any.crypton-x509 ==1.7.7,
any.crypton-x509-store ==1.6.9, any.crypton-x509-store ==1.6.9,
any.crypton-x509-system ==1.6.7, any.crypton-x509-system ==1.6.7,
any.crypton-x509-validation ==1.6.12, any.crypton-x509-validation ==1.6.13,
any.cryptonite ==0.30, 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, 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 ==0.8.0.0,
any.data-default-class ==0.1.2.0, any.data-default-class ==0.2.0.0,
any.data-default-instances-containers ==0.0.1, any.data-fix ==0.3.4,
any.data-default-instances-dlist ==0.0.1,
any.data-default-instances-old-locale ==0.0.1,
any.data-fix ==0.3.3,
any.deepseq ==1.4.8.1, any.deepseq ==1.4.8.1,
any.directory ==1.3.8.4, any.directory ==1.3.8.4,
any.distributive ==0.6.2.1, any.distributive ==0.6.2.1,
distributive +semigroups +tagged, distributive +semigroups +tagged,
any.dlist ==1.0, any.dlist ==1.0,
dlist -werror, dlist -werror,
any.entropy ==0.4.1.10, any.entropy ==0.4.1.11,
entropy -donotgetentropy, entropy -donotgetentropy,
any.envy ==2.1.3.0, any.envy ==2.1.4.0,
any.exceptions ==0.10.7, any.exceptions ==0.10.7,
any.filepath ==1.4.300.1, any.filepath ==1.4.300.1,
any.foreign-rust ==0.1.0, any.foreign-rust ==0.1.0,
@ -92,55 +89,61 @@ constraints: any.Cabal ==3.10.3.0,
any.ghc-bignum ==1.3, any.ghc-bignum ==1.3,
any.ghc-boot-th ==9.6.5, any.ghc-boot-th ==9.6.5,
any.ghc-prim ==0.10.0, any.ghc-prim ==0.10.0,
any.half ==0.3.1, any.half ==0.3.2,
any.happy ==1.20.1.1, any.happy ==2.1.4,
any.hashable ==1.4.7.0, any.happy-lib ==2.1.4,
hashable -arch-native +integer-gmp -random-initial-seed, any.hashable ==1.5.0.0,
any.haskell-lexer ==1.1.1, hashable -arch-native -random-initial-seed,
any.haskell-lexer ==1.1.2,
any.haskoin-core ==1.1.0, any.haskoin-core ==1.1.0,
any.hexstring ==0.12.1.0, any.hexstring ==0.12.1.0,
any.hourglass ==0.2.12, any.hourglass ==0.2.12,
any.hsc2hs ==0.68.10, any.hsc2hs ==0.68.10,
hsc2hs -in-ghc-tree, hsc2hs -in-ghc-tree,
any.hspec ==2.11.9, any.hspec ==2.11.10,
any.hspec-core ==2.11.9, any.hspec-core ==2.11.10,
any.hspec-discover ==2.11.9, any.hspec-discover ==2.11.10,
any.hspec-expectations ==0.8.4, any.hspec-expectations ==0.8.4,
any.http-client ==0.7.17, any.http-client ==0.7.18,
http-client +network-uri, http-client +network-uri,
any.http-client-tls ==0.3.6.3, any.http-client-tls ==0.3.6.4,
any.http-conduit ==2.3.8.3, any.http-conduit ==2.3.9.1,
http-conduit +aeson, http-conduit +aeson,
any.http-types ==0.12.4, any.http-types ==0.12.4,
any.indexed-traversable ==0.1.4, any.indexed-traversable ==0.1.4,
any.indexed-traversable-instances ==0.1.2, any.indexed-traversable-instances ==0.1.2,
any.integer-conversion ==0.1.1, any.integer-conversion ==0.1.1,
any.integer-gmp ==1.1, any.integer-gmp ==1.1,
any.integer-logarithms ==1.0.3.1, any.integer-logarithms ==1.0.4,
integer-logarithms -check-bounds +integer-gmp, integer-logarithms -check-bounds +integer-gmp,
any.iproute ==1.7.12, any.iproute ==1.7.15,
any.language-c ==0.9.3, any.language-c ==0.10.0,
language-c -allwarnings +iecfpextension +usebytestrings, language-c +iecfpextension +usebytestrings,
any.memory ==0.18.0, any.memory ==0.18.0,
memory +support_bytestring +support_deepseq, memory +support_bytestring +support_deepseq,
any.mime-types ==0.1.2.0, any.mime-types ==0.1.2.0,
any.mono-traversable ==1.0.17.0, any.mono-traversable ==1.0.21.0,
any.mtl ==2.3.1, any.mtl ==2.3.1,
any.murmur3 ==1.0.5, any.murmur3 ==1.0.5,
any.network ==3.2.1.0, any.network ==3.2.7.0,
network -devel, network -devel,
any.network-uri ==2.6.4.2, any.network-uri ==2.6.4.2,
any.old-locale ==1.0.0.7, any.old-locale ==1.0.0.7,
any.old-time ==1.1.0.4, any.old-time ==1.1.0.4,
any.os-string ==2.0.6, any.optparse-applicative ==0.18.1.0,
optparse-applicative +process,
any.os-string ==2.0.7,
any.parsec ==3.1.16.1, any.parsec ==3.1.16.1,
any.pem ==0.2.4, any.pem ==0.2.4,
any.pretty ==1.1.3.6, any.pretty ==1.1.3.6,
any.prettyprinter ==1.7.1,
prettyprinter -buildreadme +text,
any.prettyprinter-ansi-terminal ==1.1.3,
any.primitive ==0.9.0.0, any.primitive ==0.9.0.0,
any.process ==1.6.19.0, any.process ==1.6.19.0,
any.quickcheck-io ==0.2.0, any.quickcheck-io ==0.2.0,
any.quickcheck-transformer ==0.3.1.2, any.quickcheck-transformer ==0.3.1.2,
any.random ==1.2.1.2, any.random ==1.2.1.3,
any.regex-base ==0.94.0.2, any.regex-base ==0.94.0.2,
any.regex-compat ==0.95.2.1, any.regex-compat ==0.95.2.1,
any.regex-posix ==0.96.0.1, any.regex-posix ==0.96.0.1,
@ -150,7 +153,7 @@ constraints: any.Cabal ==3.10.3.0,
any.safe ==0.3.21, any.safe ==0.3.21,
any.scientific ==0.3.8.0, any.scientific ==0.3.8.0,
scientific -integer-simple, scientific -integer-simple,
any.secp256k1-haskell ==1.2.0, any.secp256k1-haskell ==1.4.2,
any.semialign ==1.3.1, any.semialign ==1.3.1,
semialign +semigroupoids, semialign +semigroupoids,
any.semigroupoids ==6.0.1, any.semigroupoids ==6.0.1,
@ -160,42 +163,44 @@ constraints: any.Cabal ==3.10.3.0,
any.socks ==0.6.1, any.socks ==0.6.1,
any.sop-core ==0.5.0.2, any.sop-core ==0.5.0.2,
any.split ==0.2.5, any.split ==0.2.5,
any.splitmix ==0.1.0.5, any.splitmix ==0.1.1,
splitmix -optimised-mixer, splitmix -optimised-mixer,
any.stm ==2.5.1.0, any.stm ==2.5.1.0,
any.streaming-commons ==0.2.2.6, any.streaming-commons ==0.2.3.0,
streaming-commons -use-bytestring-builder, streaming-commons -use-bytestring-builder,
any.strict ==0.5, any.strict ==0.5.1,
any.string-conversions ==0.4.0.1, any.string-conversions ==0.4.0.1,
any.tagged ==0.8.8, any.tagged ==0.8.9,
tagged +deepseq +transformers, tagged +deepseq +transformers,
any.tasty ==1.5.3,
tasty +unix,
any.template-haskell ==2.20.0.0, any.template-haskell ==2.20.0.0,
any.text ==2.0.2, any.text ==2.0.2,
any.text-iso8601 ==0.1.1, any.text-iso8601 ==0.1.1,
any.text-short ==0.1.6, any.text-short ==0.1.6,
text-short -asserts, text-short -asserts,
any.tf-random ==0.5, any.tf-random ==0.5,
any.th-abstraction ==0.7.0.0, any.th-abstraction ==0.7.1.0,
any.th-compat ==0.1.5, any.th-compat ==0.1.6,
any.these ==1.2.1, any.these ==1.2.1,
any.time ==1.12.2, any.time ==1.12.2,
any.time-compat ==1.9.7, any.time-compat ==1.9.8,
any.tls ==2.1.0, any.tls ==2.1.7,
tls -devel, tls -devel,
any.transformers ==0.6.1.0, any.transformers ==0.6.1.0,
any.transformers-compat ==0.7.2, any.transformers-compat ==0.7.2,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.typed-process ==0.2.11.1, any.typed-process ==0.2.12.0,
any.unix ==2.8.4.0, any.unix ==2.8.4.0,
any.unix-time ==0.4.15, any.unix-time ==0.4.16,
any.unliftio-core ==0.2.1.0, any.unliftio-core ==0.2.1.0,
any.unordered-containers ==0.2.20, any.unordered-containers ==0.2.20,
unordered-containers -debug, unordered-containers -debug,
any.utf8-string ==1.0.2, any.utf8-string ==1.0.2,
any.uuid-types ==1.0.6, any.uuid-types ==1.0.6,
any.vector ==0.13.1.0, any.vector ==0.13.2.0,
vector +boundschecks -internalchecks -unsafechecks -wall, vector +boundschecks -internalchecks -unsafechecks -wall,
any.vector-algorithms ==0.9.0.2, any.vector-algorithms ==0.9.0.3,
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks, vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
any.vector-stream ==0.1.0.1, any.vector-stream ==0.1.0.1,
any.void ==0.7.3, any.void ==0.7.3,
@ -204,4 +209,4 @@ constraints: any.Cabal ==3.10.3.0,
any.witherable ==0.5, any.witherable ==0.5,
any.zlib ==0.7.1.0, any.zlib ==0.7.1.0,
zlib -bundled-c-zlib +non-blocking-ffi +pkg-config zlib -bundled-c-zlib +non-blocking-ffi +pkg-config
index-state: hackage.haskell.org 2024-07-01T20:28:56Z index-state: hackage.haskell.org 2025-01-31T18:30:19Z

File diff suppressed because it is too large Load diff

View file

@ -9,20 +9,23 @@ haskell-ffi.git = "https://github.com/BeFunctional/haskell-rust-ffi.git"
haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274" haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274"
f4jumble = "0.1" f4jumble = "0.1"
zcash_address = "0.2.0" zcash_address = "0.2.0"
borsh = "0.10" borsh = "0.9"
bech32 = "0.11" bech32 = "0.11"
orchard = "0.7.1" orchard = "0.10.0"
zcash_note_encryption = "0.4.0" zcash_note_encryption = "0.4.0"
zcash_primitives = { version = "0.14.0", features = ["transparent-inputs"]} zcash_primitives = { version = "0.21.0", features = ["transparent-inputs"]}
zcash_client_backend = "0.11.1" zcash_client_backend = "0.16.0"
sapling-crypto = "0.1.3" sapling-crypto = "0.4"
zip32 = "0.1.0" zip32 = "0.1.2"
proc-macro2 = "1.0.66" proc-macro2 = "1.0.66"
nonempty = "0.7.0" nonempty = "0.7.0"
incrementalmerkletree = "0.5.0" incrementalmerkletree = "0.7.0"
secp256k1 = "0.26.0" secp256k1 = "0.27.0"
jubjub = "0.10.0" jubjub = "0.10.0"
rand_core = { version = "0.6.4", features = ["getrandom"]} rand_core = { version = "0.6.4", features = ["getrandom"]}
wagyu-zcash-parameters = "0.2.0"
bip0039 = "0.12.0"
ahash = "0.7.8"
[features] [features]

View file

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "nightly-2024-02-04" channel = "nightly"
components = [ "rustfmt", "rustc-dev"] components = [ "rustfmt", "rustc-dev"]
profile = "minimal" profile = "minimal"

File diff suppressed because it is too large Load diff

View file

@ -120,6 +120,13 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_uivk_decode as rustWrapperUivkDecode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer UnifiedIncomingViewingKey'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode {# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'& , toBorshVar* `OrchardAction'&
@ -128,6 +135,23 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_orchard_note_decrypt_fvk as rustWrapperOrchardNoteDecodeFvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'&
, `Bool'
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_orchard_note_decrypt_ivk as rustWrapperOrchardNoteDecodeIvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'&
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK {# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'& , toBorshVar* `OrchardAction'&
@ -203,16 +227,33 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree {# fun unsafe rust_wrapper_derive_orchard_receiver_fvk as rustWrapperGenOrchardReceiverFvk
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, `Word32'
, `Bool'
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_derive_orchard_receiver_ivk as rustWrapperGenOrchardReceiverIvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree
{ toBorshVar* `SaplingFrontier'&
, toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'& , getVarBuffer `Buffer SaplingFrontier'&
} }
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_sapling_witness as rustWrapperReadSaplingWitness {# fun unsafe rust_wrapper_read_sapling_witness as rustWrapperReadSaplingWitness
{ toBorshVar* `BS.ByteString'& { toBorshVar* `SaplingFrontier'&
, getVarBuffer `Buffer HexString'& , getVarBuffer `Buffer HexString'&
} }
-> `()' -> `()'
@ -232,6 +273,13 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_sapling_frontier as rustWrapperReadSaplingFrontier
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer SaplingFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_decode_sapling_address as rustWrapperDecodeSaplingAddress {# fun unsafe rust_wrapper_decode_sapling_address as rustWrapperDecodeSaplingAddress
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer (BS.ByteString)'& , getVarBuffer `Buffer (BS.ByteString)'&
@ -239,16 +287,120 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree {# fun unsafe rust_wrapper_read_sapling_node as rustWrapperReadSaplingNode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_combine_sapling_nodes as rustWrapperCombineSaplingNodes
{ `Int8'
, toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'& , getVarBuffer `Buffer HexString'&
} }
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_read_orchard_witness as rustWrapperReadOrchardWitness {# fun unsafe rust_wrapper_get_sapling_root as rustWrapperGetSaplingRootTest
{ `Int8'
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_commitment_tree_parts as rustWrapperReadSaplingTreeParts
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer SaplingRawTree'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_tree_anchor as rustWrapperReadSaplingTreeAnchor
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_path_anchor as rustWrapperReadSaplingPathAnchor
{ toBorshVar* `MerklePath'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_node as rustWrapperReadOrchardNode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_combine_orchard_nodes as rustWrapperCombineOrchardNodes
{ `Int8'
, toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_tree_anchor as rustWrapperReadOrchardTreeAnchor
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_witness_anchor as rustWrapperReadOrchardWitnessAnchor
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_path_anchor as rustWrapperReadOrchardPathAnchor
{ toBorshVar* `MerklePath'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_get_orchard_root as rustWrapperGetOrchardRootTest
{ `Int8'
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree
{ toBorshVar* `OrchardFrontier'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer OrchardFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_commitment_tree_parts as rustWrapperReadOrchardTreeParts
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer OrchardRawTree'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_frontier as rustWrapperReadOrchardFrontier
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer OrchardFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_witness as rustWrapperReadOrchardWitness
{ toBorshVar* `OrchardFrontier'&
, getVarBuffer `Buffer HexString'& , getVarBuffer `Buffer HexString'&
} }
-> `()' -> `()'
@ -260,6 +412,15 @@ import ZcashHaskell.Types
-> `Word64' -> `Word64'
#} #}
{# fun unsafe rust_wrapper_orchard_add_node as rustWrapperOrchardAddNodeTest
{ `Int8'
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_update_sapling_witness as rustWrapperUpdateSaplingWitness {# fun unsafe rust_wrapper_update_sapling_witness as rustWrapperUpdateSaplingWitness
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, toBorshVar* `[BS.ByteString]'& , toBorshVar* `[BS.ByteString]'&
@ -283,8 +444,6 @@ import ZcashHaskell.Types
, toBorshVar* `[SaplingTxSpend]'& , toBorshVar* `[SaplingTxSpend]'&
, toBorshVar* `[OrchardTxSpend]'& , toBorshVar* `[OrchardTxSpend]'&
, toBorshVar* `[OutgoingNote]'& , toBorshVar* `[OutgoingNote]'&
, toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, `Bool' , `Bool'
, `Word64' , `Word64'
, `Bool' , `Bool'
@ -292,3 +451,72 @@ import ZcashHaskell.Types
} }
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_create_orchard_fvk as rustWrapperCreateOrchardFvk
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_create_orchard_ivk as rustWrapperCreateOrchardIvk
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_create_sapling_fvk as rustWrapperCreateSaplingFvk
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_create_sapling_ivk as rustWrapperCreateSaplingIvk
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_receiver_fvk as rustWrapperSaplingReceiverFvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer BS.ByteString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_receiver_ivk as rustWrapperSaplingReceiverIvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer BS.ByteString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_change_receiver_fvk as rustWrapperSaplingChgReceiverFvk
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer BS.ByteString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_note_decrypt_fvk as rustWrapperSaplingDecodeFvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `ShieldedOutput'&
, `Bool'
, `Int64'
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_note_decrypt_ivk as rustWrapperSaplingDecodeIvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `ShieldedOutput'&
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}

View file

@ -1,3 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
-- Copyright 2022-2024 Vergara Technologies LLC -- Copyright 2022-2024 Vergara Technologies LLC
-- This file is part of Zcash-Haskell. -- This file is part of Zcash-Haskell.
-- --
@ -15,13 +17,47 @@
module ZcashHaskell.Keys where module ZcashHaskell.Keys where
import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed) import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
import Crypto.Secp256k1 (PubKey(..), createContext, exportPubKey)
import qualified Data.Binary as Bi
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.HexString (HexString(..), hexBytes)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import Data.Word (Word8(..))
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
( withBorshVarBuffer ( withBorshVarBuffer
, withPureBorshVarBuffer , withPureBorshVarBuffer
) )
import ZcashHaskell.Types (Phrase, Seed(..), ToBytes(..)) import Haskoin.Address.Base58 (decodeBase58)
import Haskoin.Crypto.Keys.Extended
( DerivPath(..)
, DerivPathI(..)
, XPubKey(..)
, derivePath
, deriveXPubKey
, xPubExport
)
import Haskoin.Network.Constants (btc)
import ZcashHaskell.Orchard (deriveOrchardFvk, deriveOrchardIvk)
import ZcashHaskell.Sapling (deriveSaplingFvk, deriveSaplingIvk)
import ZcashHaskell.Types
( OrchardSpendingKey(..)
, Phrase
, SaplingSpendingKey(..)
, Seed(..)
, ToBytes(..)
, TransparentSpendingKey(..)
, UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidVk(..)
, ZcashNet(..)
, uniFullViewingKeyHrp
, uniIncomingViewingKeyHrp
, uniTestFullViewingKeyHrp
, uniTestIncomingViewingKeyHrp
)
import ZcashHaskell.Utils (encodeBech32m, f4Jumble)
-- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses. -- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses.
generateWalletSeedPhrase :: IO Phrase generateWalletSeedPhrase :: IO Phrase
@ -36,3 +72,138 @@ getWalletSeed p =
where where
result :: Seed result :: Seed
result = (withPureBorshVarBuffer . rustWrapperGetSeed) p result = (withPureBorshVarBuffer . rustWrapperGetSeed) p
-- | Derive a transparent root node for unified viewing keys
deriveFullTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
deriveFullTransparentNode sk = do
ioCtx <- createContext
let (XPubKey d p i c k) = deriveXPubKey ioCtx sk
let tPubKeyBytes = BSL.toStrict (Bi.encode c) <> exportPubKey ioCtx True k
if BS.length tPubKeyBytes == 65
then return tPubKeyBytes
else fail "Unable to get transparent key bytes"
-- | Derive a transparent incoming root node for unified incoming viewing keys
deriveIncomingTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
deriveIncomingTransparentNode sk = do
ioCtx <- createContext
let path = Deriv :/ 0 :: DerivPath
let childPrvKey = derivePath ioCtx path sk
let (XPubKey d p i c k) = deriveXPubKey ioCtx childPrvKey
let tPubKeyBytes = BSL.toStrict (Bi.encode c) <> exportPubKey ioCtx True k
if BS.length tPubKeyBytes == 65
then return tPubKeyBytes
else fail "Unable to get transparent key bytes"
-- | Derive a Unified Full Viewing Key
deriveUfvk ::
ZcashNet
-> Maybe OrchardSpendingKey
-> Maybe SaplingSpendingKey
-> Maybe TransparentSpendingKey
-> IO ValidVk
deriveUfvk net okey skey tkey = do
tSec <- maybe (return BS.empty) deriveFullTransparentNode tkey
let oSec = deriveOrchardFvk =<< okey
let sSec = deriveSaplingFvk =<< skey
case oSec of
Nothing -> fail "Unable to derive Orchard viewing key"
Just oSec' -> do
return $
FullVk $
UnifiedFullViewingKey
(case net of
MainNet -> 1
TestNet -> 2)
(hexBytes oSec')
(maybe BS.empty hexBytes sSec)
tSec
-- | Derive a Unified Incoming Viewing Key
deriveUivk ::
ZcashNet
-> Maybe OrchardSpendingKey
-> Maybe SaplingSpendingKey
-> Maybe TransparentSpendingKey
-> IO ValidVk
deriveUivk net okey skey tkey = do
tSec <- maybe (return BS.empty) deriveIncomingTransparentNode tkey
let oSec = deriveOrchardIvk =<< okey
let sSec = deriveSaplingIvk =<< skey
case oSec of
Nothing -> fail "Unable to derive Orchard viewing key"
Just oSec' -> do
return $
IncomingVk $
UnifiedIncomingViewingKey
(case net of
MainNet -> 1
TestNet -> 2)
(hexBytes oSec')
(maybe BS.empty hexBytes sSec)
tSec
-- | Encode a Unified Viewing Key per [ZIP-316](https://zips.z.cash/zip-0316)
encodeVK ::
ValidVk -- ^ The viewing key
-> T.Text
encodeVK vk = encodeBech32m (E.encodeUtf8 hr) b
where
tReceiver =
if tvk /= BS.empty
then packReceiver 0x00 $ Just tvk
else packReceiver 0x00 Nothing
b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding
tvk =
case vk of
FullVk f -> t_key f
IncomingVk i -> i_t_key i
svk =
case vk of
FullVk f -> s_key f
IncomingVk i -> i_s_key i
ovk =
case vk of
FullVk f -> o_key f
IncomingVk i -> i_o_key i
znet =
case vk of
FullVk f ->
if net f == 1
then MainNet
else TestNet
IncomingVk i ->
if i_net i == 1
then MainNet
else TestNet
hr =
case vk of
FullVk _ ->
case znet of
MainNet -> uniFullViewingKeyHrp
TestNet -> uniTestFullViewingKeyHrp
IncomingVk _ ->
case znet of
MainNet -> uniIncomingViewingKeyHrp
TestNet -> uniTestIncomingViewingKeyHrp
sReceiver =
packReceiver 0x02 $
if svk /= BS.empty
then Just svk
else Nothing
oReceiver =
packReceiver 0x03 $
if ovk /= BS.empty
then Just ovk
else Nothing
padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr
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

View file

@ -18,16 +18,32 @@
module ZcashHaskell.Orchard where module ZcashHaskell.Orchard where
import C.Zcash import C.Zcash
( rustWrapperGenOrchardReceiver ( rustWrapperCombineOrchardNodes
, rustWrapperCreateOrchardFvk
, rustWrapperCreateOrchardIvk
, rustWrapperGenOrchardReceiver
, rustWrapperGenOrchardReceiverFvk
, rustWrapperGenOrchardReceiverIvk
, rustWrapperGenOrchardSpendKey , rustWrapperGenOrchardSpendKey
, rustWrapperGetOrchardRootTest
, rustWrapperOrchardAddNodeTest
, rustWrapperOrchardCheck , rustWrapperOrchardCheck
, rustWrapperOrchardNoteDecode , rustWrapperOrchardNoteDecode
, rustWrapperOrchardNoteDecodeFvk
, rustWrapperOrchardNoteDecodeIvk
, rustWrapperOrchardNoteDecodeSK , rustWrapperOrchardNoteDecodeSK
, rustWrapperReadOrchardCommitmentTree , rustWrapperReadOrchardCommitmentTree
, rustWrapperReadOrchardFrontier
, rustWrapperReadOrchardNode
, rustWrapperReadOrchardPathAnchor
, rustWrapperReadOrchardPosition , rustWrapperReadOrchardPosition
, rustWrapperReadOrchardTreeAnchor
, rustWrapperReadOrchardTreeParts
, rustWrapperReadOrchardWitness , rustWrapperReadOrchardWitness
, rustWrapperReadOrchardWitnessAnchor
, rustWrapperUADecode , rustWrapperUADecode
, rustWrapperUfvkDecode , rustWrapperUfvkDecode
, rustWrapperUivkDecode
, rustWrapperUpdateOrchardWitness , rustWrapperUpdateOrchardWitness
) )
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
@ -37,6 +53,11 @@ import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Word import Data.Word
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
import ZcashHaskell.Sapling (decodeSaplingAddress)
import ZcashHaskell.Transparent
( decodeExchangeAddress
, decodeTransparentAddress
)
import ZcashHaskell.Types import ZcashHaskell.Types
import ZcashHaskell.Utils (encodeBech32, encodeBech32m, f4Jumble) import ZcashHaskell.Utils (encodeBech32, encodeBech32m, f4Jumble)
@ -76,6 +97,35 @@ genOrchardReceiver i scope osk =
(fromIntegral i) (fromIntegral i)
(scope == External) (scope == External)
-- | Derives an Orchard receiver for the given full viewing key and index
genOrchardReceiverFvk ::
Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses, `Internal` for change addresses
-> BS.ByteString -- ^ The full viewing key
-> Maybe OrchardReceiver
genOrchardReceiverFvk i scope osk =
if BS.length k /= 43
then Nothing
else Just $ OrchardReceiver k
where
k =
withPureBorshVarBuffer $
rustWrapperGenOrchardReceiverFvk osk (fromIntegral i) (scope == External)
-- | Derives an Orchard receiver for the given incoming viewing key and index
genOrchardReceiverIvk ::
Int -- ^ The index of the address to be created
-> BS.ByteString -- ^ The full viewing key
-> Maybe OrchardReceiver
genOrchardReceiverIvk i osk =
if BS.length k /= 43
then Nothing
else Just $ OrchardReceiver k
where
k =
withPureBorshVarBuffer $
rustWrapperGenOrchardReceiverIvk osk (fromIntegral i)
-- | Checks if given bytestring is a valid encoded unified address -- | Checks if given bytestring is a valid encoded unified address
isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress
isValidUnifiedAddress str = isValidUnifiedAddress str =
@ -98,9 +148,9 @@ isValidUnifiedAddress str =
(if BS.length (raw_s x) == 43 (if BS.length (raw_s x) == 43
then Just $ SaplingReceiver (raw_s x) then Just $ SaplingReceiver (raw_s x)
else Nothing) else Nothing)
(if not (BS.null (raw_t x)) (if BS.length (raw_t x) > 1
then Just $ TransparentReceiver P2PKH (fromRawBytes $ raw_t x) then Just $ TransparentReceiver P2PKH (fromRawBytes $ raw_t x)
else if not (BS.null (raw_to x)) else if BS.length (raw_to x) > 1
then Just $ TransparentReceiver P2SH (fromRawBytes $ raw_to x) then Just $ TransparentReceiver P2SH (fromRawBytes $ raw_to x)
else Nothing) else Nothing)
@ -143,6 +193,15 @@ decodeUfvk str =
where where
decodedKey = (withPureBorshVarBuffer . rustWrapperUfvkDecode) str decodedKey = (withPureBorshVarBuffer . rustWrapperUfvkDecode) str
-- | Attempts to decode the given bytestring into a Unified Full Viewing Key
decodeUivk :: BS.ByteString -> Maybe UnifiedIncomingViewingKey
decodeUivk str =
case i_net decodedKey of
0 -> Nothing
_ -> Just decodedKey
where
decodedKey = (withPureBorshVarBuffer . rustWrapperUivkDecode) str
-- | Check if the given UVK matches the UA given -- | Check if the given UVK matches the UA given
matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool
matchOrchardAddress = rustWrapperOrchardCheck matchOrchardAddress = rustWrapperOrchardCheck
@ -159,6 +218,30 @@ decryptOrchardAction key encAction =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperOrchardNoteDecode (o_key key) encAction rustWrapperOrchardNoteDecode (o_key key) encAction
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
decryptOrchardActionFvk ::
UnifiedFullViewingKey -> Scope -> OrchardAction -> Maybe DecodedNote
decryptOrchardActionFvk key scope encAction =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperOrchardNoteDecodeFvk (o_key key) encAction (scope == External)
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
decryptOrchardActionIvk ::
UnifiedIncomingViewingKey -> OrchardAction -> Maybe DecodedNote
decryptOrchardActionIvk key encAction =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperOrchardNoteDecodeIvk (i_o_key key) encAction
getSaplingFromUA :: BS.ByteString -> Maybe T.Text getSaplingFromUA :: BS.ByteString -> Maybe T.Text
getSaplingFromUA uadd = do getSaplingFromUA uadd = do
let a = isValidUnifiedAddress uadd let a = isValidUnifiedAddress uadd
@ -189,40 +272,159 @@ decryptOrchardActionSK sk scope oa =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External) rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External)
-- | Update a Orchard commitment tree getOrchardFrontier :: OrchardCommitmentTree -> Maybe OrchardFrontier
updateOrchardCommitmentTree :: getOrchardFrontier tree =
OrchardCommitmentTree -- ^ the base tree if of_pos updatedTree > 1
-> HexString -- ^ the new note commitment then Just updatedTree
-> Maybe OrchardCommitmentTree
updateOrchardCommitmentTree tree cmx =
if BS.length (hexBytes updatedTree) > 1
then Just $ OrchardCommitmentTree updatedTree
else Nothing else Nothing
where where
updatedTree = updatedTree =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperReadOrchardCommitmentTree rustWrapperReadOrchardFrontier $ toBytes $ orchTree tree
(hexBytes $ orchTree tree)
(hexBytes cmx) getOrchardTreeAnchor :: OrchardCommitmentTree -> HexString
getOrchardTreeAnchor tree =
withPureBorshVarBuffer $
rustWrapperReadOrchardTreeAnchor $ toBytes $ orchTree tree
getOrchardWitnessAnchor :: OrchardWitness -> HexString
getOrchardWitnessAnchor wit =
withPureBorshVarBuffer $
rustWrapperReadOrchardWitnessAnchor $ toBytes $ orchWit wit
getOrchardRootTest :: Int -> HexString
getOrchardRootTest level =
withPureBorshVarBuffer $ rustWrapperGetOrchardRootTest $ fromIntegral level
addOrchardNodeGetRoot :: Int -> BS.ByteString -> HexString
addOrchardNodeGetRoot l n =
withPureBorshVarBuffer $ rustWrapperOrchardAddNodeTest (fromIntegral l) n
getOrchardTreeParts :: OrchardCommitmentTree -> Maybe OrchardTree
getOrchardTreeParts h =
if isBlank (ort_left tree) && isBlank (ort_right tree)
then Nothing
else Just $
OrchardTree
(parseHex $ ort_left tree)
(parseHex $ ort_right tree)
(map parseHex (ort_parents tree))
where
isBlank h = (BS.length $ hexBytes $ h) == 1
parseHex h =
if (BS.length $ hexBytes $ h) > 1
then Just h
else Nothing
tree =
withPureBorshVarBuffer $
rustWrapperReadOrchardTreeParts $ toBytes $ orchTree h
getOrchardPathAnchor :: HexString -> MerklePath -> HexString
getOrchardPathAnchor hex p =
withPureBorshVarBuffer $ rustWrapperReadOrchardPathAnchor p (hexBytes hex)
-- | Update a Orchard commitment tree
updateOrchardCommitmentTree ::
OrchardFrontier -- ^ the base tree
-> HexString -- ^ the new note commitment
-> Maybe OrchardFrontier
updateOrchardCommitmentTree tree cmx =
if of_pos updatedTree > 1
then Just updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadOrchardCommitmentTree tree (hexBytes cmx)
-- | Get the Orchard incremental witness from a commitment tree -- | Get the Orchard incremental witness from a commitment tree
getOrchardWitness :: OrchardCommitmentTree -> Maybe OrchardWitness getOrchardWitness :: OrchardFrontier -> Maybe OrchardWitness
getOrchardWitness tree = getOrchardWitness tree =
if BS.length (hexBytes wit) > 1 if BS.length (hexBytes wit) > 1
then Just $ OrchardWitness wit then Just $ OrchardWitness wit
else Nothing else Nothing
where where
wit = wit = withPureBorshVarBuffer $ rustWrapperReadOrchardWitness tree
withPureBorshVarBuffer $
rustWrapperReadOrchardWitness (hexBytes $ orchTree tree)
-- | Get the Sapling note position from a witness -- | Get the Sapling note position from a witness
getOrchardNotePosition :: OrchardWitness -> Integer getOrchardNotePosition :: OrchardWitness -> Integer
getOrchardNotePosition = getOrchardNotePosition =
fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit
-- | Update the witness of an Orchard note
updateOrchardWitness :: OrchardWitness -> [HexString] -> OrchardWitness updateOrchardWitness :: OrchardWitness -> [HexString] -> OrchardWitness
updateOrchardWitness wit cmus = updateOrchardWitness wit cmus =
OrchardWitness $ if not (null cmus)
then OrchardWitness $
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperUpdateOrchardWitness (toBytes $ orchWit wit) (map toBytes cmus) rustWrapperUpdateOrchardWitness
(toBytes $ orchWit wit)
(map toBytes cmus)
else wit
getOrchardNodeValue :: BS.ByteString -> Maybe HexString
getOrchardNodeValue cmx =
if BS.length (hexBytes n) > 1
then Just n
else Nothing
where
n = withPureBorshVarBuffer $ rustWrapperReadOrchardNode cmx
combineOrchardNodes :: Integer -> HexString -> HexString -> Maybe HexString
combineOrchardNodes level n1 n2 =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r =
withPureBorshVarBuffer $
rustWrapperCombineOrchardNodes
(fromIntegral level)
(toBytes n1)
(toBytes n2)
-- | Parse a potential Zcash address
parseAddress :: BS.ByteString -> Maybe ValidAddress
parseAddress t =
case isValidUnifiedAddress t of
Nothing ->
case decodeSaplingAddress t of
Nothing ->
case decodeTransparentAddress t of
Nothing ->
case decodeExchangeAddress t of
Nothing -> Nothing
Just x -> Just $ Exchange x
Just t -> Just $ Transparent t
Just s -> Just $ Sapling s
Just u -> Just $ Unified u
compareAddress :: ValidAddress -> UnifiedAddress -> Bool
compareAddress a u =
case a of
Unified i -> i == u
Sapling s -> s_rec u == Just (sa_receiver s) && ua_net u == net_type s
Transparent t -> t_rec u == Just (ta_receiver t) && ua_net u == ta_network t
Exchange x -> False
-- | Derive an Orchard Full Viewing Key
deriveOrchardFvk ::
OrchardSpendingKey -- ^ The Orchard spending key
-> Maybe HexString
deriveOrchardFvk sk =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r = withPureBorshVarBuffer $ rustWrapperCreateOrchardFvk $ getBytes sk
-- | Derive an Orchard Incoming Viewing Key
deriveOrchardIvk ::
OrchardSpendingKey -- ^ The Orchard spending key
-> Maybe HexString
deriveOrchardIvk sk =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r = withPureBorshVarBuffer $ rustWrapperCreateOrchardIvk $ getBytes sk

View file

@ -18,16 +18,30 @@
module ZcashHaskell.Sapling where module ZcashHaskell.Sapling where
import C.Zcash import C.Zcash
( rustWrapperDecodeSaplingAddress ( rustWrapperCombineSaplingNodes
, rustWrapperCreateSaplingFvk
, rustWrapperCreateSaplingIvk
, rustWrapperDecodeSaplingAddress
, rustWrapperGetSaplingRootTest
, rustWrapperIsShielded , rustWrapperIsShielded
, rustWrapperReadSaplingCommitmentTree , rustWrapperReadSaplingCommitmentTree
, rustWrapperReadSaplingFrontier
, rustWrapperReadSaplingNode
, rustWrapperReadSaplingPathAnchor
, rustWrapperReadSaplingPosition , rustWrapperReadSaplingPosition
, rustWrapperReadSaplingTreeAnchor
, rustWrapperReadSaplingTreeParts
, rustWrapperReadSaplingWitness , rustWrapperReadSaplingWitness
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingChgReceiverFvk
, rustWrapperSaplingDecodeEsk , rustWrapperSaplingDecodeEsk
, rustWrapperSaplingDecodeFvk
, rustWrapperSaplingDecodeIvk
, rustWrapperSaplingNoteDecode , rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress , rustWrapperSaplingPaymentAddress
, rustWrapperSaplingReceiverFvk
, rustWrapperSaplingReceiverIvk
, rustWrapperSaplingSpendingkey , rustWrapperSaplingSpendingkey
, rustWrapperSaplingVkDecode , rustWrapperSaplingVkDecode
, rustWrapperTxParse , rustWrapperTxParse
@ -37,6 +51,7 @@ import Data.Aeson
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Char8 as C
import Data.HexString (HexString(..), fromText, hexString, toBytes, toText) import Data.HexString (HexString(..), fromText, hexString, toBytes, toText)
import Data.Int (Int64, Int8)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Word import Data.Word
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
@ -85,6 +100,27 @@ decodeSaplingOutput key out =
decodedAction = decodedAction =
withPureBorshVarBuffer $ rustWrapperSaplingNoteDecode key out withPureBorshVarBuffer $ rustWrapperSaplingNoteDecode key out
-- | Attempt to decode the given @ShieldedOutput@ using the given Sapling full viewing key
decodeSaplingOutputFvk ::
BS.ByteString -> ShieldedOutput -> Scope -> Int64 -> Maybe DecodedNote
decodeSaplingOutputFvk fvk so scope pos =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperSaplingDecodeFvk fvk so (scope == External) pos
-- | Attempt to decode the given @ShieldedOutput@ using the given Sapling incoming viewing key
decodeSaplingOutputIvk :: BS.ByteString -> ShieldedOutput -> Maybe DecodedNote
decodeSaplingOutputIvk fvk so =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction = withPureBorshVarBuffer $ rustWrapperSaplingDecodeIvk fvk so
instance FromJSON RawTxResponse where instance FromJSON RawTxResponse where
parseJSON = parseJSON =
withObject "RawTxResponse" $ \obj -> do withObject "RawTxResponse" $ \obj -> do
@ -174,6 +210,28 @@ genSaplingPaymentAddress i extspk =
(getBytes extspk) (getBytes extspk)
(fromIntegral (i * 111))) (fromIntegral (i * 111)))
-- | Attempts to generate a Sapling receiver using an full viewing key and a diversifier index
genSaplingReceiverFvk :: Int -> BS.ByteString -> Maybe SaplingReceiver
genSaplingReceiverFvk i fvk =
if BS.length res == 43
then Just $ SaplingReceiver res
else Nothing
where
res =
withPureBorshVarBuffer
(rustWrapperSaplingReceiverFvk fvk (fromIntegral (i * 111)))
-- | Attempts to generate a Sapling receiver using an incoming viewing key and a diversifier index
genSaplingReceiverIvk :: Int -> BS.ByteString -> Maybe SaplingReceiver
genSaplingReceiverIvk i ivk =
if BS.length res == 43
then Just $ SaplingReceiver res
else Nothing
where
res =
withPureBorshVarBuffer
(rustWrapperSaplingReceiverIvk ivk (fromIntegral (i * 111)))
-- | Generate an internal Sapling address -- | Generate an internal Sapling address
genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver
genSaplingInternalAddress sk = genSaplingInternalAddress sk =
@ -184,32 +242,97 @@ genSaplingInternalAddress sk =
res = res =
withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk) withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk)
-- | Update a Sapling commitment tree -- | Generate a change Sapling receiver from Full Viewing Key
updateSaplingCommitmentTree :: genSaplingInternalAddressFvk :: BS.ByteString -> Maybe SaplingReceiver
SaplingCommitmentTree -- ^ the base tree genSaplingInternalAddressFvk fvk =
-> HexString -- ^ the new note commitment if BS.length res == 43
-> Maybe SaplingCommitmentTree then Just $ SaplingReceiver res
updateSaplingCommitmentTree tree cmu = else Nothing
if BS.length (hexBytes updatedTree) > 1 where
then Just $ SaplingCommitmentTree updatedTree res = withPureBorshVarBuffer (rustWrapperSaplingChgReceiverFvk fvk)
getSaplingNodeValue :: BS.ByteString -> Maybe HexString
getSaplingNodeValue cmu =
if BS.length (hexBytes n) > 1
then Just n
else Nothing
where
n = withPureBorshVarBuffer $ rustWrapperReadSaplingNode cmu
combineSaplingNodes :: Int8 -> HexString -> HexString -> Maybe HexString
combineSaplingNodes level n1 n2 =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r =
withPureBorshVarBuffer $
rustWrapperCombineSaplingNodes level (toBytes n1) (toBytes n2)
getSaplingRootTest :: Int8 -> HexString
getSaplingRootTest level =
withPureBorshVarBuffer $ rustWrapperGetSaplingRootTest level
getSaplingTreeParts :: SaplingCommitmentTree -> Maybe SaplingTree
getSaplingTreeParts h =
if isBlank (srt_left tree) && isBlank (srt_right tree)
then Nothing
else Just $
SaplingTree
(parseHex $ srt_left tree)
(parseHex $ srt_right tree)
(map parseHex (srt_parents tree))
where
isBlank h = (BS.length $ hexBytes $ h) == 1
parseHex h =
if (BS.length $ hexBytes $ h) > 1
then Just h
else Nothing
tree =
withPureBorshVarBuffer $
rustWrapperReadSaplingTreeParts $ toBytes $ sapTree h
getSaplingTreeAnchor :: SaplingCommitmentTree -> HexString
getSaplingTreeAnchor tree =
withPureBorshVarBuffer $
rustWrapperReadSaplingTreeAnchor $ toBytes $ sapTree tree
getSaplingPathAnchor :: HexString -> MerklePath -> HexString
getSaplingPathAnchor hex p =
withPureBorshVarBuffer $ rustWrapperReadSaplingPathAnchor p (hexBytes hex)
getSaplingFrontier :: SaplingCommitmentTree -> Maybe SaplingFrontier
getSaplingFrontier tree =
if sf_pos updatedTree > 1
then Just updatedTree
else Nothing else Nothing
where where
updatedTree = updatedTree =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperReadSaplingCommitmentTree rustWrapperReadSaplingFrontier $ toBytes $ sapTree tree
(hexBytes $ sapTree tree)
(hexBytes cmu) -- | Update a Sapling commitment tree
updateSaplingCommitmentTree ::
SaplingFrontier -- ^ the base tree
-> HexString -- ^ the new note commitment
-> Maybe SaplingFrontier
updateSaplingCommitmentTree tree cmu =
if sf_pos updatedTree > 1
then Just updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadSaplingCommitmentTree tree (hexBytes cmu)
-- | Get the Sapling incremental witness from a commitment tree -- | Get the Sapling incremental witness from a commitment tree
getSaplingWitness :: SaplingCommitmentTree -> Maybe SaplingWitness getSaplingWitness :: SaplingFrontier -> Maybe SaplingWitness
getSaplingWitness tree = getSaplingWitness tree =
if BS.length (hexBytes wit) > 1 if BS.length (hexBytes wit) > 1
then Just $ SaplingWitness wit then Just $ SaplingWitness wit
else Nothing else Nothing
where where
wit = wit = withPureBorshVarBuffer $ rustWrapperReadSaplingWitness tree
withPureBorshVarBuffer $
rustWrapperReadSaplingWitness (hexBytes $ sapTree tree)
-- | Get the Sapling note position from a witness -- | Get the Sapling note position from a witness
getSaplingNotePosition :: SaplingWitness -> Integer getSaplingNotePosition :: SaplingWitness -> Integer
@ -218,9 +341,13 @@ getSaplingNotePosition =
updateSaplingWitness :: SaplingWitness -> [HexString] -> SaplingWitness updateSaplingWitness :: SaplingWitness -> [HexString] -> SaplingWitness
updateSaplingWitness wit cmus = updateSaplingWitness wit cmus =
SaplingWitness $ if not (null cmus)
then SaplingWitness $
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperUpdateSaplingWitness (toBytes $ sapWit wit) (map toBytes cmus) rustWrapperUpdateSaplingWitness
(toBytes $ sapWit wit)
(map toBytes cmus)
else wit
-- | Encode a SaplingReceiver into HRF text -- | Encode a SaplingReceiver into HRF text
encodeSaplingAddress :: ZcashNet -> SaplingReceiver -> Maybe T.Text encodeSaplingAddress :: ZcashNet -> SaplingReceiver -> Maybe T.Text
@ -250,3 +377,25 @@ decodeSaplingAddress sapling_address = do
where where
sa = sa =
withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address
-- | Derive a Sapling Full Viewing Key
deriveSaplingFvk ::
SaplingSpendingKey -- ^ The Sapling spending key
-> Maybe HexString
deriveSaplingFvk sk =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r = withPureBorshVarBuffer $ rustWrapperCreateSaplingFvk $ getBytes sk
-- | Derive a Sapling Incoming Viewing Key
deriveSaplingIvk ::
SaplingSpendingKey -- ^ The Sapling spending key
-> Maybe HexString
deriveSaplingIvk sk =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r = withPureBorshVarBuffer $ rustWrapperCreateSaplingIvk $ getBytes sk

View file

@ -1,3 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
-- Copyright 2022-2024 Vergara Technologies LLC -- Copyright 2022-2024 Vergara Technologies LLC
-- --
-- This file is part of Zcash-Haskell. -- This file is part of Zcash-Haskell.
@ -18,33 +20,36 @@ module ZcashHaskell.Transparent where
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Crypto.Hash import Crypto.Hash
import Crypto.Secp256k1 import Crypto.Secp256k1
import qualified Data.Binary as Bi
import qualified Data.ByteArray as BA import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58)
import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as Short
import Data.Char (chr) import Data.Char (chr)
import Data.HexString import Data.HexString
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Word import Data.Word
import Haskoin.Address (Address(..)) import Haskoin.Address (Address(..))
import qualified Haskoin.Crypto.Hash as H import qualified Haskoin.Crypto.Hash as H (Hash256(..))
import Haskoin.Crypto.Keys.Extended import Haskoin.Crypto.Keys.Extended
import ZcashHaskell.Types import ZcashHaskell.Types
( AccountId
-- ( AccountId , CoinType(..)
-- , CoinType(..) , ExchangeAddress(..)
-- , Scope(..) , RawData(..)
-- , Seed(..) , Scope(..)
-- , ToBytes(..) , Seed(..)
-- , TransparentAddress(..) , ToBytes(..)
-- , TransparentReceiver(..) , TransparentAddress(..)
-- , TransparentSpendingKey(..) , TransparentReceiver(..)
-- , TransparentType(..) , TransparentSpendingKey(..)
-- , ZcashNet(..) , TransparentType(..)
-- , getTransparentPrefix , ZcashNet(..)
-- , getValue , getTransparentPrefix
-- ) , getValue
)
import ZcashHaskell.Utils (decodeBech32, encodeBech32m) import ZcashHaskell.Utils (decodeBech32, encodeBech32m)
-- | Required for `TransparentReceiver` encoding and decoding -- | Required for `TransparentReceiver` encoding and decoding
@ -101,7 +106,76 @@ genTransparentReceiver i scope xprvk = do
ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j
_anyOtherKind -> throwIO $ userError "Unsupported transparent address type" _anyOtherKind -> throwIO $ userError "Unsupported transparent address type"
-- | Generate a transparent receiver -- | Generate a transparent receiver from a Full Transparent Node
genTransparentReceiverFvk ::
Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses
-> BS.ByteString -- ^ The transparent public key
-> IO (Maybe TransparentReceiver)
genTransparentReceiverFvk i scope pubkey = do
ioCtx <- createContext
let s =
case scope of
External -> 0
Internal -> 1
let path = Deriv :/ s :/ fromIntegral i :: SoftPath
let fp' = textToFingerprint "f09f8fbe"
case fp' of
Left e -> fail "couldn't get fingerprint"
Right fp -> do
let x = importPubKey ioCtx $ BS.takeEnd 33 pubkey
case x of
Nothing -> fail "couldn't get pubkey"
Just pk -> do
let prepKey =
XPubKey
3
fp
0
(Bi.decode $ BSL.fromStrict $ BS.take 32 pubkey)
pk
let childPubKey = derivePubPath ioCtx path prepKey
let x = xPubAddr ioCtx childPubKey
case x of
PubKeyAddress k ->
return $ Just $ TransparentReceiver P2PKH $ fromBinary k
ScriptAddress j ->
return $ Just $ TransparentReceiver P2SH $ fromBinary j
_anyOtherKind -> return Nothing
-- | Generate a transparent receiver from a Incoming Transparent Node
genTransparentReceiverIvk ::
Int -- ^ The index of the address to be created
-> BS.ByteString -- ^ The transparent public key
-> IO (Maybe TransparentReceiver)
genTransparentReceiverIvk i pubkey = do
ioCtx <- createContext
let path = Deriv :/ fromIntegral i :: SoftPath
let fp' = textToFingerprint "f09f8fbe"
case fp' of
Left e -> fail "couldn't get fingerprint"
Right fp -> do
let x = importPubKey ioCtx $ BS.takeEnd 33 pubkey
case x of
Nothing -> fail "couldn't get pubkey"
Just pk -> do
let prepKey =
XPubKey
3
fp
0
(Bi.decode $ BSL.fromStrict $ BS.take 32 pubkey)
pk
let childPubKey = derivePubPath ioCtx path prepKey
let x = xPubAddr ioCtx childPubKey
case x of
PubKeyAddress k ->
return $ Just $ TransparentReceiver P2PKH $ fromBinary k
ScriptAddress j ->
return $ Just $ TransparentReceiver P2SH $ fromBinary j
_anyOtherKind -> return Nothing
-- | Generate a transparent spending key
genTransparentSecretKey :: genTransparentSecretKey ::
Int -- ^ The index of the address to be created Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses
@ -172,27 +246,27 @@ decodeTransparentAddress taddress = do
-- | Encode an Exchange Addresss into HRF from TransparentReceiver -- | Encode an Exchange Addresss into HRF from TransparentReceiver
encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text
encodeExchangeAddress net tr = do encodeExchangeAddress net tr = do
case (tr_type tr) of case tr_type tr of
P2PKH -> do P2PKH -> do
case net of case net of
MainNet -> do MainNet -> do
let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr)) let vhash = encodeBech32m "tex" (toBytes (tr_bytes tr))
Just vhash Just vhash
TestNet -> do TestNet -> do
let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr)) let vhash = encodeBech32m "textest" (toBytes (tr_bytes tr))
Just vhash Just vhash
_ -> Nothing _any -> Nothing
-- | Decode an Exchange Address into a ExchangeAddress -- | Decode an Exchange Address into a ExchangeAddress
decodeExchangeAddress :: T.Text -> Maybe ExchangeAddress decodeExchangeAddress :: BS.ByteString -> Maybe ExchangeAddress
decodeExchangeAddress ex = do decodeExchangeAddress ex = do
if (T.length ex) > 1 if BS.length ex > 1
then do then do
let rawd = decodeBech32 (E.encodeUtf8 ex) let rawd = decodeBech32 ex
let tMain = BS.unpack (BC.pack "tex") let tMain = "tex"
let tTest = BS.unpack (BC.pack "textest") let tTest = "textest"
let tFail = BS.unpack (BC.pack "fail") let tFail = "fail"
let hr = BS.unpack (hrp rawd) let hr = hrp rawd
if hr /= tFail if hr /= tFail
then do then do
let transparentReceiver = bytes rawd let transparentReceiver = bytes rawd

View file

@ -38,6 +38,7 @@ import Data.Maybe (fromJust, fromMaybe)
import Data.Structured import Data.Structured
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import qualified Data.Vector as V
import Data.Word import Data.Word
import qualified GHC.Generics as GHC import qualified GHC.Generics as GHC
import qualified Generics.SOP as SOP import qualified Generics.SOP as SOP
@ -90,7 +91,7 @@ data ZcashNet
= MainNet = MainNet
| TestNet | TestNet
| RegTestNet | RegTestNet
deriving (Eq, Prelude.Show, Read) deriving (Eq, Prelude.Show, Read, GHC.Generic, ToJSON, FromJSON)
type AccountId = Int type AccountId = Int
@ -133,6 +134,18 @@ data Transaction = Transaction
, tx_orchardBundle :: !(Maybe OrchardBundle) , tx_orchardBundle :: !(Maybe OrchardBundle)
} deriving (Prelude.Show, Eq, Read) } deriving (Prelude.Show, Eq, Read)
instance ToJSON Transaction where
toJSON (Transaction t h c e tb sb ob) =
object
[ "txid" .= t
, "height" .= h
, "confirmations" .= c
, "expiry" .= e
, "transparent" .= tb
, "sapling" .= sb
, "orchard" .= ob
]
-- | The transparent portion of a Zcash transaction -- | The transparent portion of a Zcash transaction
data TransparentBundle = TransparentBundle data TransparentBundle = TransparentBundle
{ tb_vin :: ![H.TxIn] { tb_vin :: ![H.TxIn]
@ -140,6 +153,10 @@ data TransparentBundle = TransparentBundle
, tb_coinbase :: !Bool , tb_coinbase :: !Bool
} deriving (Eq, Prelude.Show, Read) } deriving (Eq, Prelude.Show, Read)
instance ToJSON TransparentBundle where
toJSON (TransparentBundle vin vout c) =
object ["vin" .= vin, "vout" .= vout, "coinbase" .= c]
-- | Read a raw transparent bundle into the Haskell type -- | Read a raw transparent bundle into the Haskell type
fromRawTBundle :: RawTBundle -> Maybe TransparentBundle fromRawTBundle :: RawTBundle -> Maybe TransparentBundle
fromRawTBundle rtb = fromRawTBundle rtb =
@ -239,7 +256,8 @@ instance FromJSON RpcError where
-- ** `zcashd` -- ** `zcashd`
-- | Type to represent response from the `zcashd` RPC `getblock` method -- | Type to represent response from the `zcashd` RPC `getblock` method
data BlockResponse = BlockResponse data BlockResponse = BlockResponse
{ bl_confirmations :: !Integer -- ^ Block confirmations { bl_hash :: !HexString
, bl_confirmations :: !Integer -- ^ Block confirmations
, bl_height :: !Integer -- ^ Block height , bl_height :: !Integer -- ^ Block height
, bl_time :: !Integer -- ^ Block time , bl_time :: !Integer -- ^ Block time
, bl_txs :: ![HexString] -- ^ List of transaction IDs in the block , bl_txs :: ![HexString] -- ^ List of transaction IDs in the block
@ -250,9 +268,20 @@ instance FromJSON BlockResponse where
withObject "BlockResponse" $ \obj -> do withObject "BlockResponse" $ \obj -> do
c <- obj .: "confirmations" c <- obj .: "confirmations"
h <- obj .: "height" h <- obj .: "height"
t <- obj .:? "time" t <- obj .: "time"
txs <- obj .: "tx" txs <- obj .: "tx"
pure $ BlockResponse c h (fromMaybe 0 t) txs hash <- obj .: "hash"
pure $ BlockResponse hash c h t txs
instance ToJSON BlockResponse where
toJSON (BlockResponse h c ht t txs) =
object
[ "hash" .= h
, "confirmations" .= c
, "height" .= ht
, "time" .= t
, "tx" .= txs
]
-- | Type to represent response from the `zcashd` RPC `getrawtransaction` -- | Type to represent response from the `zcashd` RPC `getrawtransaction`
data RawTxResponse = RawTxResponse data RawTxResponse = RawTxResponse
@ -324,6 +353,10 @@ data SaplingBundle = SaplingBundle
, sbSig :: !HexString , sbSig :: !HexString
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read) } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
instance ToJSON SaplingBundle where
toJSON (SaplingBundle s o v sig) =
object ["spends" .= s, "outputs" .= o, "value" .= v, "sig" .= sig]
fromRawSBundle :: RawSBundle -> Maybe SaplingBundle fromRawSBundle :: RawSBundle -> Maybe SaplingBundle
fromRawSBundle b = fromRawSBundle b =
if zsb_empty b if zsb_empty b
@ -355,6 +388,17 @@ data OrchardBundle = OrchardBundle
, obSig :: !HexString , obSig :: !HexString
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read) } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
instance ToJSON OrchardBundle where
toJSON (OrchardBundle a f v an p s) =
object
[ "actions" .= a
, "flags" .= f
, "value" .= v
, "anchor" .= an
, "proof" .= p
, "sig" .= s
]
fromRawOBundle :: RawOBundle -> Maybe OrchardBundle fromRawOBundle :: RawOBundle -> Maybe OrchardBundle
fromRawOBundle b = fromRawOBundle b =
if zob_empty b if zob_empty b
@ -377,6 +421,10 @@ data OrchardFlags = OrchardFlags
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardFlags deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardFlags
instance ToJSON OrchardFlags where
toJSON (OrchardFlags s o) =
Data.Aeson.Array $ V.fromList [Data.Aeson.Bool s, Data.Aeson.Bool o]
-- | Type for the response from the `zebrad` RPC method `getinfo` -- | Type for the response from the `zebrad` RPC method `getinfo`
data ZebraGetInfo = ZebraGetInfo data ZebraGetInfo = ZebraGetInfo
{ zgi_build :: !T.Text { zgi_build :: !T.Text
@ -501,6 +549,17 @@ data ShieldedSpend = ShieldedSpend
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedSpend deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedSpend
instance ToJSON ShieldedSpend where
toJSON (ShieldedSpend cv a n rk p au) =
object
[ "cv" .= cv
, "anchor" .= a
, "nullifier" .= n
, "rk" .= rk
, "proof" .= p
, "spendAuthSig" .= au
]
instance FromJSON ShieldedSpend where instance FromJSON ShieldedSpend where
parseJSON = parseJSON =
withObject "ShieldedSpend" $ \obj -> do withObject "ShieldedSpend" $ \obj -> do
@ -525,6 +584,17 @@ data ShieldedOutput = ShieldedOutput
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput
instance ToJSON ShieldedOutput where
toJSON (ShieldedOutput c cm e enc o p) =
object
[ "cv" .= c
, "cmu" .= cm
, "ephemeralKey" .= e
, "encCiphertext" .= enc
, "outCiphertext" .= o
, "proof" .= p
]
instance FromJSON ShieldedOutput where instance FromJSON ShieldedOutput where
parseJSON = parseJSON =
withObject "ShieldedOutput" $ \obj -> do withObject "ShieldedOutput" $ \obj -> do
@ -541,6 +611,30 @@ newtype SaplingCommitmentTree = SaplingCommitmentTree
{ sapTree :: HexString { sapTree :: HexString
} deriving (Eq, Prelude.Show, Read) } deriving (Eq, Prelude.Show, Read)
data SaplingRawTree = SaplingRawTree
{ srt_left :: !HexString
, srt_right :: !HexString
, srt_parents :: ![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 SaplingRawTree
data SaplingTree = SaplingTree
{ st_left :: !(Maybe HexString)
, st_right :: !(Maybe HexString)
, st_parents :: ![Maybe HexString]
} deriving (Eq, Prelude.Show, Read)
data SaplingFrontier = SaplingFrontier
{ sf_pos :: !Int64
, sf_leaf :: !HexString
, sf_ommers :: ![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 SaplingFrontier
-- | Type for a Sapling incremental witness -- | Type for a Sapling incremental witness
newtype SaplingWitness = SaplingWitness newtype SaplingWitness = SaplingWitness
{ sapWit :: HexString { sapWit :: HexString
@ -583,17 +677,43 @@ data RawUA = RawUA
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawUA deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawUA
-- | A type to handle user-entered addresses
data ValidAddress
= Unified !UnifiedAddress
| Sapling !SaplingAddress
| Transparent !TransparentAddress
| Exchange !ExchangeAddress
deriving stock (Eq, Prelude.Show)
-- | A type to handle user-entered viewing keys
data ValidVk
= FullVk !UnifiedFullViewingKey
| IncomingVk !UnifiedIncomingViewingKey
deriving stock (Eq, Prelude.Show, Read)
-- | Type to represent a Unified Full Viewing Key -- | Type to represent a Unified Full Viewing Key
data UnifiedFullViewingKey = UnifiedFullViewingKey data UnifiedFullViewingKey = UnifiedFullViewingKey
{ net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@. { 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) , 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) , 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) , 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 stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct UnifiedFullViewingKey deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct UnifiedFullViewingKey
-- | Type to represent a Unified Incoming Viewing Key
data UnifiedIncomingViewingKey = UnifiedIncomingViewingKey
{ i_net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@.
, i_o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, i_s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, i_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, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct
UnifiedIncomingViewingKey
-- | 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) -- | 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 data OrchardAction = OrchardAction
{ nf :: !HexString -- ^ The nullifier of the input note { nf :: !HexString -- ^ The nullifier of the input note
@ -609,6 +729,19 @@ data OrchardAction = OrchardAction
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardAction deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardAction
instance ToJSON OrchardAction where
toJSON (OrchardAction n r c e en o cv a) =
object
[ "nullifier" .= n
, "rk" .= r
, "cmx" .= c
, "ephemeralKey" .= e
, "encCiphertext" .= en
, "outCiphertext" .= o
, "cv" .= cv
, "spendAuthSig" .= a
]
instance FromJSON OrchardAction where instance FromJSON OrchardAction where
parseJSON = parseJSON =
withObject "OrchardAction" $ \obj -> do withObject "OrchardAction" $ \obj -> do
@ -622,11 +755,43 @@ instance FromJSON OrchardAction where
a <- obj .: "spendAuthSig" a <- obj .: "spendAuthSig"
pure $ OrchardAction n r c ephKey encText outText cval a pure $ OrchardAction n r c ephKey encText outText cval a
data MerklePath = MerklePath
{ mp_position :: !Int32
, mp_path :: ![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 MerklePath
-- | Type for a Orchard note commitment tree -- | Type for a Orchard note commitment tree
newtype OrchardCommitmentTree = OrchardCommitmentTree newtype OrchardCommitmentTree = OrchardCommitmentTree
{ orchTree :: HexString { orchTree :: HexString
} deriving (Eq, Prelude.Show, Read) } deriving (Eq, Prelude.Show, Read)
data OrchardRawTree = OrchardRawTree
{ ort_left :: !HexString
, ort_right :: !HexString
, ort_parents :: ![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 OrchardRawTree
data OrchardTree = OrchardTree
{ ot_left :: !(Maybe HexString)
, ot_right :: !(Maybe HexString)
, ot_parents :: ![Maybe HexString]
} deriving (Eq, Prelude.Show, Read)
data OrchardFrontier = OrchardFrontier
{ of_pos :: !Int64
, of_leaf :: !HexString
, of_ommers :: ![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 OrchardFrontier
-- | Type for a Sapling incremental witness -- | Type for a Sapling incremental witness
newtype OrchardWitness = OrchardWitness newtype OrchardWitness = OrchardWitness
{ orchWit :: HexString { orchWit :: HexString
@ -665,7 +830,7 @@ data TransparentTxSpend = TransparentTxSpend
data SaplingTxSpend = SaplingTxSpend data SaplingTxSpend = SaplingTxSpend
{ ss_sk :: !BS.ByteString { ss_sk :: !BS.ByteString
, ss_note :: !DecodedNote , ss_note :: !DecodedNote
, ss_iw :: !BS.ByteString , ss_iw :: !MerklePath
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
@ -674,7 +839,7 @@ data SaplingTxSpend = SaplingTxSpend
data OrchardTxSpend = OrchardTxSpend data OrchardTxSpend = OrchardTxSpend
{ ss_sk :: !BS.ByteString { ss_sk :: !BS.ByteString
, ss_note :: !DecodedNote , ss_note :: !DecodedNote
, ss_iw :: !BS.ByteString , ss_iw :: !MerklePath
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
@ -712,6 +877,7 @@ data TxError
| OrchardRecipient | OrchardRecipient
| SaplingBuilderNotAvailable | SaplingBuilderNotAvailable
| OrchardBuilderNotAvailable | OrchardBuilderNotAvailable
| PrivacyPolicyError !T.Text
| ZHError | ZHError
deriving (Eq, Prelude.Show, Read) deriving (Eq, Prelude.Show, Read)

View file

@ -123,53 +123,41 @@ readZebraTransaction hex =
rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex
createTransaction :: createTransaction ::
Maybe SaplingCommitmentTree -- ^ to obtain the Sapling anchor HexString -- ^ to obtain the Sapling anchor
-> Maybe OrchardCommitmentTree -- ^ to obtain the Orchard anchor -> HexString -- ^ to obtain the Orchard anchor
-> [TransparentTxSpend] -- ^ the list of transparent notes to spend -> [TransparentTxSpend] -- ^ the list of transparent notes to spend
-> [SaplingTxSpend] -- ^ the list of Sapling notes to spend -> [SaplingTxSpend] -- ^ the list of Sapling notes to spend
-> [OrchardTxSpend] -- ^ the list of Orchard notes to spend -> [OrchardTxSpend] -- ^ the list of Orchard notes to spend
-> [OutgoingNote] -- ^ the list of outgoing notes, including change notes -> [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 -> ZcashNet -- ^ the network to be used
-> Int -- ^ target block height -> Int -- ^ target block height
-> Bool -- ^ True to build, False to estimate fee -> Bool -- ^ True to build, False to estimate fee
-> Either TxError HexString -> IO (Either TxError HexString)
createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh build = createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do
processResult $! txResult txResult <-
where withBorshBufferOfInitSize 102400 $
processResult :: HexString -> Either TxError HexString
processResult input =
if BS.length (hexBytes input) > 1
then Right input
else case head (BS.unpack $ hexBytes input) 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
txResult =
withPureBorshVarBuffer $
rustWrapperCreateTx rustWrapperCreateTx
(case sapAnchor of (hexBytes sapAnchor)
Nothing -> "0" (hexBytes orchAnchor)
Just sA -> toBytes $ sapTree sA)
(case orchAnchor of
Nothing -> "0"
Just oA -> toBytes $ orchTree oA)
tSpend tSpend
sSpend sSpend
oSpend oSpend
outgoing outgoing
(sapSParams sParams)
(sapOParams oParams)
(znet == MainNet) (znet == MainNet)
(fromIntegral bh) (fromIntegral bh)
build build
if BS.length (hexBytes txResult) > 1
then pure $ Right txResult
else case head (BS.unpack $ hexBytes txResult) of
0 -> pure $ Left InsufficientFunds
1 -> pure $ Left ChangeRequired
2 -> pure $ Left Fee
3 -> pure $ Left Balance
4 -> pure $ Left TransparentBuild
5 -> pure $ Left SaplingBuild
6 -> pure $ Left OrchardBuild
7 -> pure $ Left OrchardSpend
8 -> pure $ Left OrchardRecipient
9 -> pure $ Left SaplingBuilderNotAvailable
10 -> pure $ Left OrchardBuilderNotAvailable
_ -> pure $ Left ZHError

View file

@ -22,10 +22,13 @@
import C.Zcash (rustWrapperUADecode) import C.Zcash (rustWrapperUADecode)
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Crypto.Secp256k1
import Data.Aeson import Data.Aeson
import qualified Data.Binary as Bi
import Data.Bool (Bool(True)) import Data.Bool (Bool(True))
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as BSL
import Data.Either (isRight) import Data.Either (isRight)
import Data.Foldable (sequenceA_) import Data.Foldable (sequenceA_)
import Data.HexString import Data.HexString
@ -40,20 +43,34 @@ import GHC.Float.RealFracMethods (properFractionDoubleInteger)
import Haskoin.Crypto.Hash (ripemd160) import Haskoin.Crypto.Hash (ripemd160)
import Haskoin.Crypto.Keys.Extended import Haskoin.Crypto.Keys.Extended
import Haskoin.Transaction.Common import Haskoin.Transaction.Common
import Network.HTTP.Simple (Response(..))
import Test.HUnit import Test.HUnit
import Test.Hspec import Test.Hspec
import Test.Hspec.QuickCheck import Test.Hspec.QuickCheck
import Test.QuickCheck import Test.QuickCheck
import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed) import ZcashHaskell.Keys
( deriveFullTransparentNode
, deriveUfvk
, deriveUivk
, generateWalletSeedPhrase
, getWalletSeed
)
import ZcashHaskell.Orchard import ZcashHaskell.Orchard
import ZcashHaskell.Sapling import ZcashHaskell.Sapling
( decodeSaplingAddress ( decodeSaplingAddress
, decodeSaplingOutput , decodeSaplingOutput
, decodeSaplingOutputEsk , decodeSaplingOutputEsk
, decodeSaplingOutputFvk
, decodeSaplingOutputIvk
, deriveSaplingFvk
, encodeSaplingAddress , encodeSaplingAddress
, genSaplingInternalAddress , genSaplingInternalAddress
, genSaplingInternalAddressFvk
, genSaplingPaymentAddress , genSaplingPaymentAddress
, genSaplingReceiverFvk
, genSaplingReceiverIvk
, genSaplingSpendingKey , genSaplingSpendingKey
, getSaplingFrontier
, getSaplingNotePosition , getSaplingNotePosition
, getSaplingWitness , getSaplingWitness
, getShieldedOutputs , getShieldedOutputs
@ -72,6 +89,7 @@ import ZcashHaskell.Types
, OrchardAction(..) , OrchardAction(..)
, OrchardBundle(..) , OrchardBundle(..)
, OrchardCommitmentTree(..) , OrchardCommitmentTree(..)
, OrchardFrontier(..)
, OrchardSpendingKey(..) , OrchardSpendingKey(..)
, OrchardWitness(..) , OrchardWitness(..)
, Phrase(..) , Phrase(..)
@ -82,9 +100,12 @@ import ZcashHaskell.Types
, RawTxOut(..) , RawTxOut(..)
, RawTxResponse(..) , RawTxResponse(..)
, RawZebraTx(..) , RawZebraTx(..)
, RpcError(..)
, RpcResponse(..)
, SaplingAddress(..) , SaplingAddress(..)
, SaplingBundle(..) , SaplingBundle(..)
, SaplingCommitmentTree(..) , SaplingCommitmentTree(..)
, SaplingFrontier(..)
, SaplingReceiver(..) , SaplingReceiver(..)
, SaplingSpendingKey(..) , SaplingSpendingKey(..)
, SaplingWitness(..) , SaplingWitness(..)
@ -98,6 +119,9 @@ import ZcashHaskell.Types
, TransparentType(..) , TransparentType(..)
, UnifiedAddress(..) , UnifiedAddress(..)
, UnifiedFullViewingKey(..) , UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidAddress(..)
, ValidVk(..)
, ZcashNet(..) , ZcashNet(..)
, ZebraTxResponse(..) , ZebraTxResponse(..)
, decodeHexText , decodeHexText
@ -892,34 +916,36 @@ main = do
Just t' -> do Just t' -> do
let tb = zt_tBundle t' let tb = zt_tBundle t'
show tb `shouldNotBe` "" show tb `shouldNotBe` ""
describe "Sapling commitment trees" $ do {-
let tree = -describe "Sapling commitment trees" $ do
SaplingCommitmentTree $ - let tree =
hexString - SaplingCommitmentTree $
"01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" - hexString
let cmu1 = - "01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
hexString - let cmu1 =
"45e47c5df6f5c5e48aa3526e977b2d1b57eda57214e36f06128008cb17b0125f" - hexString
let cmu2 = - "45e47c5df6f5c5e48aa3526e977b2d1b57eda57214e36f06128008cb17b0125f"
hexString - let cmu2 =
"426ef44b3b22e0eeda7e4d2b62bac63966572b224e50f97ee56c9490cde4910d" - hexString
let tree2 = - "426ef44b3b22e0eeda7e4d2b62bac63966572b224e50f97ee56c9490cde4910d"
hexString - let tree2 =
"01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" - hexString
it "Commitment tree is updated correctly" $ do - "01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
let t1 = updateSaplingCommitmentTree tree cmu1 - it "Commitment tree is updated correctly" $ do
t1 `shouldNotBe` Nothing - let t1 = updateSaplingCommitmentTree tree cmu1
it "Incremental witness is generated" $ do - t1 `shouldNotBe` Nothing
let t1 = updateSaplingCommitmentTree tree cmu1 - it "Incremental witness is generated" $ do
case t1 of - let t1 = updateSaplingCommitmentTree tree cmu1
Nothing -> assertFailure "Failed to append node to tree" - case t1 of
Just t -> getSaplingWitness t `shouldNotBe` Nothing - Nothing -> assertFailure "Failed to append node to tree"
it "Position of note is obtained" $ do - Just t -> getSaplingWitness t `shouldNotBe` Nothing
let p = - it "Position of note is obtained" $ do
getSaplingNotePosition <$> - let p =
(getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1) - getSaplingNotePosition <$>
p `shouldBe` Just 129405 - (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1)
describe "Orchard commitment trees" $ do - p `shouldBe` Just 129405
-}
{- describe "Orchard commitment trees" $ do
let tree = let tree =
OrchardCommitmentTree $ OrchardCommitmentTree $
hexString hexString
@ -939,7 +965,7 @@ main = do
let p = let p =
getOrchardNotePosition <$> getOrchardNotePosition <$>
(getOrchardWitness =<< updateOrchardCommitmentTree tree cmx) (getOrchardWitness =<< updateOrchardCommitmentTree tree cmx)
p `shouldBe` Just 39432 p `shouldBe` Just 39432 -}
describe "Extract Sapling Address - UA Valid" $ do describe "Extract Sapling Address - UA Valid" $ do
let sr = let sr =
getSaplingFromUA getSaplingFromUA
@ -1058,17 +1084,21 @@ main = do
(hexString (hexString
"97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e8892") "97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e8892")
it "Sap output 1" $ do it "Sap output 1" $ do
case getSaplingFrontier tree of
Nothing -> assertFailure "failed to read comm tree"
Just tree' -> do
let pos = let pos =
getSaplingNotePosition <$> sf_pos <$>
(getSaplingWitness =<<
updateSaplingCommitmentTree updateSaplingCommitmentTree
tree tree'
(fromText (fromText
"fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")) "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")
case pos of case pos of
Nothing -> assertFailure "couldn't get note position" Nothing -> assertFailure "couldn't get note position"
Just p -> do Just p -> do
let dn = decodeSaplingOutputEsk sk so1 TestNet External p let dn =
decodeSaplingOutputEsk sk so1 TestNet External $
fromIntegral p
dn `shouldBe` Nothing dn `shouldBe` Nothing
it "Sap output 2" $ do it "Sap output 2" $ do
case readZebraTransaction txHex2 of case readZebraTransaction txHex2 of
@ -1079,11 +1109,14 @@ main = do
Nothing -> assertFailure "Failed to get sapling bundle" Nothing -> assertFailure "Failed to get sapling bundle"
Just sB -> do Just sB -> do
let sOuts = sbOutputs sB let sOuts = sbOutputs sB
case getSaplingFrontier tree of
Nothing -> assertFailure "Failed to read tree"
Just tree' -> do
let pos = let pos =
getSaplingNotePosition <$> getSaplingNotePosition <$>
(getSaplingWitness =<< (getSaplingWitness =<<
updateSaplingCommitmentTree updateSaplingCommitmentTree
tree tree'
(fromText (fromText
"d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843"))
case pos of case pos of
@ -1109,13 +1142,15 @@ main = do
Nothing -> assertFailure "Failed to get sapling bundle" Nothing -> assertFailure "Failed to get sapling bundle"
Just sB -> do Just sB -> do
let sOuts = sbOutputs sB let sOuts = sbOutputs sB
case getSaplingFrontier tree of
Nothing -> assertFailure "failed to read comm tree"
Just tree' -> do
let pos = let pos =
getSaplingNotePosition <$> sf_pos <$>
(getSaplingWitness =<<
updateSaplingCommitmentTree updateSaplingCommitmentTree
tree tree'
(fromText (fromText
"d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")) "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")
case pos of case pos of
Nothing -> assertFailure "couldn't get note position" Nothing -> assertFailure "couldn't get note position"
Just p -> do Just p -> do
@ -1125,7 +1160,7 @@ main = do
(head . tail $ sOuts) (head . tail $ sOuts)
MainNet MainNet
External External
p (fromIntegral p)
dn `shouldNotBe` Nothing dn `shouldNotBe` Nothing
describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do
let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e"
@ -1143,43 +1178,218 @@ main = do
case exch of case exch of
Nothing -> assertFailure "Failed to encode Exchange address" Nothing -> assertFailure "Failed to encode Exchange address"
Just addr -> do Just addr -> do
let eadr = decodeExchangeAddress addr let eadr = decodeExchangeAddress (E.encodeUtf8 addr)
eadr `shouldNotBe` Nothing eadr `shouldNotBe` Nothing
describe "Witness updates" $ do describe "Generate Viewing Keys" $ do
it "Sapling" $ do let p =
let wit = Phrase
SaplingWitness $ "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest"
hexString let seed = getWalletSeed p
"01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e57ec972a9b9383dc9cb228980d2d7752bb2abebc4a604ca48c5457039d2e05b000301392bed8592185dde5ab7fc81aed75e98fcf041f1a3fda55ad0b0b139ba9380130001808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15" let oK = genOrchardSpendingKey (fromJust seed) MainNetCoin 0
let cmus = let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0
[ hexString it "Generate FVK" $ do
"958ccdc752f2f593f6c1c8e2d7201348cd896e54c6d3c92200bdbe8b859eac44" tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
, hexString FullVk fvk <- deriveUfvk MainNet oK sK (Just tK)
"e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447" net fvk `shouldBe` 1
, hexString it "Generate IVK" $ do
"0c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e18" tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
] IncomingVk ivk <- deriveUivk MainNet oK sK (Just tK)
updateSaplingWitness wit cmus `shouldBe` i_net ivk `shouldBe` 1
SaplingWitness describe "Use viewing keys" $ do
describe "FVKs" $ do
let fvk =
decodeUfvk
"uviewtest1jna46ql5qns5rlg99jgs6mhf0j9tk8zxvqsm472scgvmj0vs0rqv2kvdf626gftx7dgn2tltyf0s200gvjlsdvz5celpue9wxxw78txswqmayxc3pfrt5fs5frvr3ep0jrjg8euahqzc63yx9sy4z8lql4ev6q3asptl9rhsfzzrup2g5slwnlvy3dgft44jw3l08xtzypjmsrwxskgnp5s03xlc2kg5520a25pa6fdjxhzutam4wkwr6mh4zeq3qndpks8dk0y90y7gucgsp0j5k2xnhh90m3krk5glz4794dj93pf59h85dqms6337f85ccvpxhays94kvsj2hyjsltf52tygqs8y0vp2yf39drxl687the6xkp8nxkfffc3kqlkhw53t5plplde0vk9rwv340ys04gg48fs0pxfp35rvt2f2pvxjmgmln6lp5k2yzkm0r87k89p6xqv68a6uyfpsauswh9fsckfqey02pjedz5gs934qa"
let addrFromWallet =
"utest1act45pg36s7345emmp8lpxx560d4tjy2ef7vzzqvdqmt8d5gjxn7x4aarskvqzqccgyjrv5gc4mr6nf2elz6ylpu8hq3rgm0gj43jkl3elrugr49swlk0pv5edvcgnmhrqswkck6kvswvr89h2q0m6gtwktxzdkjp80c86nlp7x02kd0ttpsylsjddk488nmagtj85xclluug793h8n"
let cua =
"utest1zcjhrp39ux52ype4j6055fngdufet0h2d6hx564s48cgnzthm3xma3eca4x8sh5a89jk88nerngvy5uq9tyxgxq552k64rs93ell63f8sd2rurhn34lr6fjznw64uf473mehpn39gy2k0m86r3gpp5lcdh2senl7kwgl6hku03n0gpqvcfc7c48kjv9z49yp52etntgwkltsz3zvj0m"
let oa =
OrchardAction
(hexString (hexString
"01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447010c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e1803000121c06ee1f1584f79d50785797a694c742be2ded600367ab7d54f3ed49e3adf7201808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15") "33d6bf1d78f6414b725b5b43bfbb92b460d81cf04a352831d74bbc3c3aa86c2a")
it "Orchard" $ do
let wit =
OrchardWitness $
hexString
"016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e000200019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301"
let cmxs =
[ hexString
"712ba86615ff4447e8d7c7b59f3873f03c03a173438b8e4c8d416756ed4fae10"
, hexString
"c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e"
, hexString
"ac20b8170b008888c19fc6e16f5e30a5ef1653e5219d0cd0c9353c3aa8f79823"
]
updateOrchardWitness wit cmxs `shouldBe`
OrchardWitness
(hexString (hexString
"016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e0002010cfb50d8c877eb39e9c07082a032dd99d34be7c19fa7f30e9fecf5f14736240f019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301") "ce6d2090bf747ddce410ad967ea98e727120710f9814a6d77ebdd593c6f9660b")
(hexString
"567be1d3c5e2b8aba20fba30edd357810eb40647f1f5f9bb4e23691a9c174235")
(hexString
"b4ee13a27e1b7f2674c7b3924c2178b4c010c05750b78b65cb741b10476d1c24")
(hexString
"e8c37d9a2ea88f2ef266fed77753744da9afedf2530940b15ad882cc187a69d6116c5c34cdaf94e1f178ec50ef95d49b46ab6a7206c2a6b57d9d55f65cc7f5f57ccb91d71e827af5286897a4c8ca9c3fc2dad60208adbc8bd3d81eb5febf4c3c75e69b83627fdff730a946bb2e6e6703c4c676675e34ece1b073ce61490298a93503fa10f2b86fbd1274825c72e26b2340d1ed338d3b254803614b2fc9778155c988516d6ce1f334aeca076063c06588961c0d7dfa1bafd59cbca1782fcd863fce1f25abdf151c9718dce3708f8cede130d9e785ed4798aebfca8fec386f4a4d38a75c7b054c453676ad9ce7de32f4c4687f6932686f52e381bd41508a468ca684ea2755d56522a7b4138f58d0b10e1195a6fe27393ca382d0525a74acde5e0f6f2cb8ff3d74221f2a02fe2fd1874a2cf6f542bfa8f0107a21728249ca549bbba66a5422fd9aeaaeff09d1edfecb711c15290c9e8d031baf41fe47a5da3f9a614016f45886fb4d3ea2795802bf7785c1c9e3de15dc18300891f6abb9569851a7c4bf4cd054f8f593969b42c80968bf732ebaa90abf1a7614d1d325e8ed930161e0fbf82333c96cea455743c3e3e3cd1cdd754383a6f2364eacc162059e31885045eee7f2d60b068e78e29490a4df0f34e690b3bb17923669b828d245727bf11b0fe1e0fc4b97b62be37f7a7944db5b5b13221b0000f74c3cdcf9c246e60e77327c4f853f4037eeb2feb9f5d84961e4321c54c30d193866897872b31113a5f819dc2a264d2ef7b91edd16504ad75b6ee622dad79dadaadcd005215932d66232738a7dd8d2")
(hexString
"0b7be09c8f69e068d3277f345a0a4bf11057ff4c46e7657bb8a61a1c2b4c0d61e8e561131f935053f02cb46360f13179dc4647a243fabdd9ac59ba9ac2529a3b7af0ea5020d2b4c17466ebf309448491")
(hexString
"81939f32c4ca40c0a4c9334cffd68339e207afc322b533cb9cbb7b156758dc94")
(hexString
"44e3d8c4fbac16fc0edfd634b396b0a4822fc9a2cfdc8ec293a5c6ffc2001e96d2840c303463e3bc9add8c9263c56569e5b7ac14cf8d84316735f4b77493c136")
let so =
ShieldedOutput
(hexString
"dae2e33897847cc968d4719efc6ff6ea69c7ea8f8567883ba891c3d99b7fc7f3")
(hexString
"3d7c752b89af7dd63430d458d54c60643533ed6db9c57f55131eb7303daf7844")
(hexString
"8f7bd84a946a9068cb2030dd8d1f7446bba2ff22a5cce5ac97cc5c46f1c9bba9")
(hexString
"bc7b212e7c0a598a1237493a014688ee1232f631928e50583df6a1980adefb867dca6f97aa468f3c3a3882db8f359fc0d29205a808173645c22ba8a815e307fec633ffd01d34830e639e4dda548cd6e3ea32054206ac77e60ae09b0bf911066caf2be0bc8e3936edfa3e05c74f2e8a8bf5bbd8867eb83fd7fe24107612f6a0eaa586c6997b19fbe43c3b7d3f8cd5f40ae1a5431cf9d0d336516fb22ce4e295f77326ada3b453b9813a4a63981a52b9ba65af43cc6ec198b384417247b8f1bb15199fba5e90e435c946d03f97c62f7b40fbfef85e456dcb8fdaec94585a600cb40b2ed6c23c08eba5115719944528c156bea996b993d6e1730a17c6cf036deee43a18bd6afbd6e0f55f6c35ab5557df8e1356866514784ab4dd5ff158f0c5bfb6011adefae26c6dcee7472b7086680a72859e231829b7cf408683ced04171065e8740abde2b528c790fbec34d80e15ca869aaa100640c7ab167d7538a48174ea7caac91297455f85a41a03ab760565dd899b6b10b7034b221f4390373bd2dc1e3766629f7d5b606449ef07057995da06a63b6bb07aa117fd26e7154e55636ab8df21c3e3665574899eb1186d2106a55cc20b7eee2bc9b5970acddbac4a74f2f5599d7b42c21e65f340363748b208e61800ce6317e43c7e55757e1c9210bc716a93edb5ca2ed26ead4c14510086fb6752a8bfa089787360cca5c6b053744ad1487f7032ba027ba7758ce842a372b2f7dd418ecbd6353ca199629faa8848dfb7f228610aa2f76324eb7092ca05c6e2e7c7e9972cf33009c716ad43adc492a25fb8d1d67e72f")
(hexString
"ebe23f6ad66a16f9e5faece7139ae55aae93631b8b36ba2b5d21885f29b39dab224549afef0137c612d0c7a8ec597e98ea5a08334392079e5ea5c061cef2ebe296e71b6845e6bea09291bba505a76a03")
(hexString
"ad238bb5d88ccc438998bbcda831b499775f76c706f67bd9e56b8c3d6a6479afcf562a459c8db54003b641f1ac83d2dfa83baf8e04d6c2ab335eec290b3f581b0063cb088b53eb30b372ce80ac86a904448b9766d02624caa42fd9828ba4912e075b786fcbed921bc4cd25555031b2cd92509894d8ade8f4da92007b010b506c5664802e4e80c3363cd6e16c2a67abdcb4f3aef7638095091ab74cf79b6dcb919f7b38547e9576a7512f0f128504bb3461b8fee69ab7774320f56b2af13a6c9d")
it "Orchard external receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
genOrchardReceiverFvk 0 External (o_key k) `shouldNotBe` Nothing
it "Orchard internal receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
genOrchardReceiverFvk 0 Internal (o_key k) `shouldNotBe` Nothing
it "Decrypt Orchard action with FVK" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
a_nullifier <$>
decryptOrchardActionFvk k External oa `shouldBe`
Just (hexString "00")
it "Sapling external receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> genSaplingReceiverFvk 0 (s_key k) `shouldNotBe` Nothing
it "Sapling internal receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
genSaplingInternalAddressFvk (s_key k) `shouldNotBe` Nothing
it "Decode external Sapling with fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
a_nullifier <$>
decodeSaplingOutputFvk (s_key k) so External 234878 `shouldBe`
Just (hexString "00")
it "Transparent external receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
recv <- genTransparentReceiverFvk 0 External (t_key k)
recv `shouldNotBe` Nothing
it "Transparent internal receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
recv <- genTransparentReceiverFvk 0 Internal (t_key k)
recv `shouldNotBe` Nothing
it "Generate UA from FVK" $ do
let parsedUA = parseAddress addrFromWallet
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
case parsedUA of
Nothing -> assertFailure "Failed to parse UA"
Just va -> do
case va of
Unified ua -> do
let orec = genOrchardReceiverFvk 0 External (o_key k)
let srec = genSaplingReceiverFvk 0 (s_key k)
trec <- genTransparentReceiverFvk 0 External (t_key k)
let myUa = UnifiedAddress TestNet orec srec trec
myUa `shouldBe` ua
_any -> assertFailure "Address to UA"
it "Generate change UA from FVK" $ do
let parsedCua = parseAddress cua
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
case parsedCua of
Nothing -> assertFailure "Failed to parse UA"
Just va -> do
case va of
Unified ua -> do
let orec = genOrchardReceiverFvk 0 Internal (o_key k)
let srec = genSaplingInternalAddressFvk (s_key k)
trec <- genTransparentReceiverFvk 0 Internal (t_key k)
let myUa = UnifiedAddress TestNet orec srec trec
myUa `shouldBe` ua
_any -> assertFailure "Address to UA"
describe "IVKs" $ do
let ivk =
decodeUivk
"uivktest10yy38f5v5sz5hnne93flkrpfqr4geyfuaq97u4fe9zkrng20ppeqsu7a6fkfttn0phjj9kutlnn4nzvfls233xujcv3fw6ax9w8pqzsw792py78dl8p4cyhv3eyusu589382qwf4pwz3vs7n6pv4qj7j6apg5nvw4yl4u5g033tpr2a7jjzt3f3khjm09wj7er7myr8vy04595nj6pqcdp3ndp98ckg82y0w08d8wnx7wynrzhmqrxh9pf0m50tj332vjld08uz027xwyegakyzem745y462khv9xzlc4h5rxwsqfklvak3x26excpcjn54e3cd2zttxujnuzv3rrtrpuld9e2"
let addrFromWallet =
"utest1act45pg36s7345emmp8lpxx560d4tjy2ef7vzzqvdqmt8d5gjxn7x4aarskvqzqccgyjrv5gc4mr6nf2elz6ylpu8hq3rgm0gj43jkl3elrugr49swlk0pv5edvcgnmhrqswkck6kvswvr89h2q0m6gtwktxzdkjp80c86nlp7x02kd0ttpsylsjddk488nmagtj85xclluug793h8n"
let oa =
OrchardAction
(hexString
"33d6bf1d78f6414b725b5b43bfbb92b460d81cf04a352831d74bbc3c3aa86c2a")
(hexString
"ce6d2090bf747ddce410ad967ea98e727120710f9814a6d77ebdd593c6f9660b")
(hexString
"567be1d3c5e2b8aba20fba30edd357810eb40647f1f5f9bb4e23691a9c174235")
(hexString
"b4ee13a27e1b7f2674c7b3924c2178b4c010c05750b78b65cb741b10476d1c24")
(hexString
"e8c37d9a2ea88f2ef266fed77753744da9afedf2530940b15ad882cc187a69d6116c5c34cdaf94e1f178ec50ef95d49b46ab6a7206c2a6b57d9d55f65cc7f5f57ccb91d71e827af5286897a4c8ca9c3fc2dad60208adbc8bd3d81eb5febf4c3c75e69b83627fdff730a946bb2e6e6703c4c676675e34ece1b073ce61490298a93503fa10f2b86fbd1274825c72e26b2340d1ed338d3b254803614b2fc9778155c988516d6ce1f334aeca076063c06588961c0d7dfa1bafd59cbca1782fcd863fce1f25abdf151c9718dce3708f8cede130d9e785ed4798aebfca8fec386f4a4d38a75c7b054c453676ad9ce7de32f4c4687f6932686f52e381bd41508a468ca684ea2755d56522a7b4138f58d0b10e1195a6fe27393ca382d0525a74acde5e0f6f2cb8ff3d74221f2a02fe2fd1874a2cf6f542bfa8f0107a21728249ca549bbba66a5422fd9aeaaeff09d1edfecb711c15290c9e8d031baf41fe47a5da3f9a614016f45886fb4d3ea2795802bf7785c1c9e3de15dc18300891f6abb9569851a7c4bf4cd054f8f593969b42c80968bf732ebaa90abf1a7614d1d325e8ed930161e0fbf82333c96cea455743c3e3e3cd1cdd754383a6f2364eacc162059e31885045eee7f2d60b068e78e29490a4df0f34e690b3bb17923669b828d245727bf11b0fe1e0fc4b97b62be37f7a7944db5b5b13221b0000f74c3cdcf9c246e60e77327c4f853f4037eeb2feb9f5d84961e4321c54c30d193866897872b31113a5f819dc2a264d2ef7b91edd16504ad75b6ee622dad79dadaadcd005215932d66232738a7dd8d2")
(hexString
"0b7be09c8f69e068d3277f345a0a4bf11057ff4c46e7657bb8a61a1c2b4c0d61e8e561131f935053f02cb46360f13179dc4647a243fabdd9ac59ba9ac2529a3b7af0ea5020d2b4c17466ebf309448491")
(hexString
"81939f32c4ca40c0a4c9334cffd68339e207afc322b533cb9cbb7b156758dc94")
(hexString
"44e3d8c4fbac16fc0edfd634b396b0a4822fc9a2cfdc8ec293a5c6ffc2001e96d2840c303463e3bc9add8c9263c56569e5b7ac14cf8d84316735f4b77493c136")
let so =
ShieldedOutput
(hexString
"dae2e33897847cc968d4719efc6ff6ea69c7ea8f8567883ba891c3d99b7fc7f3")
(hexString
"3d7c752b89af7dd63430d458d54c60643533ed6db9c57f55131eb7303daf7844")
(hexString
"8f7bd84a946a9068cb2030dd8d1f7446bba2ff22a5cce5ac97cc5c46f1c9bba9")
(hexString
"bc7b212e7c0a598a1237493a014688ee1232f631928e50583df6a1980adefb867dca6f97aa468f3c3a3882db8f359fc0d29205a808173645c22ba8a815e307fec633ffd01d34830e639e4dda548cd6e3ea32054206ac77e60ae09b0bf911066caf2be0bc8e3936edfa3e05c74f2e8a8bf5bbd8867eb83fd7fe24107612f6a0eaa586c6997b19fbe43c3b7d3f8cd5f40ae1a5431cf9d0d336516fb22ce4e295f77326ada3b453b9813a4a63981a52b9ba65af43cc6ec198b384417247b8f1bb15199fba5e90e435c946d03f97c62f7b40fbfef85e456dcb8fdaec94585a600cb40b2ed6c23c08eba5115719944528c156bea996b993d6e1730a17c6cf036deee43a18bd6afbd6e0f55f6c35ab5557df8e1356866514784ab4dd5ff158f0c5bfb6011adefae26c6dcee7472b7086680a72859e231829b7cf408683ced04171065e8740abde2b528c790fbec34d80e15ca869aaa100640c7ab167d7538a48174ea7caac91297455f85a41a03ab760565dd899b6b10b7034b221f4390373bd2dc1e3766629f7d5b606449ef07057995da06a63b6bb07aa117fd26e7154e55636ab8df21c3e3665574899eb1186d2106a55cc20b7eee2bc9b5970acddbac4a74f2f5599d7b42c21e65f340363748b208e61800ce6317e43c7e55757e1c9210bc716a93edb5ca2ed26ead4c14510086fb6752a8bfa089787360cca5c6b053744ad1487f7032ba027ba7758ce842a372b2f7dd418ecbd6353ca199629faa8848dfb7f228610aa2f76324eb7092ca05c6e2e7c7e9972cf33009c716ad43adc492a25fb8d1d67e72f")
(hexString
"ebe23f6ad66a16f9e5faece7139ae55aae93631b8b36ba2b5d21885f29b39dab224549afef0137c612d0c7a8ec597e98ea5a08334392079e5ea5c061cef2ebe296e71b6845e6bea09291bba505a76a03")
(hexString
"ad238bb5d88ccc438998bbcda831b499775f76c706f67bd9e56b8c3d6a6479afcf562a459c8db54003b641f1ac83d2dfa83baf8e04d6c2ab335eec290b3f581b0063cb088b53eb30b372ce80ac86a904448b9766d02624caa42fd9828ba4912e075b786fcbed921bc4cd25555031b2cd92509894d8ade8f4da92007b010b506c5664802e4e80c3363cd6e16c2a67abdcb4f3aef7638095091ab74cf79b6dcb919f7b38547e9576a7512f0f128504bb3461b8fee69ab7774320f56b2af13a6c9d")
it "Orchard external receiver from ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> genOrchardReceiverIvk 0 (i_o_key k) `shouldNotBe` Nothing
it "Decrypt Orchard action with IVK" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> decryptOrchardActionIvk k oa `shouldNotBe` Nothing
it "Sapling external receiver from ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> genSaplingReceiverIvk 0 (i_s_key k) `shouldNotBe` Nothing
it "Decode external Sapling with ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
decodeSaplingOutputIvk (i_s_key k) so `shouldNotBe` Nothing
it "Transparent external receiver from ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
recv <- genTransparentReceiverIvk 0 (i_t_key k)
recv `shouldNotBe` Nothing
it "Generate UA from IVK" $ do
let parsedUA = parseAddress addrFromWallet
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
case parsedUA of
Nothing -> assertFailure "Failed to parse UA"
Just va -> do
case va of
Unified ua -> do
let orec = genOrchardReceiverIvk 0 (i_o_key k)
let srec = genSaplingReceiverIvk 0 (i_s_key k)
trec <- genTransparentReceiverIvk 0 (i_t_key k)
let myUa = UnifiedAddress TestNet orec srec trec
myUa `shouldBe` ua
_any -> assertFailure "Address to UA"
-- | Properties -- | Properties
prop_PhraseLength :: Property prop_PhraseLength :: Property

View file

@ -5,7 +5,7 @@ cabal-version: 3.0
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
name: zcash-haskell name: zcash-haskell
version: 0.6.2.1 version: 0.8.1.0
synopsis: Utilities to interact with the Zcash blockchain synopsis: Utilities to interact with the Zcash blockchain
description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme> description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme>
category: Blockchain category: Blockchain
@ -59,6 +59,7 @@ library
, text , text
, haskoin-core , haskoin-core
, secp256k1-haskell >= 1.1 , secp256k1-haskell >= 1.1
, vector
, utf8-string , utf8-string
build-tool-depends: build-tool-depends:
c2hs:c2hs c2hs:c2hs
@ -85,5 +86,6 @@ test-suite zcash-haskell-test
, binary , binary
, cryptonite , cryptonite
, secp256k1-haskell , secp256k1-haskell
, http-conduit
pkgconfig-depends: rustzcash_wrapper pkgconfig-depends: rustzcash_wrapper
default-language: Haskell2010 default-language: Haskell2010