diff --git a/.gitignore b/.gitignore
index bb46a99..f9cbc7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
*~
librustzcash-wrapper/target/
stack.yaml.lock
+dist-newstyle/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b07525..cf7b6d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,184 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.6.1.1]
+
+### Added
+
+- Type for transaction creation errors
+- Types for Sapling circuit parameters
+- Function to create transaction
+- Function to derive distinct transparent spending keys
+
+### Changed
+
+- Add `Read` instance for `Rseed`
+
+## [0.6.1.0]
+
+### Added
+
+- Function to create a raw transaction
+- New types for transaction creation:
+ - `Rseed`
+ - `TransparentTxSpend`
+ - `SaplingTxSpend`
+ - `OrchardTxSpend`
+ - `OutgoingNote`
+- Rust crates:
+ - `secp256k1`
+ - `jubjub`
+ - `rand_core`
+
+
+### Changed
+
+- `DecodedNote` type now includes a field for `rho` and one for `rseed`
+
+## [0.6.0.0]
+
+### Added
+
+- Rust crates:
+ - `sapling-crypto` 0.1.3
+
+### Changed
+
+- Modified handling of `ShieldedOutput`s based on new Rust crates
+- Upgraded Rust crates:
+ - `orchard` to 0.7.1
+ - `zcash_primitives` to 0.14.0
+ - `zcash_client_backend` to 0.11.1
+
+
+## [0.5.5.4]
+
+### Added
+
+- Functions to update Sapling witnesses.
+- Functions to update Orchard witnesses.
+
+## [0.5.5.3]
+
+### Added
+
+- Added function to generate an `ExchangeAddress` in Human Readable Format Using a `TransparentAddress` in HRF
+- `encodeExchangeAddress` a function to create a `ExchangeAddress` in HRF
+- `decodeExchangeAddress` a function to obtain a `TransparentAddress` object from an `ExchangeAddress` in HRF
+- Added new type `ExchangeAddress`
+
+### Fixed
+
+- Orchard note nullifier calculation
+- Sapling spend field parsing
+
+## [0.5.5.2]
+
+### Added
+
+- Added function to encode a Sappling Address in Human Readable Format Using a SaplingReceiver
+ `encodeSaplingAddress` a zcash sapling address is returned or Nothing if the function fails
+- Added decoding and encoding test
+
+## [0.5.5.1]
+
+### Added
+
+- Added unction to decode a Sappling Address in Human Readable Format
+ `decodeSaplingAddress` returns 43 byte array containing
+- Added a new Datatype `SaplingAddress`
+- Added a new FFI function `rust_wrapper_decode_sapling_address` to haskell-rust interface
+
+### Changed
+
+- `TransparentAddress` type refactored
+- `TransparentReceiver` added to replace old `TransparentAddress`
+- `sha256` Function moved outside of `encodeTransparentReceiver`
+
+## [0.5.5.0]
+
+### Added
+
+- Added unction to decode Transparent Address in Human Readable Format
+
+### Changed
+
+- `TransparentAddress` type refactored
+- `TransparentReceiver` added to replace old `TransparentAddress`
+- `sha256` Function moved outside of `encodeTransparentReceiver`
+## [0.5.4.1]
+
+### Added
+
+- Functions to handle Sapling commitment trees, incremental witnesses and note positions
+
+## [0.5.4.0]
+
+### Added
+- Function to decode Orchard actions with a spending key
+- Functions for Bech32 encoding
+- Function to encode a Sapling address
+
+## [0.5.3.0]
+
+### Added
+
+- Function to decode Sapling outputs with a spending key
+
+### Fixed
+
+- Parsing of `TxIn` for FFI
+
+## [0.5.2.0]
+
+### Added
+
+- Functionality to parse transparent bundles from Zebra
+- Types for transparent `TxIn`, `TxOut`, `OutPoint`
+
+## [0.5.1.0]
+
+### Added
+
+- Functionality to capture Sapling Spends
+
+### Changed
+
+- Modified the `makeZebraCall` function to handle errors explicitly
+- Modified the RPC response to handle missing `result` field
+
+## [0.5.0.1]
+
+### Added
+
+- Function to encode a human-readable transparent address
+- Function to generate a seed phrase
+- Implementations of `Read` for types
+- Function to make RPC calls to `zebrad`
+- Function to encode unified addresses from receivers
+- Function to generate an Orchard spending key
+- Constants for Zcash protocol
+- Types for Spending Keys and Receivers for Sapling and Orchard
+- Function to generate an Orchard receiver
+- Function to generate a Sapling receiver
+- Function to generate a Transparent receiver
+
+### Changed
+
+- Update installation to `cabal`
+- Updated Rust crates:
+ - `bech32` to 0.11
+ - `orchard` to 0.7.0
+ - `zcash_note_encryption` to 0.4.0
+ - `zcash_primitives` to 0.13.0
+ - `zcash_client_backend` to 0.10.0
+ - `zip32` to 0.1.0
+- Changed the `UnifiedAddress` to allow for optional shielded receivers
+
+### Removed
+
+- `Makefile`
+
## [0.3.0]
### Added
diff --git a/COPYING.LESSER.md b/COPYING.LESSER.md
deleted file mode 100644
index 6fb6a01..0000000
--- a/COPYING.LESSER.md
+++ /dev/null
@@ -1,157 +0,0 @@
-# GNU LESSER GENERAL PUBLIC LICENSE
-
-Version 3, 29 June 2007
-
-Copyright (C) 2007 Free Software Foundation, Inc.
-
-
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
-
-This version of the GNU Lesser General Public License incorporates the
-terms and conditions of version 3 of the GNU General Public License,
-supplemented by the additional permissions listed below.
-
-## 0. Additional Definitions.
-
-As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the
-GNU General Public License.
-
-"The Library" refers to a covered work governed by this License, other
-than an Application or a Combined Work as defined below.
-
-An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
-A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
-The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
-The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
-## 1. Exception to Section 3 of the GNU GPL.
-
-You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
-## 2. Conveying Modified Versions.
-
-If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
-- a) under this License, provided that you make a good faith effort
- to ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
-## 3. Object Code Incorporating Material from Library Header Files.
-
-The object code form of an Application may incorporate material from a
-header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
-- a) Give prominent notice with each copy of the object code that
- the Library is used in it and that the Library and its use are
- covered by this License.
-- b) Accompany the object code with a copy of the GNU GPL and this
- license document.
-
-## 4. Combined Works.
-
-You may convey a Combined Work under terms of your choice that, taken
-together, effectively do not restrict modification of the portions of
-the Library contained in the Combined Work and reverse engineering for
-debugging such modifications, if you also do each of the following:
-
-- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-- b) Accompany the Combined Work with a copy of the GNU GPL and this
- license document.
-- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-- d) Do one of the following:
- - 0) Convey the Minimal Corresponding Source under the terms of
- this License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
- - 1) Use a suitable shared library mechanism for linking with
- the Library. A suitable mechanism is one that (a) uses at run
- time a copy of the Library already present on the user's
- computer system, and (b) will operate properly with a modified
- version of the Library that is interface-compatible with the
- Linked Version.
-- e) Provide Installation Information, but only if you would
- otherwise be required to provide such information under section 6
- of the GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the Application
- with a modified version of the Linked Version. (If you use option
- 4d0, the Installation Information must accompany the Minimal
- Corresponding Source and Corresponding Application Code. If you
- use option 4d1, you must provide the Installation Information in
- the manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.)
-
-## 5. Combined Libraries.
-
-You may place library facilities that are a work based on the Library
-side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
-- a) Accompany the combined library with a copy of the same work
- based on the Library, uncombined with any other library
- facilities, conveyed under the terms of this License.
-- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
-## 6. Revised Versions of the GNU Lesser General Public License.
-
-The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library
-as you received it specifies that a certain numbered version of the
-GNU Lesser General Public License "or any later version" applies to
-it, you have the option of following the terms and conditions either
-of that published version or of any later version published by the
-Free Software Foundation. If the Library as you received it does not
-specify a version number of the GNU Lesser General Public License, you
-may choose any version of the GNU Lesser General Public License ever
-published by the Free Software Foundation.
-
-If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/COPYING.md b/COPYING.md
deleted file mode 100644
index 496acdb..0000000
--- a/COPYING.md
+++ /dev/null
@@ -1,675 +0,0 @@
-# GNU GENERAL PUBLIC LICENSE
-
-Version 3, 29 June 2007
-
-Copyright (C) 2007 Free Software Foundation, Inc.
-
-
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
-
-## Preamble
-
-The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom
-to share and change all versions of a program--to make sure it remains
-free software for all its users. We, the Free Software Foundation, use
-the GNU General Public License for most of our software; it applies
-also to any other work released this way by its authors. You can apply
-it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you
-have certain responsibilities if you distribute copies of the
-software, or if you modify it: responsibilities to respect the freedom
-of others.
-
-For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the
-manufacturer can do so. This is fundamentally incompatible with the
-aim of protecting users' freedom to change the software. The
-systematic pattern of such abuse occurs in the area of products for
-individuals to use, which is precisely where it is most unacceptable.
-Therefore, we have designed this version of the GPL to prohibit the
-practice for those products. If such problems arise substantially in
-other domains, we stand ready to extend this provision to those
-domains in future versions of the GPL, as needed to protect the
-freedom of users.
-
-Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish
-to avoid the special danger that patents applied to a free program
-could make it effectively proprietary. To prevent this, the GPL
-assures that patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and
-modification follow.
-
-## TERMS AND CONDITIONS
-
-### 0. Definitions.
-
-"This License" refers to version 3 of the GNU General Public License.
-
-"Copyright" also means copyright-like laws that apply to other kinds
-of works, such as semiconductor masks.
-
-"The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
-To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of
-an exact copy. The resulting work is called a "modified version" of
-the earlier work or a work "based on" the earlier work.
-
-A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user
-through a computer network, with no transfer of a copy, is not
-conveying.
-
-An interactive user interface displays "Appropriate Legal Notices" to
-the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-### 1. Source Code.
-
-The "source code" for a work means the preferred form of the work for
-making modifications to it. "Object code" means any non-source form of
-a work.
-
-A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can
-regenerate automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same
-work.
-
-### 2. Basic Permissions.
-
-All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey,
-without conditions so long as your license otherwise remains in force.
-You may convey covered works to others for the sole purpose of having
-them make modifications exclusively for you, or provide you with
-facilities for running those works, provided that you comply with the
-terms of this License in conveying all material for which you do not
-control copyright. Those thus making or running the covered works for
-you must do so exclusively on your behalf, under your direction and
-control, on terms that prohibit them from making any copies of your
-copyrighted material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under the
-conditions stated below. Sublicensing is not allowed; section 10 makes
-it unnecessary.
-
-### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such
-circumvention is effected by exercising rights under this License with
-respect to the covered work, and you disclaim any intention to limit
-operation or modification of the work as a means of enforcing, against
-the work's users, your or third parties' legal rights to forbid
-circumvention of technological measures.
-
-### 4. Conveying Verbatim Copies.
-
-You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-### 5. Conveying Modified Source Versions.
-
-You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these
-conditions:
-
-- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under
- section 7. This requirement modifies the requirement in section 4
- to "keep intact all notices".
-- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
-A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-### 6. Conveying Non-Source Forms.
-
-You may convey a covered work in object code form under the terms of
-sections 4 and 5, provided that you also convey the machine-readable
-Corresponding Source under the terms of this License, in one of these
-ways:
-
-- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the Corresponding
- Source from a network server at no charge.
-- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-- e) Convey the object code using peer-to-peer transmission,
- provided you inform other peers where the object code and
- Corresponding Source of the work are being offered to the general
- public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal,
-family, or household purposes, or (2) anything designed or sold for
-incorporation into a dwelling. In determining whether a product is a
-consumer product, doubtful cases shall be resolved in favor of
-coverage. For a particular product received by a particular user,
-"normally used" refers to a typical or common use of that class of
-product, regardless of the status of the particular user or of the way
-in which the particular user actually uses, or expects or is expected
-to use, the product. A product is a consumer product regardless of
-whether the product has substantial commercial, industrial or
-non-consumer uses, unless such uses represent the only significant
-mode of use of the product.
-
-"Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to
-install and execute modified versions of a covered work in that User
-Product from a modified version of its Corresponding Source. The
-information must suffice to ensure that the continued functioning of
-the modified object code is in no case prevented or interfered with
-solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or
-updates for a work that has been modified or installed by the
-recipient, or for the User Product in which it has been modified or
-installed. Access to a network may be denied when the modification
-itself materially and adversely affects the operation of the network
-or violates the rules and protocols for communication across the
-network.
-
-Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-### 7. Additional Terms.
-
-"Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders
-of that material) supplement the terms of this License with terms:
-
-- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-- c) Prohibiting misrepresentation of the origin of that material,
- or requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-- d) Limiting the use for publicity purposes of names of licensors
- or authors of the material; or
-- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions
- of it) with contractual assumptions of liability to the recipient,
- for any liability that these contractual assumptions directly
- impose on those licensors and authors.
-
-All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions; the
-above requirements apply either way.
-
-### 8. Termination.
-
-You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-However, if you cease all violation of this License, then your license
-from a particular copyright holder is reinstated (a) provisionally,
-unless and until the copyright holder explicitly and finally
-terminates your license, and (b) permanently, if the copyright holder
-fails to notify you of the violation by some reasonable means prior to
-60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-### 9. Acceptance Not Required for Having Copies.
-
-You are not required to accept this License in order to receive or run
-a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-### 10. Automatic Licensing of Downstream Recipients.
-
-Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
-An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-### 11. Patents.
-
-A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
-A contributor's "essential patent claims" are all patent claims owned
-or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-A patent license is "discriminatory" if it does not include within the
-scope of its coverage, prohibits the exercise of, or is conditioned on
-the non-exercise of one or more of the rights that are specifically
-granted under this License. You may not convey a covered work if you
-are a party to an arrangement with a third party that is in the
-business of distributing software, under which you make payment to the
-third party based on the extent of your activity of conveying the
-work, and under which the third party grants, to any of the parties
-who would receive the covered work from you, a discriminatory patent
-license (a) in connection with copies of the covered work conveyed by
-you (or copies made from those copies), or (b) primarily for and in
-connection with specific products or compilations that contain the
-covered work, unless you entered into that arrangement, or that patent
-license was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-### 12. No Surrender of Others' Freedom.
-
-If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under
-this License and any other pertinent obligations, then as a
-consequence you may not convey it at all. For example, if you agree to
-terms that obligate you to collect a royalty for further conveying
-from those to whom you convey the Program, the only way you could
-satisfy both those terms and this License would be to refrain entirely
-from conveying the Program.
-
-### 13. Use with the GNU Affero General Public License.
-
-Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-### 14. Revised Versions of this License.
-
-The Free Software Foundation may publish revised and/or new versions
-of the GNU General Public License from time to time. Such new versions
-will be similar in spirit to the present version, but may differ in
-detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies that a certain numbered version of the GNU General Public
-License "or any later version" applies to it, you have the option of
-following the terms and conditions either of that numbered version or
-of any later version published by the Free Software Foundation. If the
-Program does not specify a version number of the GNU General Public
-License, you may choose any version ever published by the Free
-Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions
-of the GNU General Public License can be used, that proxy's public
-statement of acceptance of a version permanently authorizes you to
-choose that version for the Program.
-
-Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-### 15. Disclaimer of Warranty.
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
-WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
-PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
-DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
-CORRECTION.
-
-### 16. Limitation of Liability.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
-CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
-ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
-NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
-LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
-TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
-PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-### 17. Interpretation of Sections 15 and 16.
-
-If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
-## How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these
-terms.
-
-To do so, attach the following notices to the program. It is safest to
-attach them to the start of each source file to most effectively state
-the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper
-mail.
-
-If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands \`show w' and \`show c' should show the
-appropriate parts of the General Public License. Of course, your
-program's commands might be different; for a GUI interface, you would
-use an "about box".
-
-You should also get your employer (if you work as a programmer) or
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. For more information on this, and how to apply and follow
-the GNU GPL, see .
-
-The GNU General Public License does not permit incorporating your
-program into proprietary programs. If your program is a subroutine
-library, you may consider it more useful to permit linking proprietary
-applications with the library. If this is what you want to do, use the
-GNU Lesser General Public License instead of this License. But first,
-please read .
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..03b6d4f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2022-2024 Vergara Technologies LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 5943f32..0000000
--- a/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2022-2024 Vergara Technologies LLC
-#
-# This file is part of Zcash-Haskell.
-#
-# Zcash-Haskell is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option) any
-# later version.
-#
-# Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License along with
-# Zcash-Haskell. If not, see .
-
-rustlib := librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug
-
-.PHONY: all
-
-all: haskell
-
-test: test/Spec.hs haskell
- stack test
-
-haskell: src/ZcashHaskell/Orchard.hs src/ZcashHaskell/Sapling.hs src/ZcashHaskell/Types.hs src/ZcashHaskell/Utils.hs src/C/Zcash.chs package.yaml stack.yaml $(rustlib)/rustzcash_wrapper.h $(rustlib)/librustzcash_wrapper.a $(rustlib)/librustzcash_wrapper.so $(rustlib)/rustzcash_wrapper-uninstalled.pc
- stack build
-
-$(rustlib)/rustzcash_wrapper.h: librustzcash-wrapper/src/lib.rs librustzcash-wrapper/Cargo.toml
- cd librustzcash-wrapper && cargo +nightly cbuild
diff --git a/README.md b/README.md
index 8ffa015..a6f7cc1 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,10 @@ A Haskell library to interact with the Zcash blockchain.
```
./configure
```
-- Compile the Rust and Haskell portions: `make`
+- Compile the Rust and Haskell portions:
+ ```
+ cabal build
+ ```
## Use
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
index 0000000..968fd0c
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,137 @@
+import Control.Exception (throw)
+import Control.Monad (forM_, when)
+import Data.Maybe (fromMaybe)
+import Distribution.PackageDescription
+import Distribution.Simple
+import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..), localPkgDescr)
+import Distribution.Simple.PreProcess
+import Distribution.Simple.Program.Find
+ ( defaultProgramSearchPath
+ , findProgramOnSearchPath
+ )
+import Distribution.Simple.Setup
+import Distribution.Simple.Utils
+ ( IODataMode(IODataModeBinary)
+ , maybeExit
+ , rawSystemStdInOut
+ )
+import Distribution.Verbosity (Verbosity)
+import qualified Distribution.Verbosity as Verbosity
+import GHC.Generics
+import System.Directory
+ ( XdgDirectory(..)
+ , copyFile
+ , createDirectory
+ , createDirectoryIfMissing
+ , doesDirectoryExist
+ , doesFileExist
+ , getCurrentDirectory
+ , getDirectoryContents
+ , getHomeDirectory
+ , getXdgDirectory
+ )
+import System.Environment
+import System.FilePath ((>))
+import Text.Regex
+import Text.Regex.Base
+
+main :: IO ()
+main = defaultMainWithHooks hooks
+ where
+ hooks =
+ simpleUserHooks
+ { preConf =
+ \_ flags -> do
+ rsMake (fromFlag $ configVerbosity flags)
+ pure emptyHookedBuildInfo
+ , hookedPreProcessors = knownSuffixHandlers
+ , confHook = \a flags -> confHook simpleUserHooks a flags >>= rsAddDirs
+ , postClean = \_ flags _ _ -> rsClean (fromFlag $ cleanVerbosity flags)
+ }
+
+rsFolder :: FilePath
+rsFolder = "librustzcash-wrapper"
+
+execCargo :: Verbosity -> String -> [String] -> IO ()
+execCargo verbosity command args = do
+ cargoPath <-
+ findProgramOnSearchPath Verbosity.normal defaultProgramSearchPath "cargo"
+ dir <- getCurrentDirectory
+ let cargoExec =
+ case cargoPath of
+ Just (p, _) -> p
+ Nothing -> "cargo"
+ cargoArgs = command : args
+ workingDir = Just (dir > rsFolder)
+ thirdComponent (_, _, c) = c
+ maybeExit . fmap thirdComponent $
+ rawSystemStdInOut
+ verbosity
+ cargoExec
+ cargoArgs
+ workingDir
+ Nothing
+ Nothing
+ IODataModeBinary
+
+rsMake :: Verbosity -> IO ()
+rsMake verbosity = do
+ execCargo verbosity "cbuild" []
+
+rsAddDirs :: LocalBuildInfo -> IO LocalBuildInfo
+rsAddDirs lbi' = do
+ localData <- getXdgDirectory XdgData "zcash-haskell"
+ createDirectoryIfMissing True localData
+ dir <- getCurrentDirectory
+ let rustIncludeDir =
+ dir > rsFolder > "target/x86_64-unknown-linux-gnu/debug"
+ rustLibDir = dir > rsFolder > "target/x86_64-unknown-linux-gnu/debug"
+ updateLbi lbi = lbi {localPkgDescr = updatePkgDescr (localPkgDescr lbi)}
+ updatePkgDescr pkgDescr =
+ pkgDescr {library = updateLib <$> library pkgDescr}
+ updateLib lib = lib {libBuildInfo = updateLibBi (libBuildInfo lib)}
+ updateLibBi libBuild =
+ libBuild
+ { includeDirs = rustIncludeDir : includeDirs libBuild
+ , extraLibDirs = rustLibDir : extraLibDirs libBuild
+ }
+ copyDir rustLibDir localData
+ pure $ updateLbi lbi'
+
+rsClean :: Verbosity -> IO ()
+rsClean verbosity = execCargo verbosity "clean" []
+
+cabalFlag :: FlagName -> ConfigFlags -> Bool
+cabalFlag name =
+ fromMaybe False . lookupFlagAssignment name . configConfigurationsFlags
+
+unlessFlagM :: FlagName -> ConfigFlags -> IO () -> IO ()
+unlessFlagM name flags action
+ | cabalFlag name flags = pure ()
+ | otherwise = action
+
+applyUnlessM :: FlagName -> ConfigFlags -> (a -> IO a) -> a -> IO a
+applyUnlessM name flags apply a
+ | cabalFlag name flags = pure a
+ | otherwise = apply a
+
+copyDir :: FilePath -> FilePath -> IO ()
+copyDir src dst = do
+ whenM (not <$> doesDirectoryExist src) $
+ throw (userError "source does not exist")
+ --whenM (doesFileOrDirectoryExist dst) $
+ --throw (userError "destination already exists")
+ createDirectoryIfMissing True dst
+ content <- getDirectoryContents src
+ let xs = filter (`notElem` [".", ".."]) content
+ forM_ xs $ \name -> do
+ let srcPath = src > name
+ let dstPath = dst > name
+ isDirectory <- doesDirectoryExist srcPath
+ if isDirectory
+ then copyDir srcPath dstPath
+ else copyFile srcPath dstPath
+ where
+ doesFileOrDirectoryExist x = orM [doesDirectoryExist x, doesFileExist x]
+ orM xs = or <$> sequence xs
+ whenM s r = s >>= flip when r
diff --git a/cabal.project b/cabal.project
new file mode 100644
index 0000000..989ae5a
--- /dev/null
+++ b/cabal.project
@@ -0,0 +1,13 @@
+packages: ./*.cabal
+
+with-compiler: ghc-9.4.8
+
+source-repository-package
+ type: git
+ location: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git
+ tag: 335e804454cd30da2c526457be37e477f71e4665
+
+source-repository-package
+ type: git
+ location: https://git.vergara.tech/Vergara_Tech/haskell-hexstring.git
+ tag: 39d8da7b11a80269454c2f134a5c834e0f3cb9a7
diff --git a/cabal.project.freeze b/cabal.project.freeze
new file mode 100644
index 0000000..5cf3ed7
--- /dev/null
+++ b/cabal.project.freeze
@@ -0,0 +1,207 @@
+active-repositories: hackage.haskell.org:merge
+constraints: any.Cabal ==3.8.1.0,
+ any.Cabal-syntax ==3.8.1.0,
+ any.HUnit ==1.6.2.0,
+ any.OneTuple ==0.4.1.1,
+ any.QuickCheck ==2.14.3,
+ QuickCheck -old-random +templatehaskell,
+ any.StateVar ==1.2.2,
+ any.aeson ==2.2.1.0,
+ aeson +ordered-keymap,
+ any.alex ==3.5.1.0,
+ any.ansi-terminal ==1.1,
+ ansi-terminal -example,
+ any.ansi-terminal-types ==1.1,
+ any.appar ==0.1.8,
+ any.array ==0.5.4.0,
+ any.asn1-encoding ==0.9.6,
+ any.asn1-parse ==0.9.5,
+ any.asn1-types ==0.3.4,
+ any.assoc ==1.1,
+ assoc +tagged,
+ any.async ==2.2.5,
+ async -bench,
+ any.attoparsec ==0.14.4,
+ attoparsec -developer,
+ any.attoparsec-aeson ==2.2.0.1,
+ any.base ==4.17.2.1,
+ any.base-orphans ==0.9.1,
+ any.base16 ==1.0,
+ any.base16-bytestring ==1.0.2.0,
+ any.base58-bytestring ==0.1.0,
+ any.base64-bytestring ==1.2.1.0,
+ any.basement ==0.0.16,
+ any.bifunctors ==5.6.1,
+ bifunctors +tagged,
+ any.binary ==0.8.9.1,
+ any.binary-orphans ==1.0.4.1,
+ any.bitvec ==1.1.5.0,
+ bitvec +simd,
+ any.blaze-builder ==0.4.2.3,
+ any.borsh ==0.3.0,
+ any.byteorder ==1.0.4,
+ any.bytes ==0.17.3,
+ any.bytestring ==0.11.5.3,
+ any.c2hs ==0.28.8,
+ c2hs +base3 -regression,
+ any.call-stack ==0.4.0,
+ any.case-insensitive ==1.2.1.0,
+ any.cborg ==0.2.10.0,
+ cborg +optimize-gmp,
+ any.cereal ==0.5.8.3,
+ cereal -bytestring-builder,
+ any.colour ==2.3.6,
+ any.comonad ==5.0.8,
+ comonad +containers +distributive +indexed-traversable,
+ any.conduit ==1.3.5,
+ any.conduit-extra ==1.3.6,
+ any.containers ==0.6.7,
+ any.contravariant ==1.5.5,
+ contravariant +semigroups +statevar +tagged,
+ any.cookie ==0.5.0,
+ any.crypton ==0.34,
+ crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
+ any.crypton-connection ==0.3.2,
+ any.crypton-x509 ==1.7.6,
+ any.crypton-x509-store ==1.6.9,
+ any.crypton-x509-system ==1.6.7,
+ any.crypton-x509-validation ==1.6.12,
+ any.cryptonite ==0.30,
+ cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
+ any.data-default ==0.7.1.1,
+ any.data-default-class ==0.1.2.0,
+ any.data-default-instances-containers ==0.0.1,
+ any.data-default-instances-dlist ==0.0.1,
+ any.data-default-instances-old-locale ==0.0.1,
+ any.data-fix ==0.3.2,
+ any.deepseq ==1.4.8.0,
+ any.directory ==1.3.7.1,
+ any.distributive ==0.6.2.1,
+ distributive +semigroups +tagged,
+ any.dlist ==1.0,
+ dlist -werror,
+ any.entropy ==0.4.1.10,
+ entropy -donotgetentropy,
+ any.envy ==2.1.2.0,
+ any.exceptions ==0.10.5,
+ any.filepath ==1.4.2.2,
+ any.foldable1-classes-compat ==0.1,
+ foldable1-classes-compat +tagged,
+ any.foreign-rust ==0.1.0,
+ any.generically ==0.1.1,
+ any.generics-sop ==0.5.1.4,
+ any.ghc-bignum ==1.3,
+ any.ghc-boot-th ==9.4.8,
+ any.ghc-prim ==0.9.1,
+ any.half ==0.3.1,
+ any.happy ==1.20.1.1,
+ any.hashable ==1.4.3.0,
+ hashable +integer-gmp -random-initial-seed,
+ any.haskell-lexer ==1.1.1,
+ any.haskoin-core ==1.0.4,
+ any.hexstring ==0.12.1.0,
+ any.hourglass ==0.2.12,
+ any.hsc2hs ==0.68.10,
+ hsc2hs -in-ghc-tree,
+ any.hspec ==2.11.7,
+ any.hspec-core ==2.11.7,
+ any.hspec-discover ==2.11.7,
+ any.hspec-expectations ==0.8.4,
+ any.http-client ==0.7.16,
+ http-client +network-uri,
+ any.http-client-tls ==0.3.6.3,
+ any.http-conduit ==2.3.8.3,
+ http-conduit +aeson,
+ any.http-types ==0.12.4,
+ any.indexed-traversable ==0.1.3,
+ any.indexed-traversable-instances ==0.1.1.2,
+ any.integer-conversion ==0.1.0.1,
+ any.integer-gmp ==1.1,
+ any.integer-logarithms ==1.0.3.1,
+ integer-logarithms -check-bounds +integer-gmp,
+ any.iproute ==1.7.12,
+ any.language-c ==0.9.3,
+ language-c -allwarnings +iecfpextension +usebytestrings,
+ any.memory ==0.18.0,
+ memory +support_bytestring +support_deepseq,
+ any.mime-types ==0.1.2.0,
+ any.mono-traversable ==1.0.17.0,
+ any.mtl ==2.2.2,
+ any.murmur3 ==1.0.5,
+ any.network ==3.1.4.0,
+ network -devel,
+ any.network-uri ==2.6.4.2,
+ any.old-locale ==1.0.0.7,
+ any.old-time ==1.1.0.4,
+ any.parsec ==3.1.16.1,
+ any.pem ==0.2.4,
+ any.pretty ==1.1.3.6,
+ any.primitive ==0.9.0.0,
+ any.process ==1.6.18.0,
+ any.quickcheck-io ==0.2.0,
+ any.random ==1.2.1.2,
+ any.regex-base ==0.94.0.2,
+ any.regex-compat ==0.95.2.1,
+ any.regex-posix ==0.96.0.1,
+ regex-posix -_regex-posix-clib,
+ any.resourcet ==1.3.0,
+ any.rts ==1.0.2,
+ any.safe ==0.3.21,
+ any.scientific ==0.3.7.0,
+ scientific -bytestring-builder -integer-simple,
+ any.secp256k1-haskell ==1.1.0,
+ any.semialign ==1.3,
+ semialign +semigroupoids,
+ any.semigroupoids ==6.0.0.1,
+ semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
+ any.serialise ==0.2.6.1,
+ serialise +newtime15,
+ any.socks ==0.6.1,
+ any.sop-core ==0.5.0.2,
+ any.split ==0.2.5,
+ any.splitmix ==0.1.0.5,
+ splitmix -optimised-mixer,
+ any.stm ==2.5.1.0,
+ any.streaming-commons ==0.2.2.6,
+ streaming-commons -use-bytestring-builder,
+ any.strict ==0.5,
+ any.string-conversions ==0.4.0.1,
+ any.tagged ==0.8.8,
+ tagged +deepseq +transformers,
+ any.template-haskell ==2.19.0.0,
+ any.text ==2.0.2,
+ any.text-iso8601 ==0.1,
+ any.text-short ==0.1.5,
+ text-short -asserts,
+ any.tf-random ==0.5,
+ any.th-abstraction ==0.6.0.0,
+ any.th-compat ==0.1.4,
+ any.these ==1.2,
+ any.time ==1.12.2,
+ any.time-compat ==1.9.6.1,
+ time-compat -old-locale,
+ any.tls ==2.0.1,
+ tls -devel,
+ any.transformers ==0.5.6.2,
+ any.transformers-compat ==0.7.2,
+ transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
+ any.typed-process ==0.2.11.1,
+ any.unix ==2.7.3,
+ any.unix-time ==0.4.12,
+ any.unliftio-core ==0.2.1.0,
+ any.unordered-containers ==0.2.20,
+ unordered-containers -debug,
+ any.utf8-string ==1.0.2,
+ any.uuid-types ==1.0.5.1,
+ any.vector ==0.13.1.0,
+ vector +boundschecks -internalchecks -unsafechecks -wall,
+ any.vector-algorithms ==0.9.0.1,
+ vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
+ any.vector-stream ==0.1.0.1,
+ any.void ==0.7.3,
+ void -safe,
+ any.wide-word ==0.1.6.0,
+ any.witherable ==0.4.2,
+ any.zlib ==0.7.0.0,
+ zlib -bundled-c-zlib +non-blocking-ffi +pkg-config
+index-state: hackage.haskell.org 2024-03-06T20:26:39Z
diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock
index a77c068..53583a4 100644
--- a/librustzcash-wrapper/Cargo.lock
+++ b/librustzcash-wrapper/Cargo.lock
@@ -58,12 +58,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-[[package]]
-name = "base-x"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
-
[[package]]
name = "base64"
version = "0.21.2"
@@ -88,6 +82,33 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+[[package]]
+name = "bech32"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
+
+[[package]]
+name = "bellman"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afceed28bac7f9f5a508bca8aeeff51cdfa4770c0b967ac55c621e2ddfd6171"
+dependencies = [
+ "bitvec",
+ "blake2s_simd",
+ "byteorder",
+ "crossbeam-channel",
+ "ff",
+ "group",
+ "lazy_static",
+ "log",
+ "num_cpus",
+ "pairing",
+ "rand_core",
+ "rayon",
+ "subtle",
+]
+
[[package]]
name = "bincode"
version = "1.3.3"
@@ -117,6 +138,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
[[package]]
name = "bitvec"
version = "1.0.1"
@@ -282,10 +309,20 @@ dependencies = [
]
[[package]]
-name = "bumpalo"
-version = "3.13.0"
+name = "bs58"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896"
+dependencies = [
+ "sha2 0.10.6",
+ "tinyvec",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
[[package]]
name = "byteorder"
@@ -310,9 +347,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
[[package]]
name = "cfg-if"
@@ -355,12 +392,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "const_fn"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
-
[[package]]
name = "constant_time_eq"
version = "0.2.5"
@@ -435,6 +466,15 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
[[package]]
name = "digest"
version = "0.9.0"
@@ -456,10 +496,13 @@ dependencies = [
]
[[package]]
-name = "discard"
-version = "1.0.4"
+name = "document-features"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
+checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
+dependencies = [
+ "litrs",
+]
[[package]]
name = "either"
@@ -666,6 +709,19 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "hdwallet"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7"
+dependencies = [
+ "lazy_static",
+ "rand_core",
+ "ring",
+ "secp256k1",
+ "thiserror",
+]
+
[[package]]
name = "heck"
version = "0.4.1"
@@ -704,11 +760,11 @@ dependencies = [
[[package]]
name = "incrementalmerkletree"
-version = "0.3.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b"
+checksum = "eb1872810fb725b06b8c153dde9e86f3ec26747b9b60096da7a869883b549cbe"
dependencies = [
- "serde",
+ "either",
]
[[package]]
@@ -760,10 +816,13 @@ dependencies = [
]
[[package]]
-name = "itoa"
-version = "1.0.6"
+name = "js-sys"
+version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
+dependencies = [
+ "wasm-bindgen",
+]
[[package]]
name = "jubjub"
@@ -790,9 +849,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.146"
+version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libm"
@@ -806,6 +865,12 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
[[package]]
name = "log"
version = "0.4.19"
@@ -885,6 +950,12 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
[[package]]
name = "num-integer"
version = "0.1.45"
@@ -928,9 +999,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "orchard"
-version = "0.4.0"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6f418f2c25573923f81a091f38b4b19bc20f6c92b5070fb8f0711e64a2b998"
+checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42"
dependencies = [
"aes",
"bitvec",
@@ -952,6 +1023,8 @@ dependencies = [
"subtle",
"tracing",
"zcash_note_encryption",
+ "zcash_spec",
+ "zip32",
]
[[package]]
@@ -1017,9 +1090,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
-version = "0.2.9"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "poly1305"
@@ -1032,6 +1105,12 @@ dependencies = [
"universal-hash",
]
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -1040,12 +1119,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prettyplease"
-version = "0.1.25"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
dependencies = [
"proc-macro2",
- "syn 1.0.109",
+ "syn 2.0.32",
]
[[package]]
@@ -1057,12 +1136,6 @@ dependencies = [
"toml",
]
-[[package]]
-name = "proc-macro-hack"
-version = "0.5.20+deprecated"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
-
[[package]]
name = "proc-macro2"
version = "1.0.66"
@@ -1074,9 +1147,9 @@ dependencies = [
[[package]]
name = "prost"
-version = "0.11.9"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
+checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
dependencies = [
"bytes",
"prost-derive",
@@ -1084,53 +1157,53 @@ dependencies = [
[[package]]
name = "prost-build"
-version = "0.11.9"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
+checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2"
dependencies = [
"bytes",
"heck",
"itertools",
- "lazy_static",
"log",
"multimap",
+ "once_cell",
"petgraph",
"prettyplease",
"prost",
"prost-types",
"regex",
- "syn 1.0.109",
+ "syn 2.0.32",
"tempfile",
"which",
]
[[package]]
name = "prost-derive"
-version = "0.11.9"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
+checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.32",
]
[[package]]
name = "prost-types"
-version = "0.11.9"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
+checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e"
dependencies = [
"prost",
]
[[package]]
name = "quote"
-version = "1.0.26"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -1211,13 +1284,26 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "redjubjub"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c"
+dependencies = [
+ "rand_core",
+ "reddsa",
+ "serde",
+ "thiserror",
+ "zeroize",
+]
+
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -1237,7 +1323,7 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
]
[[package]]
@@ -1256,12 +1342,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
-name = "rustc_version"
-version = "0.2.3"
+name = "ring"
+version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
- "semver",
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "ripemd"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
+dependencies = [
+ "digest 0.10.7",
]
[[package]]
@@ -1270,7 +1371,7 @@ version = "0.37.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
@@ -1282,23 +1383,56 @@ dependencies = [
name = "rustzcash-wrapper"
version = "0.1.0"
dependencies = [
- "bech32 0.9.1",
+ "bech32 0.11.0",
"borsh 0.10.3",
"f4jumble",
"haskell-ffi",
+ "incrementalmerkletree",
+ "jubjub",
+ "nonempty",
"orchard",
"proc-macro2",
- "zcash_address",
+ "rand_core",
+ "sapling-crypto",
+ "secp256k1",
+ "zcash_address 0.2.0",
"zcash_client_backend",
"zcash_note_encryption",
"zcash_primitives",
+ "zip32",
]
[[package]]
-name = "ryu"
-version = "1.0.13"
+name = "sapling-crypto"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "02f4270033afcb0c74c5c7d59c73cfd1040367f67f224fe7ed9a919ae618f1b7"
+dependencies = [
+ "aes",
+ "bellman",
+ "bitvec",
+ "blake2b_simd",
+ "blake2s_simd",
+ "bls12_381",
+ "byteorder",
+ "document-features",
+ "ff",
+ "fpe",
+ "group",
+ "hex",
+ "incrementalmerkletree",
+ "jubjub",
+ "lazy_static",
+ "memuse",
+ "rand",
+ "rand_core",
+ "redjubjub",
+ "subtle",
+ "tracing",
+ "zcash_note_encryption",
+ "zcash_spec",
+ "zip32",
+]
[[package]]
name = "scopeguard"
@@ -1306,6 +1440,24 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "secp256k1"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894"
+dependencies = [
+ "secp256k1-sys",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "secrecy"
version = "0.8.0"
@@ -1315,67 +1467,26 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-
[[package]]
name = "serde"
-version = "1.0.159"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.159"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
]
-[[package]]
-name = "serde_json"
-version = "1.0.96"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
-dependencies = [
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "sha1"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
-dependencies = [
- "sha1_smol",
-]
-
-[[package]]
-name = "sha1_smol"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
-
[[package]]
name = "sha2"
version = "0.9.9"
@@ -1400,76 +1511,30 @@ dependencies = [
"digest 0.10.7",
]
+[[package]]
+name = "shardtree"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbf20c7a2747d9083092e3a3eeb9a7ed75577ae364896bebbc5e0bdcd4e97735"
+dependencies = [
+ "bitflags 2.4.2",
+ "either",
+ "incrementalmerkletree",
+ "tracing",
+]
+
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-[[package]]
-name = "standback"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
-dependencies = [
- "version_check",
-]
-
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-[[package]]
-name = "stdweb"
-version = "0.4.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
-dependencies = [
- "discard",
- "rustc_version",
- "stdweb-derive",
- "stdweb-internal-macros",
- "stdweb-internal-runtime",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "stdweb-derive"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
-dependencies = [
- "proc-macro2",
- "quote",
- "serde",
- "serde_derive",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "stdweb-internal-macros"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
-dependencies = [
- "base-x",
- "proc-macro2",
- "quote",
- "serde",
- "serde_derive",
- "serde_json",
- "sha1",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "stdweb-internal-runtime"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
-
[[package]]
name = "subtle"
version = "2.4.1"
@@ -1489,9 +1554,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.13"
+version = "2.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
+checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
dependencies = [
"proc-macro2",
"quote",
@@ -1535,46 +1600,27 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
]
[[package]]
name = "time"
-version = "0.2.27"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
dependencies = [
- "const_fn",
- "libc",
- "standback",
- "stdweb",
- "time-macros",
- "version_check",
- "winapi",
+ "deranged",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
]
[[package]]
-name = "time-macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
-dependencies = [
- "proc-macro-hack",
- "time-macros-impl",
-]
-
-[[package]]
-name = "time-macros-impl"
+name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
-dependencies = [
- "proc-macro-hack",
- "proc-macro2",
- "quote",
- "standback",
- "syn 1.0.109",
-]
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "tinyvec"
@@ -1602,15 +1648,15 @@ dependencies = [
[[package]]
name = "tonic-build"
-version = "0.9.2"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07"
+checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889"
dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
"quote",
- "syn 1.0.109",
+ "syn 2.0.32",
]
[[package]]
@@ -1633,7 +1679,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
]
[[package]]
@@ -1688,6 +1734,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
[[package]]
name = "version_check"
version = "0.9.4"
@@ -1702,9 +1754,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.87"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -1712,24 +1764,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.87"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.87"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1737,22 +1789,32 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.87"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.87"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
[[package]]
name = "which"
@@ -1869,39 +1931,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "804268e702b664fc09d3e2ce82786d0addf4ae57ba6976469be63e09066bf9f7"
dependencies = [
"bech32 0.8.1",
- "bs58",
+ "bs58 0.4.0",
+ "f4jumble",
+ "zcash_encoding",
+]
+
+[[package]]
+name = "zcash_address"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bce173f1d9ed4f806e310bc3a873301531e7a6dc209928584d6404e3f8228ef4"
+dependencies = [
+ "bech32 0.9.1",
+ "bs58 0.5.0",
"f4jumble",
"zcash_encoding",
]
[[package]]
name = "zcash_client_backend"
-version = "0.9.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a55db8d2cb4ca82a71fa66ccd9fa5b211f5ab90c866721311ddd85f8f90d0701"
+checksum = "001ec65dc2828ee648dc6d29f0944d7a877fe68ad06e001a203c11770ab1b3d4"
dependencies = [
"base64",
"bech32 0.9.1",
"bls12_381",
- "bs58",
+ "bs58 0.5.0",
"crossbeam-channel",
+ "document-features",
"group",
+ "hex",
+ "incrementalmerkletree",
"memuse",
"nom",
- "orchard",
+ "nonempty",
"percent-encoding",
"prost",
+ "rand_core",
"rayon",
+ "sapling-crypto",
"secrecy",
+ "shardtree",
"subtle",
"time",
"tonic-build",
"tracing",
"which",
- "zcash_address",
+ "zcash_address 0.3.1",
"zcash_encoding",
+ "zcash_keys",
"zcash_note_encryption",
"zcash_primitives",
+ "zip32",
]
[[package]]
@@ -1915,10 +1997,33 @@ dependencies = [
]
[[package]]
-name = "zcash_note_encryption"
-version = "0.3.0"
+name = "zcash_keys"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb2149e6cd5fbee36c5b87c601715a8c35554602f7fe84af38b636afa2db318"
+checksum = "4f22d3407fdd6992b49f037f23862ab376be6013be6f2d0bc85948a635edc1f5"
+dependencies = [
+ "bech32 0.9.1",
+ "bls12_381",
+ "bs58 0.5.0",
+ "document-features",
+ "group",
+ "memuse",
+ "nonempty",
+ "rand_core",
+ "sapling-crypto",
+ "subtle",
+ "tracing",
+ "zcash_address 0.3.1",
+ "zcash_encoding",
+ "zcash_primitives",
+ "zip32",
+]
+
+[[package]]
+name = "zcash_note_encryption"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b4580cd6cee12e44421dac43169be8d23791650816bdb34e6ddfa70ac89c1c5"
dependencies = [
"chacha20",
"chacha20poly1305",
@@ -1929,35 +2034,49 @@ dependencies = [
[[package]]
name = "zcash_primitives"
-version = "0.11.0"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914d2195a478d5b63191584dff126f552751115181857b290211ec88e68acc3e"
+checksum = "9070e084570bb78aed4f8d71fd6254492e62c87a5d01e084183980e98117092d"
dependencies = [
"aes",
"bip0039",
- "bitvec",
"blake2b_simd",
- "blake2s_simd",
- "bls12_381",
"byteorder",
+ "document-features",
"equihash",
"ff",
"fpe",
"group",
+ "hdwallet",
"hex",
"incrementalmerkletree",
"jubjub",
- "lazy_static",
"memuse",
"nonempty",
"orchard",
"rand",
"rand_core",
+ "redjubjub",
+ "ripemd",
+ "sapling-crypto",
+ "secp256k1",
"sha2 0.10.6",
"subtle",
- "zcash_address",
+ "tracing",
+ "zcash_address 0.3.1",
"zcash_encoding",
"zcash_note_encryption",
+ "zcash_spec",
+ "zip32",
+]
+
+[[package]]
+name = "zcash_spec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b"
+dependencies = [
+ "blake2b_simd",
]
[[package]]
@@ -1977,5 +2096,16 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.13",
+ "syn 2.0.32",
+]
+
+[[package]]
+name = "zip32"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e"
+dependencies = [
+ "blake2b_simd",
+ "memuse",
+ "subtle",
]
diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml
index 7496e7c..b8b2b37 100644
--- a/librustzcash-wrapper/Cargo.toml
+++ b/librustzcash-wrapper/Cargo.toml
@@ -4,19 +4,26 @@ version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
haskell-ffi.git = "https://github.com/BeFunctional/haskell-rust-ffi.git"
haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274"
f4jumble = "0.1"
zcash_address = "0.2.0"
borsh = "0.10"
-bech32 = "0.9.1"
-orchard = "0.4.0"
-zcash_note_encryption = "0.3.0"
-zcash_primitives = "0.11.0"
-zcash_client_backend = "0.9.0"
+bech32 = "0.11"
+orchard = "0.7.1"
+zcash_note_encryption = "0.4.0"
+zcash_primitives = { version = "0.14.0", features = ["transparent-inputs"]}
+zcash_client_backend = "0.11.1"
+sapling-crypto = "0.1.3"
+zip32 = "0.1.0"
proc-macro2 = "1.0.66"
+nonempty = "0.7.0"
+incrementalmerkletree = "0.5.0"
+secp256k1 = "0.26.0"
+jubjub = "0.10.0"
+rand_core = { version = "0.6.4", features = ["getrandom"]}
+
[features]
capi = []
diff --git a/librustzcash-wrapper/rust-toolchain.toml b/librustzcash-wrapper/rust-toolchain.toml
new file mode 100644
index 0000000..af255e0
--- /dev/null
+++ b/librustzcash-wrapper/rust-toolchain.toml
@@ -0,0 +1,4 @@
+[toolchain]
+channel = "nightly-2024-02-04"
+components = [ "rustfmt", "rustc-dev"]
+profile = "minimal"
diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs
index 8973af8..7397401 100644
--- a/librustzcash-wrapper/src/lib.rs
+++ b/librustzcash-wrapper/src/lib.rs
@@ -2,28 +2,18 @@
//
// This file is part of Zcash-Haskell.
//
-// Zcash-Haskell is free software: you can redistribute it and/or modify it
-// under the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-// details.
-//
-// You should have received a copy of the GNU Lesser General Public License along with
-// Zcash-Haskell. If not, see .
-
use std::{
marker::PhantomData,
io::{
Write,
Cursor
},
- fmt::{Debug, Display, Formatter}
};
+use nonempty::NonEmpty;
+
+use rand_core::OsRng;
+
use f4jumble;
use borsh::{BorshDeserialize, BorshSerialize};
@@ -35,28 +25,99 @@ use haskell_ffi::{
FromHaskell, HaskellSize, ToHaskell
};
-use zcash_primitives::{
- zip32::Scope as SaplingScope,
- transaction::components::sapling::{
- read_zkproof,
+use secp256k1::SecretKey;
+
+use jubjub::Fr;
+
+use incrementalmerkletree::{
+ frontier::CommitmentTree,
+ witness::IncrementalWitness
+};
+
+use zip32::{
+ Scope as SaplingScope,
+ ChildIndex
+};
+
+
+use sapling_crypto::{
+ NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH,
+ Node,
+ MerklePath,
+ PaymentAddress,
+ Anchor as SaplingAnchor,
+ value::ValueCommitment as SaplingValueCommitment,
+ note::{
+ ExtractedNoteCommitment as SaplingNoteCommitment,
+ Note as SaplingNote,
+ Rseed
+ },
+ keys::{
+ PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
+ ExpandedSpendingKey,
+ FullViewingKey as SaplingFullViewingKey
+ },
+ note_encryption::{
+ SaplingDomain,
+ Zip212Enforcement
+ },
+ bundle::{
GrothProofBytes,
OutputDescription,
- CompactOutputDescription
+ SpendDescription,
+ Authorized as SaplingAuthorized,
+ Bundle as SaplingBundle
},
- sapling::{
- value::ValueCommitment as SaplingValueCommitment,
- keys::{
- FullViewingKey as SaplingViewingKey,
- PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey
+ value::NoteValue as SaplingNoteValue,
+ circuit::{
+ SpendParameters,
+ OutputParameters
+ },
+ zip32::{
+ sapling_find_address,
+ DiversifierKey
+ }
+};
+
+use zcash_primitives::{
+ merkle_tree::{
+ read_commitment_tree,
+ write_commitment_tree,
+ read_incremental_witness,
+ write_incremental_witness
+ },
+ legacy::{
+ Script,
+ TransparentAddress
+ },
+ zip339::{Count, Mnemonic},
+ transaction::{
+ Transaction,
+ fees::zip317::FeeRule,
+ builder::{
+ Builder,
+ Error,
+ BuildConfig
},
- note_encryption::SaplingDomain,
- PaymentAddress,
- note::ExtractedNoteCommitment as SaplingExtractedNoteCommitment
+ components::{
+ amount::{
+ Amount,
+ NonNegativeAmount
+ },
+ transparent::{
+ Bundle as TransparentBundle,
+ TxIn,
+ TxOut,
+ OutPoint,
+ Authorized
+ }
+ }
},
- transaction::Transaction,
+ memo::MemoBytes,
consensus::{
BranchId::Nu5,
MainNetwork,
+ TestNetwork,
BlockHeight
}
};
@@ -67,31 +128,49 @@ use zcash_address::{
ZcashAddress
};
-use zcash_client_backend::keys::sapling::ExtendedFullViewingKey;
-
-use orchard::{
- Action,
- keys::{FullViewingKey, PreparedIncomingViewingKey, Scope},
- note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
- note_encryption::OrchardDomain,
- primitives::redpallas::{VerificationKey, SpendAuth, Signature},
- value::ValueCommitment
+use zcash_client_backend::encoding::decode_payment_address;
+use zcash_client_backend::keys::sapling::{
+ spending_key,
+ ExtendedFullViewingKey,
+ ExtendedSpendingKey,
+ DiversifiableFullViewingKey
};
-use zcash_note_encryption::EphemeralKeyBytes;
+use zcash_primitives::zip32::DiversifierIndex;
+
+use orchard::{
+ Address as OrchardAddress,
+ Bundle as OrchardBundle,
+ bundle::{
+ Authorized as OrchardAuthorized,
+ Flags
+ },
+ Action,
+ keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope},
+ note::{RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
+ note_encryption::OrchardDomain,
+ primitives::redpallas::{VerificationKey, SpendAuth, Signature},
+ tree::{
+ MerklePath as OrchardMerklePath,
+ MerkleHashOrchard,
+ Anchor as OrchardAnchor
+ },
+ value::{
+ ValueCommitment,
+ NoteValue
+ }
+};
use bech32::{
- decode,
- u5,
- FromBase32,
- ToBase32,
- Variant
+ Hrp,
+ Bech32,
+ Bech32m
};
pub enum RW {}
pub const RW: PhantomData = PhantomData;
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct RawData {
hrp: Vec,
bytes: Vec
@@ -111,7 +190,7 @@ impl ToHaskell for RawData {
//}
//}
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct HrawTx {
bytes: Vec,
s: bool,
@@ -125,14 +204,14 @@ impl ToHaskell for HrawTx {
}
}
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct HshieldedOutput {
- cv: Vec,
- cmu: Vec,
- eph_key: Vec,
- enc_txt: Vec,
- out_txt: Vec,
- proof: Vec
+ pub cv: Hhex,
+ cmu: Hhex,
+ eph_key: Hhex,
+ enc_txt: Hhex,
+ out_txt: Hhex,
+ proof: Hhex
}
impl FromHaskell for HshieldedOutput {
@@ -150,22 +229,48 @@ impl ToHaskell for HshieldedOutput {
}
impl HshieldedOutput {
- fn from_object(s: OutputDescription) -> Result {
- let o = HshieldedOutput { cv: s.cv().to_bytes().to_vec(), cmu: s.cmu().to_bytes().to_vec(), eph_key: s.ephemeral_key().0.to_vec(), enc_txt: s.enc_ciphertext().to_vec(), out_txt: s.out_ciphertext().to_vec(), proof: s.zkproof().to_vec() };
- Ok(o)
+ fn from_object(s: &OutputDescription) -> HshieldedOutput {
+ HshieldedOutput { cv: Hhex{ bytes: s.cv().to_bytes().to_vec()}, cmu: Hhex{ bytes: s.cmu().to_bytes().to_vec()}, eph_key: Hhex{ bytes: s.ephemeral_key().0.to_vec()}, enc_txt: Hhex{ bytes: s.enc_ciphertext().to_vec()}, out_txt: Hhex{ bytes: s.out_ciphertext().to_vec()}, proof: Hhex{ bytes: s.zkproof().to_vec()} }
+ }
+ pub fn pack(sp: &[OutputDescription]) -> Vec {
+ let mut r = Vec::new();
+ for s in sp {
+ r.push(HshieldedOutput::from_object(s));
+ }
+ return r
+ }
+ pub fn to_output_description(&mut self) -> Result> {
+ let cv = SaplingValueCommitment::from_bytes_not_small_order(&to_array(self.cv.bytes.clone())).unwrap();
+ let cmu = SaplingNoteCommitment::from_bytes(&to_array(self.cmu.bytes.clone())).unwrap();
+ let eph_key = zcash_note_encryption::EphemeralKeyBytes::from(to_array(self.eph_key.bytes.clone()));
+ let x = OutputDescription::from_parts(cv, cmu, eph_key, to_array(self.enc_txt.bytes.clone()), to_array(self.out_txt.bytes.clone()), to_array(self.proof.bytes.clone()));
+ return Ok(x)
}
}
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Hhex {
+ bytes: Vec
+}
+
+impl ToHaskell for Hhex {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct Haction {
- nf: Vec,
- rk: Vec,
- cmx: Vec,
- eph_key: Vec,
- enc_txt: Vec,
- out_txt: Vec,
- cv: Vec,
- auth: Vec
+ nf: Hhex,
+ rk: Hhex,
+ cmx: Hhex,
+ eph_key: Hhex,
+ enc_txt: Hhex,
+ out_txt: Hhex,
+ cv: Hhex,
+ auth: Hhex
}
impl FromHaskell for Haction {
@@ -175,11 +280,24 @@ impl FromHaskell for Haction {
}
}
-#[derive(BorshSerialize, BorshDeserialize)]
+impl Haction {
+ pub fn pack(sp: &NonEmpty>>) -> Vec {
+ let mut r = Vec::new();
+ for s in sp {
+ r.push(Haction {nf: Hhex { bytes: s.nullifier().to_bytes().to_vec()}, rk: Hhex { bytes: <[u8; 32]>::from(s.rk()).to_vec()}, cmx: Hhex{bytes: s.cmx().to_bytes().to_vec()}, eph_key: Hhex{ bytes: s.encrypted_note().epk_bytes.to_vec()}, enc_txt: Hhex {bytes: s.encrypted_note().enc_ciphertext.to_vec()}, out_txt: Hhex {bytes: s.encrypted_note().out_ciphertext.to_vec()}, cv: Hhex {bytes: s.cv_net().to_bytes().to_vec()}, auth: Hhex { bytes: <[u8; 64]>::from(s.authorization()).to_vec()}});
+ }
+ return r
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct Hnote {
note: u64,
recipient: Vec,
- memo: Vec
+ memo: Vec,
+ nullifier: Vec,
+ rho: Vec,
+ rseed: Hrseed
}
impl ToHaskell for Hnote {
@@ -189,7 +307,28 @@ impl ToHaskell for Hnote {
}
}
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Hrseed {
+ kind: u8,
+ bytes: Vec
+}
+
+
+impl FromHaskell for Hrseed {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = Hrseed::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+impl ToHaskell for Hrseed {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct Hua {
net: u8,
o_rec: Vec,
@@ -222,7 +361,288 @@ impl Hua {
}
}
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Htx {
+ txid: Vec,
+ locktime: u32,
+ expiry: u32,
+ t_bundle: HTBundle,
+ s_bundle: HSBundle,
+ o_bundle: HOBundle
+}
+
+impl ToHaskell for Htx {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HTBundle {
+ empty: bool,
+ vin: Vec,
+ vout: Vec,
+ coinbase: bool
+}
+
+impl ToHaskell for HTBundle {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl HTBundle {
+ pub fn from_bundle(b: &TransparentBundle) -> HTBundle {
+ HTBundle {empty: false, vin: b.vin.iter().map(HTxIn::pack).collect() , vout: b.vout.iter().map(HTxOut::pack).collect(), coinbase: b.is_coinbase()}
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HTxIn {
+ outpoint: Houtpoint,
+ script: Vec,
+ sequence: u32
+}
+
+impl ToHaskell for HTxIn {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl HTxIn {
+ pub fn pack(t: &TxIn) -> HTxIn {
+ return HTxIn { outpoint: Houtpoint::pack(&t.prevout), script: t.script_sig.0.clone(), sequence: t.sequence}
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HTxOut {
+ amt: i64,
+ script: Vec
+}
+
+impl FromHaskell for HTxOut {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = HTxOut::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+impl ToHaskell for HTxOut {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl HTxOut {
+ pub fn pack(t: &TxOut) -> HTxOut {
+ return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() }
+ }
+ pub fn unpack(&self) -> TxOut {
+ TxOut { value: NonNegativeAmount::from_nonnegative_i64(self.amt).unwrap(), script_pubkey: Script(self.script.clone())}
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Houtpoint {
+ hash: Vec,
+ index: u32
+}
+
+impl FromHaskell for Houtpoint {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = Houtpoint::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+impl ToHaskell for Houtpoint {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl Houtpoint {
+ pub fn pack(o: &OutPoint) -> Houtpoint {
+ return Houtpoint {hash: o.hash().to_vec() , index: o.n() }
+ }
+
+ pub fn unpack(&self) -> OutPoint {
+ OutPoint::new(to_array(self.hash.clone()), self.index)
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HtransparentInput {
+ sk: Vec,
+ utxo: Houtpoint,
+ coin: HTxOut
+}
+
+impl FromHaskell for HtransparentInput {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = HtransparentInput::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+impl ToHaskell for HtransparentInput {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HSBundle {
+ empty: bool,
+ spends: Vec,
+ outputs: Vec ,
+ value: i64,
+ sig: Vec
+}
+
+impl ToHaskell for HSBundle {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl HSBundle {
+ pub fn from_bundle(sb: &SaplingBundle) -> HSBundle {
+ let sig = <[u8; 64]>::from(sb.authorization().binding_sig);
+ return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: sig.to_vec()}
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Hspend {
+ cv: Hhex,
+ anchor: Hhex,
+ nullifier: Hhex,
+ rk: Hhex,
+ proof: Hhex,
+ authsig: Hhex
+}
+
+impl ToHaskell for Hspend {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl Hspend {
+ pub fn pack(sp: &[SpendDescription]) -> Vec {
+ let mut r = Vec::new();
+ for s in sp {
+ let rk = s.rk().clone();
+ let sig = s.spend_auth_sig().clone();
+ r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: <[u8; 32]>::from(rk).to_vec()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes: <[u8; 64]>::from(sig).to_vec()}});
+ }
+ return r
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HsaplingInput {
+ sk: Vec,
+ note: Hnote,
+ iw: Vec
+}
+
+impl FromHaskell for HsaplingInput {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = HsaplingInput::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HorchardInput {
+ sk: Vec,
+ note: Hnote,
+ iw: Vec
+}
+
+impl FromHaskell for HorchardInput {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = HorchardInput::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Houtput {
+ kind: u8,
+ ovk: Vec,
+ to: Vec,
+ amt: u64,
+ memo: Vec,
+ chg: bool
+}
+
+impl FromHaskell for Houtput {
+ fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result {
+ let x = Houtput::deserialize(buf)?;
+ Ok(x)
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct HOBundle {
+ empty: bool,
+ actions: Vec,
+ flags: Hflags,
+ value: i64,
+ anchor: Hhex,
+ proof: Hhex,
+ bindingsig: Hhex
+}
+
+impl ToHaskell for HOBundle {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl HOBundle {
+ pub fn from_bundle(b: &OrchardBundle) -> HOBundle {
+ return HOBundle {empty: false, actions: Haction::pack(b.actions()), flags: Hflags::pack(b.flags()), value: i64::from(b.value_balance()), anchor: Hhex{ bytes: b.anchor().to_bytes().to_vec()}, proof: Hhex { bytes: b.authorization().proof().as_ref().to_vec()}, bindingsig: Hhex {bytes: <[u8; 64]>::from(b.authorization().binding_signature()).to_vec()}}
+ }
+}
+
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
+pub struct Hflags {
+ spends: bool,
+ outputs: bool
+}
+
+impl ToHaskell for Hflags {
+ fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> {
+ self.serialize(writer)?;
+ Ok(())
+ }
+}
+
+impl Hflags {
+ pub fn pack(f: &Flags) -> Hflags {
+ return Hflags {spends: f.spends_enabled(), outputs: f.outputs_enabled()}
+ }
+}
+
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct Hufvk {
net: u8,
orchard: Vec,
@@ -251,7 +671,7 @@ impl Hufvk {
}
}
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct Hsvk {
vk: Vec,
ovk: Vec
@@ -334,10 +754,10 @@ pub extern "C" fn rust_wrapper_bech32decode(
out_len: &mut usize
) {
let input: String = marshall_from_haskell_var(input, input_len, RW);
- let decodedBytes = bech32::decode(&input);
- match decodedBytes {
- Ok((hrp, bytes, variant)) => {
- let rd = RawData {hrp: hrp.into(), bytes: Vec::::from_base32(&bytes).unwrap()};
+ let decoded_bytes = bech32::decode(&input);
+ match decoded_bytes {
+ Ok((hrp, bytes)) => {
+ let rd = RawData {hrp: hrp.as_bytes().to_vec(), bytes};
marshall_to_haskell_var(&rd, out, out_len, RW);
}
Err(_e) => {
@@ -347,6 +767,22 @@ pub extern "C" fn rust_wrapper_bech32decode(
}
}
+#[no_mangle]
+pub extern "C" fn rust_wrapper_bech32m_encode(
+ hr: *const u8,
+ hr_len: usize,
+ b: *const u8,
+ b_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ) {
+ let hr: String = marshall_from_haskell_var(hr, hr_len, RW);
+ let hrp = Hrp::parse(&hr).unwrap();
+ let b: Vec = marshall_from_haskell_var(b, b_len, RW);
+ let string = bech32::encode::(hrp, &b).unwrap();
+ marshall_to_haskell_var(&string, out, out_len, RW);
+}
+
#[no_mangle]
pub extern "C" fn rust_wrapper_svk_decode(
input: *const u8,
@@ -355,11 +791,11 @@ pub extern "C" fn rust_wrapper_svk_decode(
let input: Vec = marshall_from_haskell_var(input, input_len, RW);
let svk = ExtendedFullViewingKey::read(&*input);
match svk {
- Ok(k) => {
+ Ok(_k) => {
true
}
Err(e) => {
- print!("{}", e);
+ print!("{:?}", e);
false
}
}
@@ -378,10 +814,10 @@ pub extern "C" fn rust_wrapper_svk_check_address(
let sa = PaymentAddress::from_bytes(&to_array(address_input)).unwrap();
match svk {
Ok(k) => {
- let (div_index, def_address) = k.default_address();
+ let (_div_index, def_address) = k.default_address();
sa == def_address
}
- Err(e) => {
+ Err(_e) => {
false
}
}
@@ -451,6 +887,71 @@ pub extern "C" fn rust_wrapper_ufvk_decode(
}
}
+#[no_mangle]
+pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
+ key: *const u8,
+ key_len: usize,
+ note: *const u8,
+ note_len: usize,
+ external: bool,
+ net: bool,
+ pos: u64,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let sk: Vec = marshall_from_haskell_var(key, key_len, RW);
+ let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
+ let esk = ExtendedSpendingKey::from_bytes(&sk);
+ let main_domain = SaplingDomain::new(Zip212Enforcement::On);
+ //let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000));
+ let scope = if external {
+ SaplingScope::External
+ } else {
+ SaplingScope::Internal
+ };
+ match esk {
+ Ok(k) => {
+ let action = note_input.to_output_description();
+ match action {
+ Ok(action2) => {
+ let dfvk = k.to_diversifiable_full_viewing_key();
+ let ivk = dfvk.to_ivk(scope);
+ let nk = dfvk.to_nk(scope);
+ let pivk = SaplingPreparedIncomingViewingKey::new(&ivk);
+ let result = zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2);
+ match result {
+ Some((n, r, m)) => {
+ let nullifier = n.nf(&nk, pos);
+ let rseed = match n.rseed() {
+ Rseed::BeforeZip212(x) => {
+ Hrseed {kind: 1, bytes: x.to_bytes().to_vec()}
+ },
+ Rseed::AfterZip212(y) => {
+ Hrseed {kind: 2, bytes: y.to_vec()}
+ }
+ };
+ let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec(), rho: vec![0], rseed };
+ marshall_to_haskell_var(&hn, out, out_len, RW);
+ },
+ None => {
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]}};
+ marshall_to_haskell_var(&hn0, out, out_len, RW);
+ }
+ }
+ },
+ Err(_e1) => {
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} };
+ marshall_to_haskell_var(&hn0, out, out_len, RW);
+ }
+ }
+ },
+ Err(_e) => {
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} };
+ marshall_to_haskell_var(&hn0, out, out_len, RW);
+ }
+ }
+}
+
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
key: *const u8,
@@ -461,13 +962,12 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
out_len: &mut usize
){
let evk: Vec = marshall_from_haskell_var(key, key_len, RW);
- let note_input: Vec = marshall_from_haskell_var(note,note_len,RW);
- let mut note_reader = Cursor::new(note_input);
+ let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
let svk = ExtendedFullViewingKey::read(&*evk);
match svk {
Ok(k) => {
- let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000));
- let action2 = OutputDescription::read(&mut note_reader);
+ let domain = SaplingDomain::new(Zip212Enforcement::On);
+ let action2 = note_input.to_output_description();
match action2 {
Ok(action3) => {
let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External);
@@ -475,23 +975,31 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3);
match result {
Some((n, r, m)) => {
- let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec() };
+ let rseed = match n.rseed() {
+ Rseed::BeforeZip212(x) => {
+ Hrseed { kind: 1, bytes: x.to_bytes().to_vec()}
+ },
+ Rseed::AfterZip212(y) => {
+ Hrseed { kind: 2, bytes: y.to_vec()}
+ }
+ };
+ let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0], rho: vec![0], rseed};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
- let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e1) => {
- let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
}
Err(_e) => {
- let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
@@ -509,12 +1017,12 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
let fvk_input: Vec = marshall_from_haskell_var(key, key_len, RW);
let note_input: Haction = marshall_from_haskell_var(note, note_len, RW);
let action: Action> = Action::from_parts(
- Nullifier::from_bytes(&to_array(note_input.nf)).unwrap(),
- VerificationKey::try_from(to_array(note_input.rk)).unwrap(),
- ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx)).unwrap(),
- TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key), enc_ciphertext: to_array(note_input.enc_txt), out_ciphertext: to_array(note_input.out_txt)},
- ValueCommitment::from_bytes(&to_array(note_input.cv)).unwrap(),
- Signature::from(to_array(note_input.auth)));
+ Nullifier::from_bytes(&to_array(note_input.nf.bytes)).unwrap(),
+ VerificationKey::try_from(to_array(note_input.rk.bytes)).unwrap(),
+ ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx.bytes)).unwrap(),
+ TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key.bytes), enc_ciphertext: to_array(note_input.enc_txt.bytes), out_ciphertext: to_array(note_input.out_txt.bytes)},
+ ValueCommitment::from_bytes(&to_array(note_input.cv.bytes)).unwrap(),
+ Signature::from(to_array(note_input.auth.bytes)));
let fvk_array = to_array(fvk_input);
let domain = OrchardDomain::for_nullifier(*action.nullifier());
let dec_fvk = FullViewingKey::from_bytes(&fvk_array);
@@ -525,22 +1033,118 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result {
Some((n, r, m)) => {
- let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec() };
+ let rho = n.rho().to_bytes().to_vec();
+ let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()};
+ let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0], rho, rseed};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
- let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
None => {
- let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
}
+#[no_mangle]
+pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk(
+ key: *const u8,
+ key_len: usize,
+ note: *const u8,
+ note_len: usize,
+ external: bool,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let sk_input: Vec = marshall_from_haskell_var(key, key_len, RW);
+ let note_input: Haction = marshall_from_haskell_var(note, note_len, RW);
+ let action: Action> = Action::from_parts(
+ Nullifier::from_bytes(&to_array(note_input.nf.bytes)).unwrap(),
+ VerificationKey::try_from(to_array(note_input.rk.bytes)).unwrap(),
+ ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx.bytes)).unwrap(),
+ TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key.bytes), enc_ciphertext: to_array(note_input.enc_txt.bytes), out_ciphertext: to_array(note_input.out_txt.bytes)},
+ ValueCommitment::from_bytes(&to_array(note_input.cv.bytes)).unwrap(),
+ Signature::from(to_array(note_input.auth.bytes)));
+ let sk_array = to_array(sk_input);
+ let domain = OrchardDomain::for_nullifier(*action.nullifier());
+ let dec_sk = SpendingKey::from_bytes(sk_array).unwrap();
+ let fvk = FullViewingKey::from(&dec_sk);
+ let ivk = if external {
+ fvk.to_ivk(Scope::External)
+ } else {
+ fvk.to_ivk(Scope::Internal)
+ };
+ let pivk = PreparedIncomingViewingKey::new(&ivk);
+ let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
+ match result {
+ Some((n, r, m)) => {
+ let rho = n.rho().to_bytes().to_vec();
+ let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()};
+ let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec(), rho, rseed};
+ marshall_to_haskell_var(&hn, out, out_len, RW);
+ }
+ None => {
+ let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
+ marshall_to_haskell_var(&hn0, out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_tx_read(
+ tx: *const u8,
+ tx_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW);
+ let mut tx_reader = Cursor::new(tx_input);
+ let parsed_tx = Transaction::read(&mut tx_reader, Nu5);
+ match parsed_tx {
+ Ok(t) => {
+ let tb = t.transparent_bundle();
+ let sb = t.sapling_bundle();
+ let ob = t.orchard_bundle();
+ let h1 = Htx
+ { txid: t.txid().as_ref().to_vec()
+ , locktime: t.lock_time()
+ , expiry: u32::from(t.expiry_height())
+ , t_bundle: match tb {
+ Some(tb1) => {HTBundle::from_bundle(tb1)},
+ None => {HTBundle {empty: true, vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: false}}}
+ , s_bundle: match sb {
+ Some(sb1) => {HSBundle::from_bundle(sb1)},
+ None => {HSBundle{empty: true, spends: vec![Hspend{cv:Hhex { bytes: vec![0]} , anchor:Hhex { bytes: vec![0]} , nullifier:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , proof:Hhex { bytes: vec![0]} , authsig:Hhex { bytes: vec![0]} }], outputs: vec![HshieldedOutput {cv: Hhex { bytes: vec![0]}, cmu: Hhex { bytes: vec![0]}, eph_key: Hhex { bytes: vec![0]}, enc_txt: Hhex { bytes: vec![0]}, out_txt: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]}}], value: 0, sig: vec![0]}} }
+ , o_bundle: match ob {
+ Some(ob1) => {HOBundle::from_bundle(ob1)},
+ None => {HOBundle{empty: true, actions: vec![Haction {nf:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , cmx:Hhex { bytes: vec![0]} , eph_key:Hhex { bytes: vec![0]} , enc_txt:Hhex { bytes: vec![0]} , out_txt:Hhex { bytes: vec![0]} , cv:Hhex { bytes: vec![0]} , auth:Hhex { bytes: vec![0]} }], flags: Hflags{ spends:false, outputs:false}, value: 0, anchor: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]} , bindingsig: Hhex { bytes: vec![0]}}}
+ }
+ };
+ marshall_to_haskell_var(&h1, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Htx
+ {txid: vec![0],
+ locktime: 0,
+ expiry: 0,
+ t_bundle: HTBundle
+ {empty: true,
+ vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}],
+ vout: vec![HTxOut {amt: 0, script: vec![0]}],
+ coinbase: true},
+ s_bundle: HSBundle{empty: true, spends: vec![Hspend{cv:Hhex { bytes: vec![0]} , anchor:Hhex { bytes: vec![0]} , nullifier:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , proof:Hhex { bytes: vec![0]} , authsig:Hhex { bytes: vec![0]} }], outputs: vec![HshieldedOutput {cv: Hhex { bytes: vec![0]}, cmu: Hhex { bytes: vec![0]}, eph_key: Hhex { bytes: vec![0]}, enc_txt: Hhex { bytes: vec![0]}, out_txt: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]}}], value: 0, sig: vec![0]},
+ o_bundle: HOBundle{empty: true, actions: vec![Haction {nf:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , cmx:Hhex { bytes: vec![0]} , eph_key:Hhex { bytes: vec![0]} , enc_txt:Hhex { bytes: vec![0]} , out_txt:Hhex { bytes: vec![0]} , cv:Hhex { bytes: vec![0]} , auth:Hhex { bytes: vec![0]} }], flags: Hflags{ spends:false, outputs:false}, value: 0, anchor: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]} , bindingsig: Hhex { bytes: vec![0]}}
+ };
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+}
+
#[no_mangle]
pub extern "C" fn rust_wrapper_tx_parse(
tx: *const u8,
@@ -549,22 +1153,14 @@ pub extern "C" fn rust_wrapper_tx_parse(
out_len: &mut usize
){
let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW);
- let tx_bytes: Vec = tx_input.clone();
let mut tx_reader = Cursor::new(tx_input);
- let s_o = false;
- let o_a = false;
let parsed_tx = Transaction::read(&mut tx_reader, Nu5);
match parsed_tx {
Ok(t) => {
let s_bundle = t.sapling_bundle();
match s_bundle {
Some(b) => {
- let mut s_output = Vec::new();
- for s_each_out in b.shielded_outputs().iter() {
- let mut out_bytes = Vec::new();
- let _ = s_each_out.write_v4(&mut out_bytes);
- s_output.push(out_bytes);
- }
+ let s_output = HshieldedOutput::pack(b.shielded_outputs());
marshall_to_haskell_var(&s_output, out, out_len, RW);
},
None => {
@@ -582,3 +1178,706 @@ pub extern "C" fn rust_wrapper_tx_parse(
}
}
}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_gen_seed_phrase(
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let mnemonic = Mnemonic::generate(Count::Words24);
+ let seed = mnemonic.phrase().as_bytes().to_vec();
+ marshall_to_haskell_var(&seed, out, out_len, RW);
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_recover_seed(
+ input: *const u8,
+ input_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let phrase: String = marshall_from_haskell_var(input, input_len, RW);
+ let mnemonic = Mnemonic::from_phrase(phrase);
+ match mnemonic {
+ Ok(m) => {
+ let s = m.to_seed("").to_vec();
+ marshall_to_haskell_var(&s, out, out_len, RW);
+ },
+ Err(_e) => {
+ marshall_to_haskell_var(&vec![0], out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_sapling_spendingkey(
+ seed: *const u8,
+ seed_len: usize,
+ coin_type: u32,
+ acc_id: u32,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW);
+ let sk = spending_key(&s, coin_type, zcash_primitives::zip32::AccountId::try_from(acc_id).unwrap());
+ marshall_to_haskell_var(&sk.to_bytes().to_vec(), out, out_len, RW);
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_sapling_paymentaddress(
+ extspk: *const u8,
+ extspk_len: usize,
+ div_ix: u32,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let extspk: Vec = marshall_from_haskell_var(extspk, extspk_len, RW);
+ if div_ix == 0 {
+ let sp_key = ExtendedSpendingKey::from_bytes(&extspk);
+ match sp_key {
+ Ok(sp_key_x) => {
+ let (_def_div, def_address) = sp_key_x.default_address();
+ marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW);
+ },
+ Err(_e) => {
+ marshall_to_haskell_var(&vec![0], out, out_len, RW);
+ }
+ }
+ } else {
+ let expsk = ExpandedSpendingKey::from_spending_key(&extspk);
+ let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk);
+ let dk = DiversifierKey::master(&extspk);
+ let result = sapling_find_address(&fvk, &dk, DiversifierIndex::from(div_ix));
+ match result {
+ Some((_d, p_address)) => {
+ marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW);
+ },
+ None => {
+ marshall_to_haskell_var(&vec![0], out, out_len, RW);
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress(
+ extspk: *const u8,
+ extspk_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let vexspk: Vec = marshall_from_haskell_var(extspk, extspk_len, RW);
+ let vexspkp = &vexspk;
+ let extspku8 : &[u8] = &vexspkp;
+ let extspk = match ExtendedSpendingKey::from_bytes(&extspku8) {
+ Ok( k ) => k,
+ Err( _e ) => {
+ // error recovering ExtendedSpendingKey
+ marshall_to_haskell_var(&vec![0], out, out_len, RW);
+ return
+ }
+ };
+ let dfvk = extspk.to_diversifiable_full_viewing_key();
+ let ( _divIx, cPmtAddress ) = dfvk.change_address();
+ marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW);
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_derive_orchard_spending_key(
+ seed: *const u8,
+ seed_len: usize,
+ coin_type: u32,
+ acc_id: u32,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW);
+ let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap());
+ match sk {
+ Ok(key) => {
+ marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW);
+ },
+ Err(_e) => {
+ marshall_to_haskell_var(&vec![0], out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_derive_orchard_receiver(
+ spend_key: *const u8,
+ spend_key_len: usize,
+ add_id: u32,
+ scope: bool,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let sk_in: Vec = marshall_from_haskell_var(spend_key, spend_key_len, RW);
+ let sk = SpendingKey::from_bytes(sk_in[0..32].try_into().unwrap()).unwrap();
+ let fvk = FullViewingKey::from(&sk);
+ let sc = if scope {
+ Scope::External
+ } else {Scope::Internal};
+ let o_rec = fvk.address_at(add_id, sc);
+ marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW);
+
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_bech32_encode(
+ hr: *const u8,
+ hr_len: usize,
+ b: *const u8,
+ b_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ) {
+ let hr: String = marshall_from_haskell_var(hr, hr_len, RW);
+ let hrp = Hrp::parse(&hr).unwrap();
+ let b: Vec = marshall_from_haskell_var(b, b_len, RW);
+ let string = bech32::encode::(hrp, &b).unwrap();
+ marshall_to_haskell_var(&string, out, out_len, RW);
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_read_sapling_commitment_tree(
+ tree: *const u8,
+ tree_len: usize,
+ node: *const u8,
+ node_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW);
+ let tree_reader = Cursor::new(tree_in);
+ let mut ct = read_commitment_tree::>, SAPLING_DEPTH>(tree_reader);
+ match ct {
+ Ok(mut comm_tree) => {
+ let node_in: Vec = marshall_from_haskell_var(node, node_len, RW);
+ let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(node_in));
+ if sap_note_comm.is_some().into() {
+ let n = Node::from_cmu(&sap_note_comm.unwrap());
+ comm_tree.append(n);
+ let mut out_bytes: Vec = Vec::new();
+ let result = write_commitment_tree(&comm_tree, &mut out_bytes );
+ match result {
+ Ok(()) => {
+ let h = Hhex { bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+ } else {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_read_sapling_witness(
+ tree: *const u8,
+ tree_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW);
+ let tree_reader = Cursor::new(tree_in);
+ let ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap();
+ let inc_wit = IncrementalWitness::from_tree(ct);
+ let mut out_bytes: Vec = Vec::new();
+ let result = write_incremental_witness(&inc_wit, &mut out_bytes);
+ match result {
+ Ok(()) => {
+ let h = Hhex { bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_read_sapling_position(
+ wit: *const u8,
+ wit_len: usize,
+ ) -> u64 {
+ let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW);
+ let wit_reader = Cursor::new(wit_in);
+ let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap();
+ let pos = iw.witnessed_position();
+ return u64::from(pos);
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_update_sapling_witness(
+ wit: *const u8,
+ wit_len: usize,
+ cmus: *const u8,
+ cmus_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ) {
+ let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW);
+ let wit_reader = Cursor::new(wit_in);
+ let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap();
+ let cmu: Vec> = marshall_from_haskell_var(cmus, cmus_len, RW);
+ for c in cmu {
+ let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(c));
+ if sap_note_comm.is_some().into() {
+ let n = Node::from_cmu(&sap_note_comm.unwrap());
+ iw.append(n);
+ }
+ }
+ let mut out_bytes: Vec = Vec::new();
+ let result = write_incremental_witness(&iw, &mut out_bytes);
+ match result {
+ Ok(()) => {
+ let h = Hhex { bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_read_orchard_commitment_tree(
+ tree: *const u8,
+ tree_len: usize,
+ node: *const u8,
+ node_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW);
+ let tree_reader = Cursor::new(tree_in);
+ let ct = read_commitment_tree::>, 32>(tree_reader);
+ match ct {
+ Ok(mut comm_tree) => {
+ let node_in: Vec = marshall_from_haskell_var(node, node_len, RW);
+ let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(node_in));
+ if orchard_note_comm.is_some().into() {
+ let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap());
+ comm_tree.append(n);
+ let mut out_bytes: Vec = Vec::new();
+ let result = write_commitment_tree(&comm_tree, &mut out_bytes );
+ match result {
+ Ok(()) => {
+ let h = Hhex { bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+ } else {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_read_orchard_witness(
+ tree: *const u8,
+ tree_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ){
+ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW);
+ let tree_reader = Cursor::new(tree_in);
+ let ct: CommitmentTree = read_commitment_tree(tree_reader).unwrap();
+ let inc_wit = IncrementalWitness::from_tree(ct);
+ let mut out_bytes: Vec = Vec::new();
+ let result = write_incremental_witness(&inc_wit, &mut out_bytes);
+ match result {
+ Ok(()) => {
+ let h = Hhex { bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+}
+
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_read_orchard_position(
+ wit: *const u8,
+ wit_len: usize,
+ ) -> u64 {
+ let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW);
+ let wit_reader = Cursor::new(wit_in);
+ let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap();
+ let path = iw.path();
+ match path {
+ Some(p) => {
+ let pos = p.position();
+ return u64::from(pos);
+ },
+ None => {
+ return 0;
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_update_orchard_witness(
+ wit: *const u8,
+ wit_len: usize,
+ cm: *const u8,
+ cm_len: usize,
+ out: *mut u8,
+ out_len: &mut usize
+ ) {
+ let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW);
+ let wit_reader = Cursor::new(wit_in);
+ let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap();
+ let cmu: Vec> = marshall_from_haskell_var(cm, cm_len, RW);
+ for c in cmu {
+ let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(c));
+ if orchard_note_comm.is_some().into() {
+ let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap());
+ iw.append(n);
+ }
+ }
+ let mut out_bytes: Vec = Vec::new();
+ let result = write_incremental_witness(&iw, &mut out_bytes);
+ match result {
+ Ok(()) => {
+ let h = Hhex { bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(_e) => {
+ let h0 = Hhex { bytes: vec![0]};
+ marshall_to_haskell_var(&h0, out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_decode_sapling_address(
+ sapling: *const u8,
+ sapling_len: usize,
+ out: *mut u8,
+ out_len: &mut usize){
+ let sapling_address_from_haskell : Vec = marshall_from_haskell_var(sapling, sapling_len, RW);
+ let sapling_address = std::str::from_utf8(&sapling_address_from_haskell).unwrap();
+
+ let mut netid = 0;
+ match sapling_address.find("1") {
+ Some(ix) => {
+ let netstr = &sapling_address[0..ix];
+ if netstr == "zs" {
+ netid = 1
+ } else {
+ if netstr == "ztestsapling" {
+ netid = 2
+ }
+ }
+ match decode_payment_address(netstr, sapling_address) {
+ Ok( t )=> {
+ let address_to_bytes = t.to_bytes();
+ let mut out_bytes_temp : [u8;44] = [0;44];
+ out_bytes_temp[0] = netid;
+ let mut iy = 1;
+ for ix in 0..43 {
+ out_bytes_temp[iy] = address_to_bytes[ix];
+ iy += 1;
+ }
+ let out_bytes: Vec = out_bytes_temp.to_vec();
+ marshall_to_haskell_var(&out_bytes, out, out_len, RW);
+ }
+ Err(_e) => {
+ let h = vec![0];
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ }
+ }
+ }
+ None => {
+ let h = vec![0];
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rust_wrapper_create_transaction(
+ sap_wit: *const u8,
+ sap_wit_len: usize,
+ orch_wit: *const u8,
+ orch_wit_len: usize,
+ t_input: *const u8,
+ t_input_len: usize,
+ s_input: *const u8,
+ s_input_len: usize,
+ o_input: *const u8,
+ o_input_len: usize,
+ out_list: *const u8,
+ out_list_len: usize,
+ sapspend: *const u8,
+ sapspend_len: usize,
+ sapoutput: *const u8,
+ sapoutput_len: usize,
+ net: bool,
+ bl_height: u32,
+ build: bool,
+ out: *mut u8,
+ out_len: &mut usize){
+ let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW);
+ let sap_wit_reader = Cursor::new(sap_wit_in);
+ let sap_iw = read_commitment_tree::>, SAPLING_DEPTH>(sap_wit_reader);
+ let sap_anchor = match sap_iw {
+ Ok(s_iw) => {
+ Some(SaplingAnchor::from(s_iw.root()))
+ },
+ Err(_e) => {
+ None
+ }
+ };
+ //println!("{:?}", sap_anchor);
+ let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW);
+ let orch_wit_reader = Cursor::new(orch_wit_in);
+ let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader);
+ let orch_anchor = match orch_iw {
+ Ok(o_iw) => {
+ Some(OrchardAnchor::from(o_iw.root()))
+ },
+ Err(_e) => {
+ None
+ }
+ };
+ let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor};
+ let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config);
+ let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config);
+ let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW);
+ for t_in in trans_input {
+ if t_in.sk.len() > 1 {
+ let k = SecretKey::from_slice(&t_in.sk).unwrap();
+ if net {
+ match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) {
+ Ok(()) => {
+ //println!("added t-input in main");
+ continue;
+ },
+ Err(_e) => { println!("Error reading transparent input"); }
+ }
+ } else {
+ match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) {
+ Ok(()) => {
+ //println!("added t-input in test");
+ continue;
+ },
+ Err(_e) => { println!("Error reading transparent input"); }
+ }
+ }
+ }
+ }
+ let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW);
+ for s_in in sap_input {
+ if s_in.sk.len() > 1 {
+ let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk);
+ match sp_key {
+ Ok(sk) => {
+ let pay_addr = PaymentAddress::from_bytes(&to_array(s_in.note.recipient)).unwrap();
+ let rseed = if s_in.note.rseed.kind == 1 {
+ Rseed::BeforeZip212(Fr::from_bytes(&to_array(s_in.note.rseed.bytes)).unwrap())
+ } else {
+ Rseed::AfterZip212(to_array(s_in.note.rseed.bytes))
+ };
+ let note = SaplingNote::from_parts(pay_addr, SaplingNoteValue::from_raw(s_in.note.note), rseed);
+ let wit_reader = Cursor::new(s_in.iw);
+ let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap();
+ let merkle_path = iw.path().unwrap();
+ if net {
+ let _mb = main_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap();
+ } else {
+ let _tb = test_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap();
+ }
+ },
+ Err(_e) => {
+ continue;
+ }
+ }
+ }
+ }
+ let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW);
+ for o_in in orch_input {
+ if o_in.sk.len() > 1 {
+ let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap();
+ let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap();
+ let rho = Nullifier::from_bytes(&to_array(o_in.note.rho)).unwrap();
+ let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap();
+ let val = NoteValue::from_raw(o_in.note.note);
+ let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap();
+ let wit_reader = Cursor::new(o_in.iw);
+ let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap();
+ let merkle_path = OrchardMerklePath::from(iw.path().unwrap());
+ if net {
+ let _mb = main_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap();
+ } else {
+ let _tb = test_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap();
+ }
+ }
+ }
+ let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW);
+ for output in outputs {
+ match output.kind {
+ 1 => {
+ let recipient = TransparentAddress::PublicKeyHash(to_array(output.to));
+ let val = NonNegativeAmount::from_u64(output.amt).unwrap();
+ if net {
+ let _mb = main_builder.add_transparent_output(&recipient, val);
+ } else {
+ let _tb = test_builder.add_transparent_output(&recipient, val);
+ }
+ },
+ 2 => {
+ let recipient = TransparentAddress::ScriptHash(to_array(output.to));
+ let val = NonNegativeAmount::from_u64(output.amt).unwrap();
+ if net {
+ let _mb = main_builder.add_transparent_output(&recipient, val);
+ } else {
+ let _tb = test_builder.add_transparent_output(&recipient, val);
+ }
+ },
+ 3 => {
+ let ovk = Some(ExpandedSpendingKey::from_spending_key(&output.ovk).ovk);
+ let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap();
+ let val = NonNegativeAmount::from_u64(output.amt).unwrap();
+ let memo = MemoBytes::from_bytes(&output.memo).unwrap();
+ if net {
+ let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo);
+ } else {
+ let _tb = test_builder.add_sapling_output::(ovk, recipient, val, memo);
+ }
+ },
+ 4 => {
+ let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap();
+ let ovk = if output.chg {
+ Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal))
+ } else {
+ Some(FullViewingKey::from(&sk).to_ovk(Scope::External))
+ };
+ let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap();
+ let val = output.amt;
+ let memo = MemoBytes::from_bytes(&output.memo).unwrap();
+ if net {
+ let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo);
+ } else {
+ let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo);
+ }
+ },
+ _ => {
+ continue;
+ }
+ }
+ }
+ if build {
+ let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW);
+ let spend_params_reader = Cursor::new(spend_params_in);
+ let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap();
+ let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW);
+ let output_params_reader = Cursor::new(output_params_in);
+ let output_prover = OutputParameters::read(output_params_reader, false).unwrap();
+ let result = if net {
+ main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard())
+ } else {
+ test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard())
+ };
+ match result {
+ Ok(r) => {
+ let mut out_bytes: Vec = Vec::new();
+ let _t = r.transaction().write_v5(&mut out_bytes);
+ let h = Hhex {bytes: out_bytes};
+ marshall_to_haskell_var(&h, out, out_len, RW);
+ },
+ Err(e) => {
+ match e {
+ Error::InsufficientFunds(y) => {
+ let x = Hhex {bytes: vec![0]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::ChangeRequired(y1) => {
+ let x = Hhex {bytes: vec![1]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::Fee(_y2) => {
+ let x = Hhex {bytes: vec![2]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::Balance(y3) => {
+ let x = Hhex {bytes: vec![3]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::TransparentBuild(y4) => {
+ let x = Hhex {bytes: vec![4]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::SaplingBuild(y5) => {
+ let x = Hhex {bytes: vec![5]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::OrchardBuild(y7) => {
+ let x = Hhex {bytes: vec![6]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::OrchardSpend(y8) => {
+ let x = Hhex {bytes: vec![7]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::OrchardRecipient(y9) => {
+ let x = Hhex {bytes: vec![8]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::SaplingBuilderNotAvailable => {
+ let x = Hhex {bytes: vec![9]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Error::OrchardBuilderNotAvailable => {
+ let x = Hhex {bytes: vec![10]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ }
+ }
+ }
+ }
+ } else {
+ let result = if net {
+ main_builder.get_fee(&FeeRule::standard())
+ } else {
+ test_builder.get_fee(&FeeRule::standard())
+ };
+ match result {
+ Ok(r) => {
+ let x = Hhex {bytes: r.to_i64_le_bytes().to_vec()};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ },
+ Err(e) => {
+ let x = Hhex {bytes: vec![2]};
+ marshall_to_haskell_var(&x, out, out_len, RW);
+ }
+ }
+ }
+}
diff --git a/package.yaml b/package.yaml
deleted file mode 100644
index 4856888..0000000
--- a/package.yaml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: zcash-haskell
-version: 0.3.0
-git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell"
-license: LGPL-3
-author: "Rene Vergara"
-maintainer: "pitmutt@vergara.tech"
-copyright: "(c)2022-2024 Vergara Technologies LLC"
-
-extra-source-files:
-- README.md
-- CHANGELOG.md
-- configure
-
-# Metadata used when publishing your package
-synopsis: Utilities to interact with the Zcash blockchain
-category: Blockchain
-
-# To avoid duplicated efforts in documentation and dealing with the
-# complications of embedding Haddock markup inside cabal files, it is
-# common to point users to the README.md file.
-description: Please see the README on the repo at
-
-dependencies:
-- base >= 4.7 && < 5
-
-library:
- source-dirs: src
- dependencies:
- - bytestring
- - borsh >= 0.2
- - text
- - foreign-rust
- - generics-sop
- - aeson
- - http-conduit
- - base58-bytestring
- - cryptonite
- - memory
- pkg-config-dependencies:
- - rustzcash_wrapper-uninstalled
-
-tests:
- zcash-haskell-test:
- main: Spec.hs
- source-dirs: test
- ghc-options:
- - -threaded
- - -rtsopts
- - -with-rtsopts=-N
- dependencies:
- - zcash-haskell
- - hspec
- - bytestring
- - text
- - aeson
diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs
index ebda582..0f35be4 100644
--- a/src/C/Zcash.chs
+++ b/src/C/Zcash.chs
@@ -1,19 +1,7 @@
{- Copyright 2022-2024 Vergara Technologies LLC
This file is part of Zcash-Haskell.
-
- Zcash-Haskell is free software: you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the Free
- Software Foundation, either version 3 of the License, or (at your option) any
- later version.
-
- Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- details.
- You should have received a copy of the GNU Lesser General Public License along with
- Zcash-Haskell. If not, see .
-}
{-# LANGUAGE DerivingStrategies #-}
@@ -29,10 +17,11 @@ module C.Zcash where
import qualified Data.ByteString as BS
import Codec.Borsh
-import Data.Text (Text)
+import qualified Data.Text as T
import Data.Word
import Data.Int
import Data.Structured
+import Data.HexString (HexString(..))
import Foreign.C.Types
import Foreign.Rust.Marshall.External
import Foreign.Rust.Marshall.Fixed
@@ -43,6 +32,20 @@ import qualified Generics.SOP as SOP
import qualified GHC.Generics as GHC
import ZcashHaskell.Types
+{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer RawData'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_bech32m_encode as rustWrapperBech32mEncode
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer (T.Text)'&
+ }
+ -> `()'
+#}
{# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble
{ toBorshVar* `BS.ByteString'&
@@ -71,13 +74,6 @@ import ZcashHaskell.Types
-> `Bool'
#}
-{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
- { toBorshVar* `BS.ByteString'&
- , getVarBuffer `Buffer RawData'&
- }
- -> `()'
-#}
-
{# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode
{ toBorshVar* `BS.ByteString'&
}
@@ -100,12 +96,23 @@ import ZcashHaskell.Types
{# fun unsafe rust_wrapper_sapling_note_decrypt_v2 as rustWrapperSaplingNoteDecode
{ toBorshVar* `BS.ByteString'&
- , toBorshVar* `BS.ByteString'&
+ , toBorshVar* `ShieldedOutput'&
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
+{# fun unsafe rust_wrapper_sapling_esk_decrypt as rustWrapperSaplingDecodeEsk
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `ShieldedOutput'&
+ , `Bool'
+ , `Bool'
+ , `Word64'
+ , getVarBuffer `Buffer DecodedNote'&
+ }
+ -> `()'
+#}
+
{# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer UnifiedFullViewingKey'&
@@ -121,9 +128,167 @@ import ZcashHaskell.Types
-> `()'
#}
-{# fun unsafe rust_wrapper_tx_parse as rustWrapperTxParse
+{# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK
{ toBorshVar* `BS.ByteString'&
- , getVarBuffer `Buffer [BS.ByteString]'&
+ , toBorshVar* `OrchardAction'&
+ , `Bool'
+ , getVarBuffer `Buffer DecodedNote'&
+ }
+ -> `()'
+#}
+{# fun unsafe rust_wrapper_tx_parse as rustWrapperTxParse
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer [ShieldedOutput]'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_tx_read as rustWrapperTxRead
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer RawZebraTx'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase
+ { getVarBuffer `Buffer Phrase'& } -> `()'
+#}
+
+{# fun unsafe rust_wrapper_recover_seed as rustWrapperGetSeed
+ { toBorshVar* `Phrase'&
+ , getVarBuffer `Buffer Seed'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey
+ { toBorshVar* `BS.ByteString'&
+ , `Word32'
+ , `Word32'
+ , getVarBuffer `Buffer (BS.ByteString)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_sapling_paymentaddress as rustWrapperSaplingPaymentAddress
+ { toBorshVar* `BS.ByteString'&
+ , `Word32'
+ , getVarBuffer `Buffer (BS.ByteString)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_sapling_chgpaymentaddress as rustWrapperSaplingChgPaymentAddress
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer (BS.ByteString)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_derive_orchard_spending_key as rustWrapperGenOrchardSpendKey
+ { toBorshVar* `BS.ByteString'&
+ , `Word32'
+ , `Word32'
+ , getVarBuffer `Buffer (BS.ByteString)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_derive_orchard_receiver as rustWrapperGenOrchardReceiver
+ { toBorshVar* `BS.ByteString'&
+ , `Word32'
+ , `Bool'
+ , getVarBuffer `Buffer (BS.ByteString)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer HexString'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_read_sapling_witness as rustWrapperReadSaplingWitness
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer HexString'&
+ }
+ -> `()'
+#}
+
+{# fun pure unsafe rust_wrapper_read_sapling_position as rustWrapperReadSaplingPosition
+ { toBorshVar* `BS.ByteString'&
+ }
+ -> `Word64'
+#}
+
+{# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer (T.Text)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_decode_sapling_address as rustWrapperDecodeSaplingAddress
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer (BS.ByteString)'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer HexString'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_read_orchard_witness as rustWrapperReadOrchardWitness
+ { toBorshVar* `BS.ByteString'&
+ , getVarBuffer `Buffer HexString'&
+ }
+ -> `()'
+#}
+
+{# fun pure unsafe rust_wrapper_read_orchard_position as rustWrapperReadOrchardPosition
+ { toBorshVar* `BS.ByteString'&
+ }
+ -> `Word64'
+#}
+
+{# fun unsafe rust_wrapper_update_sapling_witness as rustWrapperUpdateSaplingWitness
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `[BS.ByteString]'&
+ , getVarBuffer `Buffer HexString'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_update_orchard_witness as rustWrapperUpdateOrchardWitness
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `[BS.ByteString]'&
+ , getVarBuffer `Buffer HexString'&
+ }
+ -> `()'
+#}
+
+{# fun unsafe rust_wrapper_create_transaction as rustWrapperCreateTx
+ { toBorshVar* `BS.ByteString'&
+ , toBorshVar* `BS.ByteString'&
+ , toBorshVar* `[TransparentTxSpend]'&
+ , toBorshVar* `[SaplingTxSpend]'&
+ , toBorshVar* `[OrchardTxSpend]'&
+ , toBorshVar* `[OutgoingNote]'&
+ , toBorshVar* `BS.ByteString'&
+ , toBorshVar* `BS.ByteString'&
+ , `Bool'
+ , `Word64'
+ , `Bool'
+ , getVarBuffer `Buffer HexString'&
}
-> `()'
#}
diff --git a/src/ZcashHaskell/Keys.hs b/src/ZcashHaskell/Keys.hs
new file mode 100644
index 0000000..30484e7
--- /dev/null
+++ b/src/ZcashHaskell/Keys.hs
@@ -0,0 +1,38 @@
+-- Copyright 2022-2024 Vergara Technologies LLC
+-- This file is part of Zcash-Haskell.
+--
+-- |
+-- Module : ZcashHaskell.Keys
+-- Copyright : 2022-2024 Vergara Technologies
+-- License : MIT
+--
+-- Maintainer : pitmutt@vergara.tech
+-- Stability : experimental
+-- Portability : unknown
+--
+-- Functions to generate keys for the Zcash blockchain
+--
+module ZcashHaskell.Keys where
+
+import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
+import qualified Data.ByteString as BS
+import qualified Data.Text as T
+import Foreign.Rust.Marshall.Variable
+ ( withBorshVarBuffer
+ , withPureBorshVarBuffer
+ )
+import ZcashHaskell.Types (Phrase, Seed(..), ToBytes(..))
+
+-- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses.
+generateWalletSeedPhrase :: IO Phrase
+generateWalletSeedPhrase = withBorshVarBuffer rustWrapperGenSeedPhrase
+
+-- | Derive a cryptographic seed from the given seed phrase.
+getWalletSeed :: Phrase -> Maybe Seed
+getWalletSeed p =
+ if BS.length (getBytes result) > 0
+ then Just result
+ else Nothing
+ where
+ result :: Seed
+ result = (withPureBorshVarBuffer . rustWrapperGetSeed) p
diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs
index 44d17a5..d7c3e38 100644
--- a/src/ZcashHaskell/Orchard.hs
+++ b/src/ZcashHaskell/Orchard.hs
@@ -1,24 +1,13 @@
-{- Copyright 2022-2024 Vergara Technologies LLC
+{-# LANGUAGE OverloadedStrings #-}
- This file is part of Zcash-Haskell.
-
- Zcash-Haskell is free software: you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the Free
- Software Foundation, either version 3 of the License, or (at your option) any
- later version.
-
- Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- details.
-
- You should have received a copy of the GNU Lesser General Public License along with
- Zcash-Haskell. If not, see .
--}
+-- Copyright 2022-2024 Vergara Technologies LLC
+--
+-- This file is part of Zcash-Haskell.
+--
-- |
-- Module : ZcashHaskell.Orchard
-- Copyright : 2022-2024 Vergara Technologies
--- License : LGPL-3
+-- License : MIT
--
-- Maintainer : rene@vergara.network
-- Stability : experimental
@@ -29,14 +18,63 @@
module ZcashHaskell.Orchard where
import C.Zcash
- ( rustWrapperOrchardCheck
+ ( rustWrapperGenOrchardReceiver
+ , rustWrapperGenOrchardSpendKey
+ , rustWrapperOrchardCheck
, rustWrapperOrchardNoteDecode
+ , rustWrapperOrchardNoteDecodeSK
+ , rustWrapperReadOrchardCommitmentTree
+ , rustWrapperReadOrchardPosition
+ , rustWrapperReadOrchardWitness
, rustWrapperUADecode
, rustWrapperUfvkDecode
+ , rustWrapperUpdateOrchardWitness
)
import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as C
+import Data.HexString (HexString(..), fromRawBytes, toBytes)
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as E
+import Data.Word
import Foreign.Rust.Marshall.Variable
import ZcashHaskell.Types
+import ZcashHaskell.Utils (encodeBech32, encodeBech32m, f4Jumble)
+
+-- | Derives an Orchard spending key for the given seed and account ID
+genOrchardSpendingKey ::
+ Seed -- ^ The cryptographic seed for the wallet
+ -> CoinType -- ^ The coin type constant
+ -> AccountId -- ^ The index of the account to be used
+ -> Maybe OrchardSpendingKey
+genOrchardSpendingKey s coinType accountId =
+ if BS.length k /= 32
+ then Nothing
+ else Just $ OrchardSpendingKey k
+ where
+ k =
+ withPureBorshVarBuffer $
+ rustWrapperGenOrchardSpendKey
+ (getBytes s)
+ (getValue coinType)
+ (fromIntegral accountId)
+
+-- | Derives an Orchard receiver for the given spending key and index
+genOrchardReceiver ::
+ Int -- ^ The index of the address to be created
+ -> Scope -- ^ `External` for wallet addresses, `Internal` for change addresses
+ -> OrchardSpendingKey -- ^ The spending key
+ -> Maybe OrchardReceiver
+genOrchardReceiver i scope osk =
+ if BS.length k /= 43
+ then Nothing
+ else Just $ OrchardReceiver k
+ where
+ k =
+ withPureBorshVarBuffer $
+ rustWrapperGenOrchardReceiver
+ (getBytes osk)
+ (fromIntegral i)
+ (scope == External)
-- | Checks if given bytestring is a valid encoded unified address
isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress
@@ -54,14 +92,48 @@ isValidUnifiedAddress str =
makeUA x =
UnifiedAddress
whichNet
- (raw_o x)
- (raw_s x)
+ (if BS.length (raw_o x) == 43
+ then Just $ OrchardReceiver (raw_o x)
+ else Nothing)
+ (if BS.length (raw_s x) == 43
+ then Just $ SaplingReceiver (raw_s x)
+ else Nothing)
(if not (BS.null (raw_t x))
- then Just $ TransparentAddress P2PKH whichNet (raw_t x)
+ then Just $ TransparentReceiver P2PKH (fromRawBytes $ raw_t x)
else if not (BS.null (raw_to x))
- then Just $ TransparentAddress P2SH whichNet (raw_to x)
+ then Just $ TransparentReceiver P2SH (fromRawBytes $ raw_to x)
else Nothing)
+-- | Encode a 'UnifiedAddress' per [ZIP-316](https://zips.z.cash/zip-0316)
+encodeUnifiedAddress :: UnifiedAddress -> T.Text
+encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b
+ where
+ hr =
+ case ua_net ua of
+ MainNet -> uniPaymentAddressHrp
+ TestNet -> uniTestPaymentAddressHrp
+ b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding
+ tReceiver =
+ case t_rec ua of
+ Nothing -> BS.empty
+ Just t ->
+ case tr_type t of
+ P2SH -> packReceiver 0x01 $ Just $ toBytes $ tr_bytes t
+ P2PKH -> packReceiver 0x00 $ Just $ toBytes $ tr_bytes t
+ sReceiver = packReceiver 0x02 $ getBytes <$> s_rec ua
+ oReceiver = packReceiver 0x03 $ getBytes <$> o_rec ua
+ padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr
+ packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString
+ packReceiver typeCode receiver' =
+ case receiver' of
+ Just receiver ->
+ if BS.length receiver > 1
+ then BS.singleton typeCode `BS.append`
+ (BS.singleton . toEnum . BS.length) receiver `BS.append`
+ receiver
+ else BS.empty
+ Nothing -> BS.empty
+
-- | Attempts to decode the given bytestring into a Unified Full Viewing Key
decodeUfvk :: BS.ByteString -> Maybe UnifiedFullViewingKey
decodeUfvk str =
@@ -86,3 +158,71 @@ decryptOrchardAction key encAction =
decodedAction =
withPureBorshVarBuffer $
rustWrapperOrchardNoteDecode (o_key key) encAction
+
+getSaplingFromUA :: BS.ByteString -> Maybe T.Text
+getSaplingFromUA uadd = do
+ let a = isValidUnifiedAddress uadd
+ case a of
+ Nothing -> Nothing
+ Just a -> do
+ let sraw = s_rec a
+ case sraw of
+ Nothing -> Nothing
+ Just sraw -> do
+ let net = ua_net a
+ case net of
+ MainNet ->
+ Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sraw)
+ TestNet ->
+ Just $
+ encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sraw)
+
+-- | Attemtps to decode the given @OrchardAction@ using the given @OrchardSpendingKey@
+decryptOrchardActionSK ::
+ OrchardSpendingKey -> Scope -> OrchardAction -> Maybe DecodedNote
+decryptOrchardActionSK sk scope oa =
+ case a_value decodedAction of
+ 0 -> Nothing
+ _ -> Just decodedAction
+ where
+ decodedAction =
+ withPureBorshVarBuffer $
+ rustWrapperOrchardNoteDecodeSK (getBytes sk) oa (scope == External)
+
+-- | Update a Orchard commitment tree
+updateOrchardCommitmentTree ::
+ OrchardCommitmentTree -- ^ the base tree
+ -> HexString -- ^ the new note commitment
+ -> Maybe OrchardCommitmentTree
+updateOrchardCommitmentTree tree cmx =
+ if BS.length (hexBytes updatedTree) > 1
+ then Just $ OrchardCommitmentTree updatedTree
+ else Nothing
+ where
+ updatedTree =
+ withPureBorshVarBuffer $
+ rustWrapperReadOrchardCommitmentTree
+ (hexBytes $ orchTree tree)
+ (hexBytes cmx)
+
+-- | Get the Orchard incremental witness from a commitment tree
+getOrchardWitness :: OrchardCommitmentTree -> Maybe OrchardWitness
+getOrchardWitness tree =
+ if BS.length (hexBytes wit) > 1
+ then Just $ OrchardWitness wit
+ else Nothing
+ where
+ wit =
+ withPureBorshVarBuffer $
+ rustWrapperReadOrchardWitness (hexBytes $ orchTree tree)
+
+-- | Get the Sapling note position from a witness
+getOrchardNotePosition :: OrchardWitness -> Integer
+getOrchardNotePosition =
+ fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit
+
+updateOrchardWitness :: OrchardWitness -> [HexString] -> OrchardWitness
+updateOrchardWitness wit cmus =
+ OrchardWitness $
+ withPureBorshVarBuffer $
+ rustWrapperUpdateOrchardWitness (toBytes $ orchWit wit) (map toBytes cmus)
diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs
index 2847529..5ba501e 100644
--- a/src/ZcashHaskell/Sapling.hs
+++ b/src/ZcashHaskell/Sapling.hs
@@ -1,61 +1,66 @@
-{- Copyright 2022-2024 Vergara Technologies LLC
-
- This file is part of Zcash-Haskell.
-
- Zcash-Haskell is free software: you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the Free
- Software Foundation, either version 3 of the License, or (at your option) any
- later version.
-
- Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- details.
-
- You should have received a copy of the GNU Lesser General Public License along with
- Zcash-Haskell. If not, see .
-
--}
-{-# LANGUAGE OverloadedStrings #-}
-
+-- Copyright 2022-2024 Vergara Technologies LLC
+--
+-- This file is part of Zcash-Haskell.
+--
-- |
-- Module : ZcashHaskell.Sapling
-- Copyright : 2022-2024 Vergara Technologies
--- License : LGPL-3
+-- License : MIT
--
--- Maintainer : rene@vergara.network
+-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
-- Functions to interact with the Sapling shielded pool of the Zcash blockchain.
--
+{-# LANGUAGE OverloadedStrings #-}
+
module ZcashHaskell.Sapling where
import C.Zcash
- ( rustWrapperIsShielded
+ ( rustWrapperDecodeSaplingAddress
+ , rustWrapperIsShielded
+ , rustWrapperReadSaplingCommitmentTree
+ , rustWrapperReadSaplingPosition
+ , rustWrapperReadSaplingWitness
, rustWrapperSaplingCheck
+ , rustWrapperSaplingChgPaymentAddress
+ , rustWrapperSaplingDecodeEsk
, rustWrapperSaplingNoteDecode
+ , rustWrapperSaplingPaymentAddress
+ , rustWrapperSaplingSpendingkey
, rustWrapperSaplingVkDecode
, rustWrapperTxParse
+ , rustWrapperUpdateSaplingWitness
)
import Data.Aeson
import qualified Data.ByteString as BS
-import Foreign.Rust.Marshall.Variable (withPureBorshVarBuffer)
-import ZcashHaskell.Types
- ( DecodedNote(..)
- , RawData(..)
- , RawTxResponse(..)
- , ShieldedOutput(..)
- , decodeHexText
+import qualified Data.ByteString.Char8 as C
+import Data.HexString (HexString(..), fromText, hexString, toBytes, toText)
+import qualified Data.Text as T
+import Data.Word
+import Foreign.Rust.Marshall.Variable
+ ( withPureBorshVarBuffer
+ , withPureBorshVarBuffer
)
-import ZcashHaskell.Utils (decodeBech32)
+import ZcashHaskell.Types
+import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m)
-- | Check if given bytesting is a valid encoded shielded address
isValidShieldedAddress :: BS.ByteString -> Bool
isValidShieldedAddress = rustWrapperIsShielded
-getShieldedOutputs :: BS.ByteString -> [BS.ByteString]
-getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse t
+getShieldedOutputs :: HexString -> [ShieldedOutput]
+getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t
+
+serializeShieldedOutput :: ShieldedOutput -> BS.ByteString
+serializeShieldedOutput so =
+ hexBytes . fromText $
+ toText (s_cv so) <>
+ toText (s_cmu so) <>
+ toText (s_ephKey so) <>
+ toText (s_encCipherText so) <>
+ toText (s_outCipherText so) <> toText (s_proof so)
-- | Check if given bytestring is a valid Sapling viewing key
isValidSaplingViewingKey :: BS.ByteString -> Bool
@@ -71,7 +76,7 @@ matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool
matchSaplingAddress = rustWrapperSaplingCheck
-- | Attempt to decode the given raw tx with the given Sapling viewing key
-decodeSaplingOutput :: BS.ByteString -> BS.ByteString -> Maybe DecodedNote
+decodeSaplingOutput :: BS.ByteString -> ShieldedOutput -> Maybe DecodedNote
decodeSaplingOutput key out =
case a_value decodedAction of
0 -> Nothing
@@ -89,25 +94,136 @@ instance FromJSON RawTxResponse where
ht <- obj .: "height"
c <- obj .: "confirmations"
b <- obj .: "blocktime"
+ sSpend <- obj .: "vShieldedSpend"
case o of
Nothing ->
- pure $
- RawTxResponse
- i
- (decodeHexText h)
- (getShieldedOutputs (decodeHexText h))
- []
- ht
- c
- b
+ pure $ RawTxResponse i h sSpend (getShieldedOutputs h) [] ht c b
Just o' -> do
a <- o' .: "actions"
- pure $
- RawTxResponse
- i
- (decodeHexText h)
- (getShieldedOutputs (decodeHexText h))
- a
- ht
- c
- b
+ pure $ RawTxResponse i h sSpend (getShieldedOutputs h) a ht c b
+
+-- | Attempt to decode the given raw tx with the given Sapling spending key
+decodeSaplingOutputEsk ::
+ SaplingSpendingKey
+ -> ShieldedOutput
+ -> ZcashNet
+ -> Scope
+ -> Integer
+ -> Maybe DecodedNote
+decodeSaplingOutputEsk key out znet scope pos =
+ case a_value decodedAction of
+ 0 -> Nothing
+ _ -> Just decodedAction
+ where
+ decodedAction =
+ withPureBorshVarBuffer $
+ rustWrapperSaplingDecodeEsk
+ (getBytes key)
+ out
+ (scope == External)
+ (znet == MainNet)
+ (fromIntegral pos)
+
+-- | Attempts to obtain a sapling SpendingKey using a HDSeed
+genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey
+genSaplingSpendingKey seed c i = do
+ if BS.length res == 169
+ then Just $ SaplingSpendingKey res
+ else Nothing
+ where
+ res =
+ withPureBorshVarBuffer
+ (rustWrapperSaplingSpendingkey
+ (getBytes seed)
+ (fromIntegral $ getValue c)
+ (fromIntegral i))
+
+-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index
+genSaplingPaymentAddress :: Int -> SaplingSpendingKey -> Maybe SaplingReceiver
+genSaplingPaymentAddress i extspk =
+ if BS.length res == 43
+ then Just $ SaplingReceiver res
+ else Nothing
+ where
+ res =
+ withPureBorshVarBuffer
+ (rustWrapperSaplingPaymentAddress
+ (getBytes extspk)
+ (fromIntegral (i * 111)))
+
+-- | Generate an internal Sapling address
+genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver
+genSaplingInternalAddress sk =
+ if BS.length res == 43
+ then Just $ SaplingReceiver res
+ else Nothing
+ where
+ res =
+ withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk)
+
+-- | Update a Sapling commitment tree
+updateSaplingCommitmentTree ::
+ SaplingCommitmentTree -- ^ the base tree
+ -> HexString -- ^ the new note commitment
+ -> Maybe SaplingCommitmentTree
+updateSaplingCommitmentTree tree cmu =
+ if BS.length (hexBytes updatedTree) > 1
+ then Just $ SaplingCommitmentTree updatedTree
+ else Nothing
+ where
+ updatedTree =
+ withPureBorshVarBuffer $
+ rustWrapperReadSaplingCommitmentTree
+ (hexBytes $ sapTree tree)
+ (hexBytes cmu)
+
+-- | Get the Sapling incremental witness from a commitment tree
+getSaplingWitness :: SaplingCommitmentTree -> Maybe SaplingWitness
+getSaplingWitness tree =
+ if BS.length (hexBytes wit) > 1
+ then Just $ SaplingWitness wit
+ else Nothing
+ where
+ wit =
+ withPureBorshVarBuffer $
+ rustWrapperReadSaplingWitness (hexBytes $ sapTree tree)
+
+-- | Get the Sapling note position from a witness
+getSaplingNotePosition :: SaplingWitness -> Integer
+getSaplingNotePosition =
+ fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit
+
+updateSaplingWitness :: SaplingWitness -> [HexString] -> SaplingWitness
+updateSaplingWitness wit cmus =
+ SaplingWitness $
+ withPureBorshVarBuffer $
+ rustWrapperUpdateSaplingWitness (toBytes $ sapWit wit) (map toBytes cmus)
+
+-- | Encode a SaplingReceiver into HRF text
+encodeSaplingAddress :: ZcashNet -> SaplingReceiver -> Maybe T.Text
+encodeSaplingAddress net sr = do
+ case net of
+ MainNet -> Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sr)
+ TestNet ->
+ Just $ encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sr)
+
+-- | Helper to get de Nework Id from FFI response
+getNetId :: [Word8] -> ZcashNet
+getNetId [x] = do
+ case x of
+ 1 -> MainNet
+ 2 -> TestNet
+
+-- | decode a Sapling address
+decodeSaplingAddress :: BS.ByteString -> Maybe SaplingAddress
+decodeSaplingAddress sapling_address = do
+ if BS.length sa > 1
+ then do
+ let sa0 = BS.unpack sa
+ Just $
+ SaplingAddress (getNetId (take 1 sa0)) $
+ SaplingReceiver (BS.pack (drop 1 sa0))
+ else Nothing
+ where
+ sa =
+ withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address
diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs
index bbc10d6..878a7d8 100644
--- a/src/ZcashHaskell/Transparent.hs
+++ b/src/ZcashHaskell/Transparent.hs
@@ -1,27 +1,11 @@
-{- Copyright 2022-2024 Vergara Technologies LLC
-
- This file is part of Zcash-Haskell.
-
- Zcash-Haskell is free software: you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the Free
- Software Foundation, either version 3 of the License, or (at your option) any
- later version.
-
- Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- details.
-
- You should have received a copy of the GNU Lesser General Public License along with
- Zcash-Haskell. If not, see .
-
--}
-{-# LANGUAGE OverloadedStrings #-}
-
+-- Copyright 2022-2024 Vergara Technologies LLC
+--
+-- This file is part of Zcash-Haskell.
+--
-- |
-- Module : ZcashHaskell.Transparent
-- Copyright : 2022-2024 Vergara Technologies
--- License : LGPL-3
+-- License : MIT
--
-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
@@ -31,4 +15,198 @@
--
module ZcashHaskell.Transparent where
+import Control.Exception (throwIO)
+import Crypto.Hash
+import Crypto.Secp256k1
+import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
+import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58)
+import qualified Data.ByteString.Char8 as BC
+import Data.Char (chr)
+import Data.HexString
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as E
+import Data.Word
+import Haskoin.Address (Address(..))
+import qualified Haskoin.Crypto.Hash as H
+import Haskoin.Crypto.Keys.Extended
+import ZcashHaskell.Types
+
+-- ( AccountId
+-- , CoinType(..)
+-- , Scope(..)
+-- , Seed(..)
+-- , ToBytes(..)
+-- , TransparentAddress(..)
+-- , TransparentReceiver(..)
+-- , TransparentSpendingKey(..)
+-- , TransparentType(..)
+-- , ZcashNet(..)
+-- , getTransparentPrefix
+-- , getValue
+-- )
+import ZcashHaskell.Utils (decodeBech32, encodeBech32m)
+
+-- | Required for `TransparentReceiver` encoding and decoding
+sha256 :: BS.ByteString -> BS.ByteString
+sha256 bs = BA.convert (hash bs :: Digest SHA256)
+
+-- | Encodes a `TransparentReceiver` into the human-readable format per the Zcash Protocol section 5.6.1.1
+encodeTransparentReceiver ::
+ ZcashNet -- ^ The network, `MainNet` or `TestNet`
+ -> TransparentReceiver -- ^ The address to encode
+ -> T.Text
+encodeTransparentReceiver zNet t =
+ encodeTransparent' (getTransparentPrefix zNet (tr_type t)) $
+ toBytes $ tr_bytes t
+ where
+ encodeTransparent' :: (Word8, Word8) -> BS.ByteString -> T.Text
+ encodeTransparent' (a, b) h =
+ E.decodeUtf8 $ encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum
+ where
+ digest = BS.pack [a, b] <> h
+ checksum = sha256 $ sha256 digest
+
+-- | Generate an Extended Private Key from a known HDSeed.
+genTransparentPrvKey ::
+ Seed -- ^ The cryptographic seed of the wallet
+ -> CoinType -- ^ The coin type constant to be used
+ -> AccountId -- ^ The index of the account to be used
+ -> IO TransparentSpendingKey
+genTransparentPrvKey hdseed ctype accid = do
+ let coin = getValue ctype
+ ioCtx <- createContext
+ let path = Deriv :| 44 :| coin :| fromIntegral accid :: DerivPath
+ let prvKey = makeXPrvKey $ getBytes hdseed
+ return $ derivePath ioCtx path prvKey
+
+-- | Generate a transparent receiver
+genTransparentReceiver ::
+ Int -- ^ The index of the address to be created
+ -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses
+ -> XPrvKey -- ^ The transparent private key
+ -> IO TransparentReceiver
+genTransparentReceiver i scope xprvk = do
+ ioCtx <- createContext
+ let s =
+ case scope of
+ External -> 0
+ Internal -> 1
+ let path = Deriv :/ s :/ fromIntegral i :: DerivPath
+ let childPrvKey = derivePath ioCtx path xprvk
+ let childPubKey = deriveXPubKey ioCtx childPrvKey
+ let x = xPubAddr ioCtx childPubKey
+ case x of
+ PubKeyAddress k -> return $ TransparentReceiver P2PKH $ fromBinary k
+ ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j
+ _anyOtherKind -> throwIO $ userError "Unsupported transparent address type"
+
+-- | Generate a transparent receiver
+genTransparentSecretKey ::
+ Int -- ^ The index of the address to be created
+ -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses
+ -> XPrvKey -- ^ The transparent private key
+ -> IO TransparentSpendingKey
+genTransparentSecretKey i scope xprvk = do
+ ioCtx <- createContext
+ let s =
+ case scope of
+ External -> 0
+ Internal -> 1
+ let path = Deriv :/ s :/ fromIntegral i :: DerivPath
+ return $ derivePath ioCtx path xprvk
+
+-- | decode a Transparent Address in HRF and return a TransparentAddress object
+decodeTransparentAddress :: BS.ByteString -> Maybe TransparentAddress
+decodeTransparentAddress taddress = do
+ if BS.length taddress < 34
+ then Nothing -- Not a valid transparent address
+ else do
+ let maybeDecoded = decodeBase58 bitcoinAlphabet taddress
+ case maybeDecoded of
+ Nothing -> Nothing
+ Just decoded -> do
+ let digest = BS.take 22 decoded
+ let chksum = BS.drop 22 decoded
+ let chksumd = BS.take 4 (sha256 $ sha256 digest)
+ if chksum /= chksumd
+ then Nothing -- Invalid address ( invalid checksum )
+ -- build the TransparentAddress Object
+ else do
+ let addressType = BS.take 2 digest
+ let transparentReceiver = BS.drop 2 digest
+ let fb = BS.index addressType 0
+ let sb = BS.index addressType 1
+ case fb of
+ 28 ->
+ case sb of
+ 189 ->
+ Just $
+ TransparentAddress MainNet $
+ TransparentReceiver
+ P2SH
+ (fromRawBytes transparentReceiver)
+ 186 ->
+ Just $
+ TransparentAddress TestNet $
+ TransparentReceiver
+ P2SH
+ (fromRawBytes transparentReceiver)
+ 184 ->
+ Just $
+ TransparentAddress MainNet $
+ TransparentReceiver
+ P2PKH
+ (fromRawBytes transparentReceiver)
+ _ -> Nothing
+ 29 ->
+ if sb == 37
+ then Just $
+ TransparentAddress TestNet $
+ TransparentReceiver
+ P2PKH
+ (fromRawBytes transparentReceiver)
+ else Nothing
+ _ -> Nothing
+
+-- | Encode an Exchange Addresss into HRF from TransparentReceiver
+encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text
+encodeExchangeAddress net tr = do
+ case (tr_type tr) of
+ P2PKH -> do
+ case net of
+ MainNet -> do
+ let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr))
+ Just vhash
+ TestNet -> do
+ let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr))
+ Just vhash
+ _ -> Nothing
+
+-- | Decode an Exchange Address into a ExchangeAddress
+decodeExchangeAddress :: T.Text -> Maybe ExchangeAddress
+decodeExchangeAddress ex = do
+ if (T.length ex) > 1
+ then do
+ let rawd = decodeBech32 (E.encodeUtf8 ex)
+ let tMain = BS.unpack (BC.pack "tex")
+ let tTest = BS.unpack (BC.pack "textest")
+ let tFail = BS.unpack (BC.pack "fail")
+ let hr = BS.unpack (hrp rawd)
+ if hr /= tFail
+ then do
+ let transparentReceiver = bytes rawd
+ if hr == tMain
+ then Just $
+ ExchangeAddress MainNet $
+ TransparentReceiver P2PKH (fromRawBytes transparentReceiver)
+ else do
+ if hr == tTest
+ then Just $
+ ExchangeAddress TestNet $
+ TransparentReceiver
+ P2PKH
+ (fromRawBytes transparentReceiver)
+ else Nothing
+ else Nothing
+ else Nothing
diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs
index 47df822..af60f0f 100644
--- a/src/ZcashHaskell/Types.hs
+++ b/src/ZcashHaskell/Types.hs
@@ -1,74 +1,205 @@
-{- Copyright 2022-2024 Vergara Technologies LLC
-
- This file is part of Zcash-Haskell.
-
- Zcash-Haskell is free software: you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the Free
- Software Foundation, either version 3 of the License, or (at your option) any
- later version.
-
- Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- details.
-
- You should have received a copy of the GNU Lesser General Public License along with
- Zcash-Haskell. If not, see .
-
--}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DerivingVia #-}
-{-# LANGUAGE UndecidableInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-
+-- Copyright 2022-2024 Vergara Technologies LLC
+--
+-- This file is part of Zcash-Haskell.
+--
-- |
-- Module : ZcashHaskell.Types
--- Copyright : Vergara Technologies 2023
--- License : BOSL
+-- Copyright : 2022-2024 Vergara Technologies
+-- License : MIT
--
--- Maintainer : rene@vergara.network
+-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
-- The types used by the ZcashHaskell library to interact with the Zcash blockchain
--
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE GeneralisedNewtypeDeriving #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingVia #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+
module ZcashHaskell.Types where
import Codec.Borsh
-import Control.Exception (MaskingState(Unmasked))
import Crypto.Hash
import Data.Aeson
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
-import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58)
import qualified Data.ByteString.Char8 as C
+import qualified Data.ByteString.Lazy.UTF8 as US
+import qualified Data.ByteString.Short as BS (ShortByteString, toShort)
+import Data.HexString
import Data.Int
+import Data.Maybe (fromJust, fromMaybe)
import Data.Structured
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import Data.Word
import qualified GHC.Generics as GHC
import qualified Generics.SOP as SOP
+import Haskoin.Address (Address)
+import qualified Haskoin.Crypto.Hash as H (Hash256(..))
+import Haskoin.Crypto.Keys.Extended (XPrvKey)
+import qualified Haskoin.Transaction.Common as H
-- * General
+--
+-- | A seed for generating private keys
+newtype Seed =
+ Seed BS.ByteString
+ deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Seed
+
+instance ToBytes Seed where
+ getBytes (Seed x) = x
+
+-- | A mnemonic phrase used to derive seeds
+newtype Phrase =
+ Phrase C.ByteString
+ deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Phrase
+
+instance ToBytes Phrase where
+ getBytes (Phrase x) = x
+
+-- | Scope for addresses/receivers
+data Scope
+ = External -- ^ Addresses used publically to receive payments
+ | Internal -- ^ Addresses used internally by wallets for change and shielding
+ deriving (Eq, Prelude.Show, Read)
+
-- | Type to represent data after Bech32 decoding
data RawData = RawData
- { hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding
- , bytes :: BS.ByteString -- ^ Decoded bytes
+ { hrp :: !BS.ByteString -- ^ Human-readable part of the Bech32 encoding
+ , bytes :: !BS.ByteString -- ^ Decoded bytes
} deriving stock (Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawData
--- * `zcashd` RPC
+-- | Type for the different networks of the Zcash blockchain
+data ZcashNet
+ = MainNet
+ | TestNet
+ | RegTestNet
+ deriving (Eq, Prelude.Show, Read)
+
+type AccountId = Int
+
+-- | Function to get the Base58 prefix for encoding a 'TransparentReceiver'
+getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8)
+getTransparentPrefix n t =
+ case t of
+ P2SH ->
+ case n of
+ MainNet -> (0x1c, 0xbd)
+ _ -> (0x1c, 0xba)
+ P2PKH ->
+ case n of
+ MainNet -> (0x1c, 0xb8)
+ _ -> (0x1d, 0x25)
+
+-- ** Constants
+-- | Type for coin types on the different networks
+data CoinType
+ = MainNetCoin
+ | TestNetCoin
+ | RegTestNetCoin
+ deriving (Eq, Prelude.Show, Ord)
+
+getValue :: CoinType -> Word32
+getValue c =
+ case c of
+ MainNetCoin -> 133
+ TestNetCoin -> 1
+ RegTestNetCoin -> 1
+
+-- | A Zcash transaction
+data Transaction = Transaction
+ { tx_id :: !HexString
+ , tx_height :: !Int
+ , tx_conf :: !Int
+ , tx_expiry :: !Int
+ , tx_transpBundle :: !(Maybe TransparentBundle)
+ , tx_saplingBundle :: !(Maybe SaplingBundle)
+ , tx_orchardBundle :: !(Maybe OrchardBundle)
+ } deriving (Prelude.Show, Eq, Read)
+
+-- | The transparent portion of a Zcash transaction
+data TransparentBundle = TransparentBundle
+ { tb_vin :: ![H.TxIn]
+ , tb_vout :: ![H.TxOut]
+ , tb_coinbase :: !Bool
+ } deriving (Eq, Prelude.Show, Read)
+
+-- | Read a raw transparent bundle into the Haskell type
+fromRawTBundle :: RawTBundle -> Maybe TransparentBundle
+fromRawTBundle rtb =
+ if ztb_empty rtb
+ then Nothing
+ else Just $
+ TransparentBundle
+ (map fromRawTxIn $ ztb_vin rtb)
+ (map fromRawTxOut $ ztb_vout rtb)
+ (ztb_coinbase rtb)
+
+fromRawTxIn :: RawTxIn -> H.TxIn
+fromRawTxIn t = H.TxIn op (rti_script t) (rti_seq t)
+ where
+ op =
+ if rop_hash (rti_outpoint t) ==
+ "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
+ then H.nullOutPoint
+ else H.OutPoint
+ ((fromJust .
+ H.hexToTxHash . toText . fromRawBytes . rop_hash . rti_outpoint)
+ t)
+ (rop_n $ rti_outpoint t)
+
+fromRawTxOut :: RawTxOut -> H.TxOut
+fromRawTxOut t = H.TxOut (rto_amt t) (rto_script t)
+
+-- *** Constants for Sapling Human-readable part
+sapExtSpendingKeyHrp = "secret-extended-key-main" :: String
+
+sapExtFullViewingKeyHrp = "zxviews" :: String
+
+sapPaymentAddressHrp = "zs" :: String
+
+sapTestExtSpendingKeyHrp = "secret-extended-key-test" :: String
+
+sapTestExtFullViewingKeyHrp = "zxviewtestsapling" :: String
+
+sapTestPaymentAddressHrp = "ztestsapling" :: String
+
+-- *** Constants for Unified Human-readable part
+uniPaymentAddressHrp = "u" :: T.Text
+
+uniFullViewingKeyHrp = "uview" :: T.Text
+
+uniIncomingViewingKeyHrp = "uivk" :: T.Text
+
+uniTestPaymentAddressHrp = "utest" :: T.Text
+
+uniTestFullViewingKeyHrp = "uviewtest" :: T.Text
+
+uniTestIncomingViewingKeyHrp = "uivktest" :: T.Text
+
+-- * RPC
-- | A type to model Zcash RPC calls
data RpcCall = RpcCall
- { jsonrpc :: T.Text
- , callId :: T.Text
- , method :: T.Text
- , parameters :: [Data.Aeson.Value]
+ { jsonrpc :: !T.Text
+ , callId :: !T.Text
+ , method :: !T.Text
+ , parameters :: ![Data.Aeson.Value]
} deriving stock (Prelude.Show, GHC.Generic)
instance ToJSON RpcCall where
@@ -77,24 +208,24 @@ instance ToJSON RpcCall where
-- | A type to model the response of the Zcash RPC
data RpcResponse r = MakeRpcResponse
- { err :: Maybe RpcError
- , respId :: T.Text
- , result :: Maybe r
+ { err :: !(Maybe RpcError)
+ , respId :: !T.Text
+ , result :: !(Maybe r)
} deriving stock (Prelude.Show, GHC.Generic)
deriving anyclass (ToJSON)
instance (FromJSON r) => FromJSON (RpcResponse r) where
parseJSON =
withObject "RpcResponse" $ \obj -> do
- e <- obj .: "error"
+ e <- obj .:? "error"
i <- obj .: "id"
- r <- obj .: "result"
+ r <- obj .:? "result"
pure $ MakeRpcResponse e i r
-- | A type to model the errors from the Zcash RPC
data RpcError = RpcError
- { ecode :: Double
- , emessage :: T.Text
+ { ecode :: !Double
+ , emessage :: !T.Text
} deriving stock (Prelude.Show, GHC.Generic)
deriving anyclass (ToJSON)
@@ -105,12 +236,13 @@ instance FromJSON RpcError where
m <- obj .: "message"
pure $ RpcError c m
+-- ** `zcashd`
-- | Type to represent response from the `zcashd` RPC `getblock` method
data BlockResponse = BlockResponse
- { bl_confirmations :: Integer -- ^ Block confirmations
- , bl_height :: Integer -- ^ Block height
- , bl_time :: Integer -- ^ Block time
- , bl_txs :: [T.Text] -- ^ List of transaction IDs in the block
+ { bl_confirmations :: !Integer -- ^ Block confirmations
+ , bl_height :: !Integer -- ^ Block height
+ , bl_time :: !Integer -- ^ Block time
+ , bl_txs :: ![HexString] -- ^ List of transaction IDs in the block
} deriving (Prelude.Show, Eq)
instance FromJSON BlockResponse where
@@ -118,72 +250,277 @@ instance FromJSON BlockResponse where
withObject "BlockResponse" $ \obj -> do
c <- obj .: "confirmations"
h <- obj .: "height"
- t <- obj .: "time"
+ t <- obj .:? "time"
txs <- obj .: "tx"
- pure $ BlockResponse c h t txs
+ pure $ BlockResponse c h (fromMaybe 0 t) txs
-- | Type to represent response from the `zcashd` RPC `getrawtransaction`
data RawTxResponse = RawTxResponse
- { rt_id :: T.Text
- , rt_hex :: BS.ByteString
- , rt_shieldedOutputs :: [BS.ByteString]
- , rt_orchardActions :: [OrchardAction]
- , rt_blockheight :: Integer
- , rt_confirmations :: Integer
- , rt_blocktime :: Integer
+ { rt_id :: !HexString
+ , rt_hex :: !HexString
+ , rt_shieldedSpends :: ![ShieldedSpend]
+ , rt_shieldedOutputs :: ![ShieldedOutput]
+ , rt_orchardActions :: ![OrchardAction]
+ , rt_blockheight :: !Integer
+ , rt_confirmations :: !Integer
+ , rt_blocktime :: !Integer
+ } deriving (Prelude.Show, Eq, Read)
+
+-- ** `zebrad`
+data ZebraTxResponse = ZebraTxResponse
+ { ztr_blockheight :: !Int
+ , ztr_conf :: !Int
+ , ztr_hex :: !HexString
+ } deriving (Prelude.Show, Eq, Read)
+
+instance FromJSON ZebraTxResponse where
+ parseJSON =
+ withObject "ZebraTxResponse" $ \obj -> do
+ hex <- obj .: "hex"
+ height <- obj .: "height"
+ c <- obj .: "confirmations"
+ pure $ ZebraTxResponse height c hex
+
+-- | Type to represent a raw deserialized Zebra transaction
+data RawZebraTx = RawZebraTx
+ { zt_id :: !HexString
+ , zt_locktime :: !Word32
+ , zt_expiry :: !Word32
+ , zt_tBundle :: !RawTBundle
+ , zt_sBundle :: !RawSBundle
+ , zt_oBundle :: !RawOBundle
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawZebraTx
+
+-- | Type for a raw deserialized Zebra transparent bundle
+data RawTBundle = RawTBundle
+ { ztb_empty :: !Bool
+ , ztb_vin :: ![RawTxIn]
+ , ztb_vout :: ![RawTxOut]
+ , ztb_coinbase :: !Bool
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle
+
+-- | Type for a raw deserialized Zebra Sapling bundle
+data RawSBundle = RawSBundle
+ { zsb_empty :: !Bool
+ , zsb_spends :: ![ShieldedSpend]
+ , zsb_outputs :: ![ShieldedOutput]
+ , zsb_value :: !Int64
+ , zsb_sig :: !HexString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawSBundle
+
+data SaplingBundle = SaplingBundle
+ { sbSpends :: ![ShieldedSpend]
+ , sbOutputs :: ![ShieldedOutput]
+ , sbValue :: !Int64
+ , sbSig :: !HexString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
+
+fromRawSBundle :: RawSBundle -> Maybe SaplingBundle
+fromRawSBundle b =
+ if zsb_empty b
+ then Nothing
+ else Just $
+ SaplingBundle (zsb_spends b) (zsb_outputs b) (zsb_value b) (zsb_sig b)
+
+-- | Type for a raw deseralized Zebra Orchard bundle
+data RawOBundle = RawOBundle
+ { zob_empty :: !Bool
+ , zob_actions :: ![OrchardAction]
+ , zob_flags :: !OrchardFlags
+ , zob_value :: !Int64
+ , zob_anchor :: !HexString
+ , zob_proof :: !HexString
+ , zob_sig :: !HexString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawOBundle
+
+-- | Type for an Orchard Bundle
+data OrchardBundle = OrchardBundle
+ { obActions :: ![OrchardAction]
+ , obFlags :: !OrchardFlags
+ , obValue :: !Int64
+ , obAnchor :: !HexString
+ , obProof :: !HexString
+ , obSig :: !HexString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
+
+fromRawOBundle :: RawOBundle -> Maybe OrchardBundle
+fromRawOBundle b =
+ if zob_empty b
+ then Nothing
+ else Just $
+ OrchardBundle
+ (zob_actions b)
+ (zob_flags b)
+ (zob_value b)
+ (zob_anchor b)
+ (zob_proof b)
+ (zob_sig b)
+
+-- | Type for the Orchard bundle flags
+data OrchardFlags = OrchardFlags
+ { of_spends :: !Bool
+ , of_outputs :: !Bool
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardFlags
+
+-- | Type for the response from the `zebrad` RPC method `getinfo`
+data ZebraGetInfo = ZebraGetInfo
+ { zgi_build :: !T.Text
+ , zgi_subversion :: !T.Text
} deriving (Prelude.Show, Eq)
-data ZcashNet
- = MainNet
- | TestNet
- | RegTestNet
- deriving (Eq, Prelude.Show)
+instance FromJSON ZebraGetInfo where
+ parseJSON =
+ withObject "ZebraGetInfo" $ \obj -> do
+ b <- obj .: "build"
+ s <- obj .: "subversion"
+ pure $ ZebraGetInfo b s
+
+-- | Type for the response from the `zebrad` RPC method `getblockchaininfo`
+data ZebraGetBlockChainInfo = ZebraGetBlockChainInfo
+ { zgb_best :: !HexString
+ , zgb_blocks :: !Int
+ , zgb_net :: !ZcashNet
+ } deriving (Prelude.Show, Eq)
+
+instance FromJSON ZebraGetBlockChainInfo where
+ parseJSON =
+ withObject "ZebraGetBlockChainInfo" $ \obj -> do
+ be <- obj .: "bestblockhash"
+ b <- obj .: "blocks"
+ c <- obj .: "chain"
+ pure $
+ ZebraGetBlockChainInfo
+ be
+ b
+ (case (c :: String) of
+ "main" -> MainNet
+ "test" -> TestNet
+ _ -> RegTestNet)
-- * Transparent
-- | Type to represent the two kinds of transparent addresses
data TransparentType
= P2SH
| P2PKH
- deriving (Eq)
+ deriving (Eq, Prelude.Show, Read)
+
+-- | Type for transparent spending key
+type TransparentSpendingKey = XPrvKey
+
+-- | Type to represent a transparent Zcash addresses
+data TransparentReceiver = TransparentReceiver
+ { tr_type :: !TransparentType
+ , tr_bytes :: !HexString
+ } deriving (Eq, Prelude.Show, Read)
-- | Type to represent a transparent Zcash addresses
data TransparentAddress = TransparentAddress
- { ta_type :: TransparentType
- , ta_net :: ZcashNet
- , ta_bytes :: BS.ByteString
- } deriving (Eq)
+ { ta_network :: !ZcashNet
+ , ta_receiver :: !TransparentReceiver
+ } deriving (Eq, Prelude.Show, Read)
-instance Prelude.Show TransparentAddress where
- show t =
- case ta_type t of
- P2SH ->
- case ta_net t of
- MainNet -> Prelude.show $ encodeTransparent (0x1c, 0xbd) $ ta_bytes t
- _ -> Prelude.show $ encodeTransparent (0x1c, 0xba) $ ta_bytes t
- P2PKH ->
- case ta_net t of
- MainNet -> Prelude.show $ encodeTransparent (0x1c, 0xb8) $ ta_bytes t
- _ -> Prelude.show $ encodeTransparent (0x1d, 0x25) $ ta_bytes t
- where
- encodeTransparent :: (Word8, Word8) -> BS.ByteString -> BS.ByteString
- encodeTransparent (a, b) h =
- encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum
- where
- sha256 :: BS.ByteString -> BS.ByteString
- sha256 bs = BA.convert (hash bs :: Digest SHA256)
- digest = BS.pack [a, b] <> h
- checksum = sha256 $ sha256 digest
+-- | Type to represent a TEX Zcash addresses
+data ExchangeAddress = ExchangeAddress
+ { ex_network :: !ZcashNet
+ , ex_address :: !TransparentReceiver
+ } deriving (Eq, Prelude.Show, Read)
+
+-- | Wrapper types for transparent elements
+data RawTxIn = RawTxIn
+ { rti_outpoint :: !RawOutPoint
+ , rti_script :: !BS.ByteString
+ , rti_seq :: !Word32
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxIn
+
+data RawTxOut = RawTxOut
+ { rto_amt :: !Word64
+ , rto_script :: !BS.ByteString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxOut
+
+data RawOutPoint = RawOutPoint
+ { rop_hash :: !BS.ByteString
+ , rop_n :: !Word32
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawOutPoint
-- * Sapling
+-- | A spending key for Sapling
+newtype SaplingSpendingKey =
+ SaplingSpendingKey BS.ByteString
+ deriving stock (Eq, Prelude.Show, Read)
+
+instance ToBytes SaplingSpendingKey where
+ getBytes (SaplingSpendingKey s) = s
+
+-- | A Sapling receiver
+newtype SaplingReceiver =
+ SaplingReceiver BS.ByteString
+ deriving stock (Eq, Prelude.Show, Read)
+
+instance ToBytes SaplingReceiver where
+ getBytes (SaplingReceiver s) = s
+
+data SaplingAddress = SaplingAddress
+ { net_type :: !ZcashNet
+ , sa_receiver :: !SaplingReceiver
+ } deriving (Eq, Prelude.Show, Read)
+
+-- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method
+data ShieldedSpend = ShieldedSpend
+ { sp_cv :: !HexString
+ , sp_anchor :: !HexString
+ , sp_nullifier :: !HexString
+ , sp_rk :: !HexString
+ , sp_proof :: !HexString
+ , sp_auth :: !HexString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedSpend
+
+instance FromJSON ShieldedSpend where
+ parseJSON =
+ withObject "ShieldedSpend" $ \obj -> do
+ cv <- obj .: "cv"
+ anchor <- obj .: "anchor"
+ nullifier <- obj .: "nullifier"
+ rk <- obj .: "rk"
+ p <- obj .: "proof"
+ sig <- obj .: "spendAuthSig"
+ pure $ ShieldedSpend cv anchor nullifier rk p sig
+
-- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@.
data ShieldedOutput = ShieldedOutput
- { s_cv :: BS.ByteString -- ^ Value commitment to the input note
- , s_cmu :: BS.ByteString -- ^ The u-coordinate of the note commitment for the output note
- , s_ephKey :: BS.ByteString -- ^ Ephemeral Jubjub public key
- , s_encCipherText :: BS.ByteString -- ^ The output note encrypted to the recipient
- , s_outCipherText :: BS.ByteString -- ^ A ciphertext enabling the sender to recover the output note
- , s_proof :: BS.ByteString -- ^ Zero-knowledge proof using the Sapling Output circuit
- } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ { s_cv :: !HexString -- ^ Value commitment to the input note
+ , s_cmu :: !HexString -- ^ The u-coordinate of the note commitment for the output note
+ , s_ephKey :: !HexString -- ^ Ephemeral Jubjub public key
+ , s_encCipherText :: !HexString -- ^ The output note encrypted to the recipient
+ , s_outCipherText :: !HexString -- ^ A ciphertext enabling the sender to recover the output note
+ , s_proof :: !HexString -- ^ Zero-knowledge proof using the Sapling Output circuit
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput
@@ -197,31 +534,50 @@ instance FromJSON ShieldedOutput where
encText <- obj .: "encCiphertext"
outText <- obj .: "outCiphertext"
p <- obj .: "proof"
- pure $
- ShieldedOutput
- (decodeHexText cv)
- (decodeHexText cmu)
- (decodeHexText ephKey)
- (decodeHexText encText)
- (decodeHexText outText)
- (decodeHexText p)
+ pure $ ShieldedOutput cv cmu ephKey encText outText p
+
+-- | Type for a Sapling note commitment tree
+newtype SaplingCommitmentTree = SaplingCommitmentTree
+ { sapTree :: HexString
+ } deriving (Eq, Prelude.Show, Read)
+
+-- | Type for a Sapling incremental witness
+newtype SaplingWitness = SaplingWitness
+ { sapWit :: HexString
+ } deriving (Eq, Prelude.Show, Read)
-- * Orchard
+-- | A spending key for Orchard
+newtype OrchardSpendingKey =
+ OrchardSpendingKey BS.ByteString
+ deriving stock (Eq, Prelude.Show, Read)
+
+instance ToBytes OrchardSpendingKey where
+ getBytes (OrchardSpendingKey o) = o
+
+-- | An Orchard receiver
+newtype OrchardReceiver =
+ OrchardReceiver BS.ByteString
+ deriving stock (Eq, Prelude.Show, Read)
+
+instance ToBytes OrchardReceiver where
+ getBytes (OrchardReceiver o) = o
+
-- | Type to represent a Unified Address
data UnifiedAddress = UnifiedAddress
- { ua_net :: ZcashNet
- , o_rec :: BS.ByteString
- , s_rec :: BS.ByteString
- , t_rec :: Maybe TransparentAddress
- } deriving (Prelude.Show, Eq)
+ { ua_net :: !ZcashNet
+ , o_rec :: !(Maybe OrchardReceiver)
+ , s_rec :: !(Maybe SaplingReceiver)
+ , t_rec :: !(Maybe TransparentReceiver)
+ } deriving (Prelude.Show, Eq, Read)
-- | Helper type for marshalling UAs
data RawUA = RawUA
- { raw_net :: Word8
- , raw_o :: BS.ByteString
- , raw_s :: BS.ByteString
- , raw_t :: BS.ByteString
- , raw_to :: BS.ByteString
+ { raw_net :: !Word8
+ , raw_o :: !BS.ByteString
+ , raw_s :: !BS.ByteString
+ , raw_t :: !BS.ByteString
+ , raw_to :: !BS.ByteString
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
@@ -229,10 +585,10 @@ data RawUA = RawUA
-- | Type to represent a Unified Full Viewing Key
data UnifiedFullViewingKey = UnifiedFullViewingKey
- { net :: Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@.
- , o_key :: BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
- , s_key :: BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
- , t_key :: BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
+ { net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@.
+ , o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
+ , s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
+ , t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
@@ -240,15 +596,15 @@ data UnifiedFullViewingKey = UnifiedFullViewingKey
-- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf)
data OrchardAction = OrchardAction
- { nf :: BS.ByteString -- ^ The nullifier of the input note
- , rk :: BS.ByteString -- ^ The randomized validating key for @auth@
- , cmx :: BS.ByteString -- ^ The x-coordinate of the note commitment for the output note
- , eph_key :: BS.ByteString -- ^ An encoding of an ephemeral Pallas public key
- , enc_ciphertext :: BS.ByteString -- ^ The output note encrypted to the recipient
- , out_ciphertext :: BS.ByteString -- ^ A ciphertext enabling the sender to recover the output note
- , cv :: BS.ByteString -- ^ A value commitment to the net value of the input note minus the output note
- , auth :: BS.ByteString -- ^ A signature authorizing the spend in this Action
- } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ { nf :: !HexString -- ^ The nullifier of the input note
+ , rk :: !HexString -- ^ The randomized validating key for @auth@
+ , cmx :: !HexString -- ^ The x-coordinate of the note commitment for the output note
+ , eph_key :: !HexString -- ^ An encoding of an ephemeral Pallas public key
+ , enc_ciphertext :: !HexString -- ^ The output note encrypted to the recipient
+ , out_ciphertext :: !HexString -- ^ A ciphertext enabling the sender to recover the output note
+ , cv :: !HexString -- ^ A value commitment to the net value of the input note minus the output note
+ , auth :: !HexString -- ^ A signature authorizing the spend in this Action
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardAction
@@ -264,27 +620,106 @@ instance FromJSON OrchardAction where
outText <- obj .: "outCiphertext"
cval <- obj .: "cv"
a <- obj .: "spendAuthSig"
- pure $
- OrchardAction
- (decodeHexText n)
- (decodeHexText r)
- (decodeHexText c)
- (decodeHexText ephKey)
- (decodeHexText encText)
- (decodeHexText outText)
- (decodeHexText cval)
- (decodeHexText a)
+ pure $ OrchardAction n r c ephKey encText outText cval a
+
+-- | Type for a Orchard note commitment tree
+newtype OrchardCommitmentTree = OrchardCommitmentTree
+ { orchTree :: HexString
+ } deriving (Eq, Prelude.Show, Read)
+
+-- | Type for a Sapling incremental witness
+newtype OrchardWitness = OrchardWitness
+ { orchWit :: HexString
+ } deriving (Eq, Prelude.Show, Read)
-- | Type to represent a decoded note
data DecodedNote = DecodedNote
- { a_value :: Int64 -- ^ The amount of the transaction in _zatoshis_.
- , a_recipient :: BS.ByteString -- ^ The recipient Orchard receiver.
- , a_memo :: BS.ByteString -- ^ The decoded shielded memo field.
+ { a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_.
+ , a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver.
+ , a_memo :: !BS.ByteString -- ^ The decoded shielded memo field.
+ , a_nullifier :: !HexString -- ^ The calculated nullifier
+ , a_rho :: !BS.ByteString
+ , a_rseed :: !Rseed
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote
+data Rseed = Rseed
+ { rs_kind :: !Word8
+ , rs_bytes :: !BS.ByteString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Rseed
+
+data TransparentTxSpend = TransparentTxSpend
+ { ts_sk :: !BS.ByteString
+ , ts_utxo :: !RawOutPoint
+ , ts_coin :: !RawTxOut
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct TransparentTxSpend
+
+data SaplingTxSpend = SaplingTxSpend
+ { ss_sk :: !BS.ByteString
+ , ss_note :: !DecodedNote
+ , ss_iw :: !BS.ByteString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingTxSpend
+
+data OrchardTxSpend = OrchardTxSpend
+ { ss_sk :: !BS.ByteString
+ , ss_note :: !DecodedNote
+ , ss_iw :: !BS.ByteString
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardTxSpend
+
+data OutgoingNote = OutgoingNote
+ { on_kind :: !Word8
+ , on_key :: !BS.ByteString
+ , on_recipient :: !BS.ByteString
+ , on_amt :: !Word64
+ , on_memo :: !BS.ByteString
+ , on_chg :: !Bool
+ } deriving stock (Eq, Prelude.Show, GHC.Generic)
+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
+ deriving anyclass (Data.Structured.Show)
+ deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OutgoingNote
+
+newtype SaplingSpendParams = SaplingSpendParams
+ { sapSParams :: BS.ByteString
+ } deriving newtype (Eq, Prelude.Show, Read)
+
+newtype SaplingOutputParams = SaplingOutputParams
+ { sapOParams :: BS.ByteString
+ } deriving newtype (Eq, Prelude.Show, Read)
+
+data TxError
+ = InsufficientFunds
+ | ChangeRequired
+ | Fee
+ | Balance
+ | TransparentBuild
+ | SaplingBuild
+ | OrchardBuild
+ | OrchardSpend
+ | OrchardRecipient
+ | SaplingBuilderNotAvailable
+ | OrchardBuilderNotAvailable
+ | ZHError
+ deriving (Eq, Prelude.Show, Read)
+
+-- * Classes
+-- | Class to represent types with a bytestring representation
+class ToBytes a where
+ getBytes :: a -> BS.ByteString
+
-- * Helpers
-- | Helper function to turn a hex-encoded string to bytestring
decodeHexText :: String -> BS.ByteString
diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs
index d7577ca..59b38ed 100644
--- a/src/ZcashHaskell/Utils.hs
+++ b/src/ZcashHaskell/Utils.hs
@@ -1,27 +1,13 @@
-{- Copyright 2022-2024 Vergara Technologies LLC
-
- This file is part of Zcash-Haskell.
-
- Zcash-Haskell is free software: you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the Free
- Software Foundation, either version 3 of the License, or (at your option) any
- later version.
-
- Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- details.
-
- You should have received a copy of the GNU Lesser General Public License along with
- Zcash-Haskell. If not, see .
-
--}
+-- Copyright 2022-2024 Vergara Technologies LLC
+--
+-- This file is part of Zcash-Haskell.
+--
-- |
-- Module : ZcashHaskell.Utils
--- Copyright : Vergara Technologies (c)2023
--- License : BOSL
+-- Copyright : 2022-2024 Vergara Technologies LLC
+-- License : MIT
--
--- Maintainer : rene@vergara.network
+-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
@@ -33,21 +19,40 @@ module ZcashHaskell.Utils where
import C.Zcash
( rustWrapperBech32Decode
+ , rustWrapperBech32Encode
+ , rustWrapperBech32mEncode
+ , rustWrapperCreateTx
, rustWrapperF4Jumble
, rustWrapperF4UnJumble
+ , rustWrapperTxRead
)
+import Control.Exception (SomeException(..), try)
import Control.Monad.IO.Class
import Data.Aeson
+import Data.Binary.Get
import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as LBS
+import Data.HexString (HexString(..), toBytes)
import qualified Data.Text as T
+import qualified Data.Text.Encoding as E
import Foreign.Rust.Marshall.Variable
+import Network.HTTP.Client (HttpException(..))
import Network.HTTP.Simple
import ZcashHaskell.Types
+-- * Utility functions
-- | Decode the given bytestring using Bech32
decodeBech32 :: BS.ByteString -> RawData
decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode
+-- | Encode the given Human Readable Part and bytestring as a Bech32m string
+encodeBech32m :: BS.ByteString -> BS.ByteString -> T.Text
+encodeBech32m h d = withPureBorshVarBuffer $ rustWrapperBech32mEncode h d
+
+-- | Encode the given Human Readable Part and bytestring as a Bech32 string
+encodeBech32 :: BS.ByteString -> BS.ByteString -> T.Text
+encodeBech32 h d = withPureBorshVarBuffer $ rustWrapperBech32Encode h d
+
-- | Apply the F4Jumble transformation to the given bytestring
f4Jumble :: BS.ByteString -> BS.ByteString
f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble
@@ -56,6 +61,7 @@ f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble
f4UnJumble :: BS.ByteString -> BS.ByteString
f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble
+-- * Node interaction
-- | Make a Zcash RPC call
makeZcashCall ::
(MonadIO m, FromJSON a)
@@ -72,3 +78,95 @@ makeZcashCall username password m p = do
setRequestBasicAuth username password $
setRequestMethod "POST" defaultRequest
httpJSON myRequest
+
+-- | Make a Zebra RPC call
+makeZebraCall ::
+ FromJSON a
+ => T.Text -- ^ Hostname for `zebrad`
+ -> Int -- ^ Port for `zebrad`
+ -> T.Text -- ^ RPC method to call
+ -> [Data.Aeson.Value] -- ^ List of parameters
+ -> IO (Either String a)
+makeZebraCall host port m params = do
+ let payload = RpcCall "2.0" "zh" m params
+ let myRequest =
+ setRequestBodyJSON payload $
+ setRequestPort port $
+ setRequestHost (E.encodeUtf8 host) $
+ setRequestMethod "POST" defaultRequest
+ r <-
+ try $ httpJSON myRequest :: FromJSON a1 =>
+ IO (Either SomeException (Response (RpcResponse a1)))
+ case r of
+ Left ex -> return $ Left $ show ex
+ Right res -> do
+ let zebraResp = getResponseBody res
+ case err zebraResp of
+ Just zErr -> return $ Left $ T.unpack $ emessage zErr
+ Nothing ->
+ case result zebraResp of
+ Nothing -> return $ Left "Empty response from Zebra"
+ Just zR -> return $ Right zR
+
+getBlockTime :: HexString -> Int
+getBlockTime hex_block =
+ fromIntegral $
+ runGet getInt32le $
+ LBS.fromStrict $ BS.take 4 (BS.drop 100 $ hexBytes hex_block)
+
+readZebraTransaction :: HexString -> Maybe RawZebraTx
+readZebraTransaction hex =
+ if BS.length (hexBytes $ zt_id rawTx) < 1
+ then Nothing
+ else Just rawTx
+ where
+ rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex
+
+createTransaction ::
+ Maybe SaplingCommitmentTree -- ^ to obtain the Sapling anchor
+ -> Maybe OrchardCommitmentTree -- ^ to obtain the Orchard anchor
+ -> [TransparentTxSpend] -- ^ the list of transparent notes to spend
+ -> [SaplingTxSpend] -- ^ the list of Sapling notes to spend
+ -> [OrchardTxSpend] -- ^ the list of Orchard notes to spend
+ -> [OutgoingNote] -- ^ the list of outgoing notes, including change notes
+ -> SaplingSpendParams -- ^ the Sapling circuit spending parameters
+ -> SaplingOutputParams -- ^ the Sapling circuit output parameters
+ -> ZcashNet -- ^ the network to be used
+ -> Int -- ^ target block height
+ -> Bool -- ^ True to build, False to estimate fee
+ -> Either TxError HexString
+createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh build =
+ if BS.length (hexBytes txResult) > 1
+ then Right txResult
+ else case head (BS.unpack $ hexBytes txResult) of
+ 0 -> Left InsufficientFunds
+ 1 -> Left ChangeRequired
+ 2 -> Left Fee
+ 3 -> Left Balance
+ 4 -> Left TransparentBuild
+ 5 -> Left SaplingBuild
+ 6 -> Left OrchardBuild
+ 7 -> Left OrchardSpend
+ 8 -> Left OrchardRecipient
+ 9 -> Left SaplingBuilderNotAvailable
+ 10 -> Left OrchardBuilderNotAvailable
+ _ -> Left ZHError
+ where
+ txResult =
+ withPureBorshVarBuffer $
+ rustWrapperCreateTx
+ (case sapAnchor of
+ Nothing -> "0"
+ Just sA -> toBytes $ sapTree sA)
+ (case orchAnchor of
+ Nothing -> "0"
+ Just oA -> toBytes $ orchTree oA)
+ tSpend
+ sSpend
+ oSpend
+ outgoing
+ (sapSParams sParams)
+ (sapOParams oParams)
+ (znet == MainNet)
+ (fromIntegral bh)
+ build
diff --git a/stack.yaml b/stack.yaml
deleted file mode 100644
index 33603d4..0000000
--- a/stack.yaml
+++ /dev/null
@@ -1,75 +0,0 @@
-# This file was automatically generated by 'stack init'
-#
-# Some commonly used options have been documented as comments in this file.
-# For advanced use and comprehensive documentation of the format, please see:
-# https://docs.haskellstack.org/en/stable/yaml_configuration/
-
-# Resolver to choose a 'specific' stackage snapshot or a compiler version.
-# A snapshot resolver dictates the compiler version and the set of packages
-# to be used for project dependencies. For example:
-#
-# resolver: lts-3.5
-# resolver: nightly-2015-09-21
-# resolver: ghc-7.10.2
-#
-# The location of a snapshot can be provided as a file or url. Stack assumes
-# a snapshot provided as a file might change, whereas a url resource does not.
-#
-# resolver: ./custom-snapshot.yaml
-# resolver: https://example.com/snapshots/2018-01-01.yaml
-resolver: lts-21.21
-
-# User packages to be built.
-# Various formats can be used as shown in the example below.
-#
-# packages:
-# - some-directory
-# - https://example.com/foo/bar/baz-0.0.2.tar.gz
-# subdirs:
-# - auto-update
-# - wai
-packages:
-- .
-# Dependency packages to be pulled from upstream that are not in the resolver.
-# These entries can reference officially published versions as well as
-# forks / in-progress versions pinned to a git hash. For example:
-#
-extra-deps:
-# - acme-missiles-0.3
- - git: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git
- commit: 787c2e813eb3a5d16c375d4b37dfefbd2adcdf05
- - git: https://github.com/well-typed/borsh.git
- commit: d2fcfa159e0a844b1ec5e8ed3e232d4b380fa831
- - vector-0.13.0.0@sha256:fa5cac81a17a5af388716792e8b99c24b3b66770086756d0d8b23f8272a0244c,9112
- - aeson-2.1.2.1@sha256:f10f3c661bd5cf57aee46b94420e47736240b8e209ac15f4bfc1a4e4d55831fa,6344
- - generically-0.1.1
- - semialign-1.2.0.1@sha256:ee3468e349e72ec0a604ae05573a4de7181d97d10366254244a0cca8a76d6c35,2852
- - strict-0.4.0.1@sha256:d6205a748eb8db4cd17a7179be970c94598809709294ccfa43159c7f3cc4bf5d,4187
- - these-1.1.1.1@sha256:2991c13e264b0c35c696c8f5f85c428c53bc42e93b1dfbd19a582052112d948a,2748
- - assoc-1.0.2@sha256:9decd0933cb6b903a40a8ace02d634bf90048ee2e5b0a514dccad7056c041881,1253
-#
-# extra-deps: []
-
-# Override default flag values for local packages and extra-deps
-# flags: {}
-
-# Extra package databases containing global packages
-# extra-package-dbs: []
-
-# Control whether we use the GHC we find on the path
-# system-ghc: true
-#
-# Require a specific version of Stack, using version ranges
-# require-stack-version: -any # Default
-# require-stack-version: ">=2.9"
-#
-# Override the architecture used by Stack, especially useful on Windows
-# arch: i386
-# arch: x86_64
-#
-# Extra directories used by Stack for building
-# extra-include-dirs: [/path/to/dir]
-# extra-lib-dirs: [/path/to/dir]
-#
-# Allow a newer minor version of GHC than the snapshot specifies
-# compiler-check: newer-minor
diff --git a/test/Spec.hs b/test/Spec.hs
index 8007b19..7b0aac0 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -17,49 +17,112 @@
-}
{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeSynonymInstances #-}
import C.Zcash (rustWrapperUADecode)
+import Control.Exception (throwIO)
+import Control.Monad.IO.Class (liftIO)
import Data.Aeson
import Data.Bool (Bool(True))
import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as C
+import Data.Either (isRight)
+import Data.Foldable (sequenceA_)
+import Data.HexString
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified Data.Text.Lazy.Encoding as LE
import qualified Data.Text.Lazy.IO as LTIO
import Data.Word
+import Foreign.C.Types
import GHC.Float.RealFracMethods (properFractionDoubleInteger)
+import Haskoin.Crypto.Hash (ripemd160)
+import Haskoin.Crypto.Keys.Extended
+import Haskoin.Transaction.Common
+import Test.HUnit
import Test.Hspec
+import Test.Hspec.QuickCheck
+import Test.QuickCheck
+import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
import ZcashHaskell.Orchard
import ZcashHaskell.Sapling
- ( decodeSaplingOutput
+ ( decodeSaplingAddress
+ , decodeSaplingOutput
+ , decodeSaplingOutputEsk
+ , encodeSaplingAddress
+ , genSaplingInternalAddress
+ , genSaplingPaymentAddress
+ , genSaplingSpendingKey
+ , getSaplingNotePosition
+ , getSaplingWitness
, getShieldedOutputs
, isValidSaplingViewingKey
, isValidShieldedAddress
, matchSaplingAddress
+ , updateSaplingCommitmentTree
+ , updateSaplingWitness
)
+import ZcashHaskell.Transparent
import ZcashHaskell.Types
- ( BlockResponse(..)
+ ( AccountId
+ , BlockResponse(..)
+ , CoinType(..)
, DecodedNote(..)
, OrchardAction(..)
+ , OrchardBundle(..)
+ , OrchardCommitmentTree(..)
+ , OrchardSpendingKey(..)
+ , OrchardWitness(..)
+ , Phrase(..)
, RawData(..)
+ , RawOutPoint(..)
+ , RawTBundle(..)
+ , RawTxIn(..)
+ , RawTxOut(..)
, RawTxResponse(..)
+ , RawZebraTx(..)
+ , SaplingAddress(..)
+ , SaplingBundle(..)
+ , SaplingCommitmentTree(..)
+ , SaplingReceiver(..)
+ , SaplingSpendingKey(..)
+ , SaplingWitness(..)
+ , Scope(..)
+ , Seed(..)
, ShieldedOutput(..)
+ , ToBytes(..)
+ , TransparentAddress(..)
+ , TransparentBundle(..)
+ , TransparentReceiver(..)
+ , TransparentType(..)
, UnifiedAddress(..)
, UnifiedFullViewingKey(..)
+ , ZcashNet(..)
+ , ZebraTxResponse(..)
, decodeHexText
+ , fromRawOBundle
+ , fromRawSBundle
+ , fromRawTBundle
+ , getValue
)
import ZcashHaskell.Utils
+m2bs :: Maybe BS.ByteString -> BS.ByteString
+m2bs x = fromMaybe "" x
+
main :: IO ()
main = do
hspec $ do
describe "Bech32" $ do
- let s = "bech321qqqsyrhqy2a"
+ let s = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu"
let decodedString = decodeBech32 s
- it "hrp matches" $ do hrp decodedString `shouldBe` "bech32"
- it "data matches" $ do
+ it "hrp matches" $ do hrp decodedString `shouldBe` "abc"
+ xit "data matches" $ do
bytes decodedString `shouldBe` BS.pack ([0x00, 0x01, 0x02] :: [Word8])
+ it "encoding works" $ do
+ encodeBech32m "abc" (bytes decodedString) `shouldBe`
+ E.decodeUtf8Lenient s
describe "F4Jumble" $ do
it "jumble a string" $ do
let input =
@@ -278,8 +341,20 @@ main = do
case t of
Left s -> s `shouldBe` ""
Right x ->
- rt_id x `shouldBe`
+ toText (rt_id x) `shouldBe`
"5242b51f22a7d6fe9dee237137271cde704d306a5fff6a862bffaebb6f0e7e56"
+ describe "Seeds" $ do
+ it "generate seed phrase" $ do
+ s <- generateWalletSeedPhrase
+ BS.length (getBytes s) `shouldNotBe` 0
+ it "get seed from phrase" $ do
+ s <- generateWalletSeedPhrase
+ let x = getWalletSeed s
+ let result =
+ case x of
+ Nothing -> False
+ Just s' -> True
+ result `shouldBe` True
describe "Sapling address" $ do
it "succeeds with valid address" $ do
let sa =
@@ -320,6 +395,11 @@ main = do
let ua =
"u1salpdyefbreakingtheaddressh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur"
isValidUnifiedAddress ua `shouldBe` Nothing
+ it "encodes UA correctly" $ do
+ let ua =
+ "u1salpdyefywvsg2dlmxg9589yznh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur"
+ (encodeUnifiedAddress <$> isValidUnifiedAddress ua) `shouldBe`
+ Just (E.decodeUtf8Lenient ua)
describe "Decode UVK from YWallet" $ do
let uvk =
"uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"
@@ -353,7 +433,7 @@ main = do
let rawKey = decodeBech32 svk
let badKey = decodeBech32 badvk
let rawTx =
- decodeHexText
+ fromText
"050000800a27a726b4d0d6c200000000ff8e210000000001146cc65bd6d252d09b8eb0a8ab0aab6d7a798325aefc1d3032fc6d31373a85a25a3a16b447a698f720ade1bc290a74d85574b5b20515391035a67f8d5883dc65ea3ba4a17b009d6f325d41072b3ce240270959a7ffd040e5f16c697d8148973c62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d23301c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668f0d8ffffffffffff9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900c734b62e56792f6c8bed48e4f108a817e83d64d6a59e38cfdb55c0f8a89bc7507c89326266f7ac03a3941f448cb879bd792bb116d0be8876c0856a76ddec0f0c02e16f0338626013ee5f6037fc6a3c69fa291204039d04d17c11295ee3024aea8f5d381e9b7eb3f938b6f9182bf4f889f1e53e30f998b1cdd23f45cfaa20aaef058248cc2e1c487fcdf54a4bc22a68a17cb6fa7b2fbf333b99feb84643d321398b675634929602126b2fb40171e514769bf82f18c267ce9cda0c24300caa9a5a361144d3b7b9ab2243ee9811d9b2e72c8bb1d145cdfcf6b29994a969b41c47208f5dba8d6d871e490e9b970afec4d8bca40ba51825cdc78cc7cde6b6f235a4105b1d1b5e2765efd753095ce770f070b02cce3316721b9345680c146c2f428c0bbca90d5a8cd0a1c4c31cbfa8ec165ea9f9c71d2d05e3cf8bae5e779786f179c45a3cd8087d820cae812aded04f8acda9068af80ea834f79f1bd03bfd66f8a19074649a85ce877df1a621a867debb423ec0d19015b326fcf6f143aba34029c1da2fc7b099378a366c38c9609ef6a9d9e175e21b0c1ab94a84e28ee7f1a00e39cb6fb59f44e4567e9f85f8f98158263c52ec433c042397c784edb07c28d2bca036f59090e819157375d610acb1993a4107b48da13a371f5383429baee209b2c0cc150fcef79a042749668ba1f89ad24a8c746142191ed0e8fd63624a331d98d50daa84ccf9043076947cf5115b9f8787acd36000c5e72c8d783b29bb28a3e46036d0a592ce8a158ee5a7ac210be72d3a6185c13645d96a8446021b64043ab8b589a20091c152e7d5a993ba94770eea988e289e1536d0d81dbc7046ca9c6d918446bf970894f073c920006681ccf6d1a3f138519c68eba0296069e42dc60f2bcd0f17c400efe4f4e87de8606606dc4fdf31494df4d454d14a440b1d9db4265c7aa9bc8683c68cb149f2cc826427575e2af82e842199a9cb9fdc7243b3bc12f1a71c37eac5cf88ba830cb95728897fa4c177a290d6b2b3814173262da14db9b4ef39fc54f888a6ffef4221ae672fb03bc78ebef479360a682ddb12ea0369a428a6c2960ff8327e9a2f5e5d98ce1eae748db8f6a4631c789b4d751d6b99c97c149a813998d44a7b57ba06c8bcb8a6c73c6388cdcfeb1346cec8fee7bdebf2a2388d9722183eb2d2e0e183cdd092152ef640880f4514f3c5e836cc3a8249413500630aa8da85f9e3cd92bdadbb69a2bab8d71f0b3ec5832a7ddbddd67b34c33b2e12a0c8468e852e4a8f7df45657e9632088aa7c6c5048a2686019cfec33b27fc88e23759938dd55a5dff589c1c21a37da617609e9d8be37dbf9bd6e84ee160fe10268171d969e4611afe9d3482ed4b132dcdd11ee516f36d512a333da20266fd984caebf4937fdfd18ed07b4a45771cf5c8c16c6b258b289a07d136a22acc766011f366c420bafb8fc1a10e42219bede5a3d1166c525491ab60bbd1f973fd3fb2e94cea888e24d5fb0adce51faeda75d62de70094d4b36d38d03cd824d284fad577c3ead4d98bcc8ceccd18174a889b22380bfcc12656e764ea0b8fe1409971283008ed02cbef89d6f544c62c3b001bfe96723fda9190deecba534d69cfa358036fdaf16127b89f925c52d4e750919ffb7182b6a8ad13d0a8e00e0b906978dd24ee11869c1a63837a80e46e1216e2e273aba07aa5b0d97558db0ba7f9ac4c89403c65f1719394e479311f5cf84746e6be6f1abcac03194aa8bf1735811198b5df90dd6cac345779c185c24beda0101b932048dc4144af664a63acc0c395052882ee1f18bd0ddf13bb583861923bc00ed5ae815b964698ca097eda1c4281e039139fa3091890244f926cc4ab773ca8a35d5263d3bb48fd6ac53a4bb4d7d60b36446dbc714c35b5e13a17c5b0c70f67207839d1f7404604aff63b2fa83a4da7dac92aac96b3f250412f8d04a9e298004313b02edefd076c67d8a1316355777814e2e1ab03690e426b672d32ff65c03c592ecce6a70e34fea2e15b9a6b4fd092d027199caf27e84e25c09380b38a5eb8985355b3259aa1d94be74269b84f953053b02ba3be9df872ae5fb2d893188575bdfe222ba267b5461a0d0be274a7d9e6ee51490d98e4cd97978804c4f0f8e9f4908fd8c102b01080f5a02b7578591e95d60f3f56d8e48514b1ce7ea6894f55a32c8ac8564985d18c6b82f8dcde5b315624e9321bdd49dd350c87907cc373c0238a79321e6250e38a0ceb2c060ecee6708c11cb30a49687da9923bcdf011f9aca27e6eb5a8477a2bae2dcff9884cc2349b51a66b5179ed2d8f69e4bbba74c694194e83d04a8566228227eb732a95180c6788483d1f259d52c52fe43357656d50a1cf2902c3124d60d15fc85f0447a1203f824c1106452cfec1c92b18de003f82a0000000000001cbd27436a221a53d08c4838831d1bc60ff7e93df41a51412ef6096eec98bb28fd601c53a5371b23a497062635b5cdde715c23840d37f1cf328f0a2ba96260357689ae3f84a80dbdca1520df68513be1285177d3c0da664c64944de78d8b8d5864f5ac15444cd3204adc4fe487503066c18fbbef8d0515248b0a97577f5aea1d255788ed4bb66d4d56303efe135063392c312b4671963daa20e0ade262984e11263a1588eba3cf829e6131ab506e6a850aacce603e8ecfd6e794c90a772603d80fd2aad6027b34854072a0d23079252adb1ba637bbc650ed4afd35d977e1498d998020bc1c814718b48ba7378a92c56827d3c2f20daa231fa51f0a9188520e2a11149e162489f0d6dbd27cf94fd5775311d3dfbcfeb431bafc3515bbb8c4ba4488c320dca0dfec548fe9f46d8810b3f6b16bb3e3eb0ea130747d3d127c5953ca8d561f8d425a35dc3f2cd831743139fbdcada42308b524313782e23b32d5d54a265eae408623e3b2779fe60e13cf47d54dfe520f9f4e57c68aed31f78629a9074d72ab87bea993a38f95ab40df3ef01735e7d44ad365a786e0d3032f1c1dc4e6839c974185dbe63f8725e79831ebe269f94c96705639ab38d5d0700da04c6a9f686e1ea13391885287ba43cf3ccef1c2227918f15ed55441c45adca84153530bbfea3cf37adbf84831a2bfcbf0ca4a4bbd90e623789fe993dc17503ec11b1ef3049f27b27ff778af364d634a46165cda1dd8241cb88740bce74a73e7e3d656df2dee05bb561a85e64671b191ec802c5bfaca49b8168e44271cf13df756395896ff41a99654f55b6951f20d04b2007938a420218db8e37445ef3267130e288e3270b13a92596a26043e1ae84f3934cdb13363bc2843f74a0f6608a36b52c985132aa427c56b7275a864b3c76502c37b8abb8d0286b3199c78492ba8103f5a23c6cdca2292c75d7d6d7080108850807f78af3dc7e418371c6b8951bd89b79fa586af4e16096b08ac1f4dc2b1e4feaa5c040bb002b57311523197b6e2bef5b79ac9c9b4a339be6f6bf7fbe9b5c93862c87be6647949c70bb2c7e268e2ab39cbadd69de628376b3af744eeabc85b599bbdd09defacefa443e05c9b5f259a7783743fecf1a749c57cacc85703269ed67db1d8d475f6fe25d66f84a77379411ba123d98fcb3ae4eec306489a08372893616a91268ea6bf34ddbf0fdef1360ab9e82f4ac80a24e41f439af06fadc223c61f445b7261eda5e1320e269d1277631ee2245cf930244bf8c04050c514e2d59035b80827586cbfeb7da7a59c1208aa86390b9dc7a9b6ef38879ba4deea5eef47c5c98d9167594cd730abdfaf082090efe759d1b13199d739c112ae324ba24b275bf1d89867b81f4580a7ea3a8d3d07b45e2de6c1c7099de3606873b13f3083ecd1e84456c9a1b1d358075c68b1a7cf0b1f26031a2909e226f5da7877d0085b879165ec4b1d9abb7b0732ab4a6f22d9a7bbd0d494ef3f9af4903dc733fe92c6b2f557d1406d223a93e8ad6e579ebcde9c39a5652ad31335df924e5b6a09a0191821b4a0c8f886e2d7860b75ae79ad9dfbebf3500c8b9762dcd131eb5c8b866b5efb4fbfdcc5e31605c2b7d2ff8db5198a6c41bcf880065ff232ff8f84ca3f8022d3428359dc9fb19f57a6ad3f3d174d8a348879a754b37095f01d9a7f6f873798b97dfc5d7c7eaf0383b3fdccdcc11b30dbb3a0fe3186a36c4ddc9674624e38a81cca60a9bbc1b124021b61a383b7547d6af187022c133ba9d6dadf711a3af3b0255b859b214ef6c5dec592248fc94339a64f19196ca0fdad80f7f8e3d78b1f783b1f038008d0d106bd86e23e33ae5728872d42a555bb36d0e3303f0b4ab41180f4251590ee3ee244b77191c31b9f3f990f71c6e237b9dbcf7ca21c9b4c2446b856c67861785bb9edb920b8f530a7a088313ef044419a879f26db137e1557d079315844f9f60bae03d8cffa7a28bd2857a001fd5d2d999fca95ff91df0e228567f6c9ff592b77b7ccdc93a951f7e34910361a8f4fb517e1c9fb956a3bb50ddf37ed37e8d26adfc0f71e059ba95ec77a1e34e1b3420c893f89b79fc72e3a1d864fd35526727f939badc29740f5ef9c0bb9a3f72e1e08b2ef2ab366f80d8e14e03e92162736e2ccb7cde82b2af08de8a6a81c03c63e2396974ec29fb122d818a2d2d5b29d11b704d3ac3b39431099b7b2f6aae04d28a2182b55380503127e4986ee9e8d5c0c2058e09e4592d08d013a4f088e45403720160622236bd56fbd9cc240efceb1b23c19ceafec49e9d5776ca9da5f7810ac979cf6ec5f678c09257abf79c9ab55dea00054e11b62c0a0ed4363d0a96a37ae1a323aa93bb1af253885afb684ed30caf5cf3b37afe6a6463a16f34cc28b4530c6bc6281f597bea476cd9a773205b96d47ed4b0bfeeee39b7ea44ff194911bcddbb161c2c0ce9488978b99e880d8e43624dbb4a567483ed293348d752634b2f46219575175e3c249b8e4e853142b66491aa1c142e7bb558955747cf2bd61ac802a2a4784d9fe080f771dd537d0ff928b3b04029d9ac03175c2d535ad7f3c123eff30c0437b32dd9fa31b2976e369b89b79a2a95e31aee15a462c5fe25fd937ce6b0795808d16163f2cea8f19b7c83913cb4a793576aa5c0ddfb6415326d8f2be0621017616c85ee46aa768b077dbec72311cb8a0f78ba0621a277a79af6607063839a52c6825a20e1c5818b24628862aff5fbbfe87311866b9956eeea7412ef69a3e4da84699b8d8b45c74ac96c3356989fd4962ba79fc26a92488304fd9f42486266b433eeb57368d26ae91ba4e7ed812581f790314cc7f44639aa7f6775618111369e8e2d68a6ab24388824bbbe3c3b0fbfb88635c1fc3216af1af40eba3555c0b0390a18ccba9e68afbcd21ecb212aaf82846f0a55793945902c48cac5d19332e23248a464529f4cca177137c508b6b13637e7df523254f24b8343d19164174202bcb00d5fe618b760c374f69b5065b1f9acf91af95abd7eb271586cae14fc835f633aacb4cd2ecd0f0ac08b688ef4d13b8a7b4c487ad46485bde0e340309672dc38af275e6ab525971409a39eff0ad134b1674db5d1f9725874e36d8730dc034b0a596c6e0a26e521c199d3e3f86815a64d148ffc394290b19f15390934b5d0da27dc8365360511628b93ebc375d2a531e4f4cfa031eeb501afe96d201c7b6078bbceb8e5d8615599f4c613bbb81a88f4eeaa57a9c008125073a30154044c422eaf2a32cecb15aab0774bb44e52b1792d154b8036ff9224af53e023fc011ab251d47d7d76e55c5015db1926d43c56d055feca1259de53ce98a2faecb5843ce17a3e83ccf678f5d13a8321f278a670c684e62b720e1eefd0abb2e9d0a3ddea81d5eb6e380a1c22af5587daac852e93a86f5e4293c18bb26b32035c7e5ca20cd2e3eabd3de0e55092a79a42c7ccc0aef033d6683043c2a29531a2ef1f503595c0e464ac042153c4685b062275f88bab93cc017f1ef9dc6f8aeab7b0b234f1c543420324512554f1786c82b37836238a4dfdf86f97d09ae466eb39d5f3ab159e2060be309d1284be133ab40abd61468c1706f7f9e57a7bc747479693a03edc8863dd196fd7cb2721260e42f4f606389c4972c74d357e7467b61cb7f455562015d29a59c7cafe0df03f26b77bca81c2bbac8cbccf8a65190b0c4e5ca832e82ce4e11044433aa397106cafc05634ad778270d20d8a13068586bc6b582ea24524fd921a5ee22dbae5296ee86d80f12b78bba26f8b42c1401b75d5cfcc4775c5cd1cb0a9248dabc8f82d216787b2a2780f7d13a5ee8c6ab56399a8dd5db3a152677d01eb8ee98d1927ca5069e0d1ba3907971a2199ba3634b48e570dd97d93729a6c43e4f359e2d89795218d52270a338a1f511b1f008cd04553c1a89caf987fd18c329be7ac2282084ef1789615d7eb7afd2261f606d3953b8863abe57796289e7761b01c3ca0faf2291287f9ad7027d7f0876b5f77d2a7f87dfa6ad4db905d4bcad042f403824aee3b4f8d7b5ab027fe1eda9d683db24f56f694b0b10ec72ba0df40bdd6e52b4a7b8d064ad46c7490c4705c14b06ef55222435d2d6316c7dfee83d225eaa431c11a4b85b0bafbe66fda1abadaec8eabcc2f8c688a7b9cb2942597f20cfafd7520892c535bbf6359a6989a84dd89989d95b8e5222c3aca2e8b0f8881d759e450466b75b5f36b7b723b0a212edf52abd591e7e545a3974b8b31b84b523af7b47e3804b5a268c86ae0bd7c80bc6b578b79f749eccdb4a00813925ef40259ac10bbd0fc4f2fc536c30f7c1efe68a52bee22f57021d23f445211b36fee6302202c9b62c6cf9064a2df424563f9f805e51c4092482253e4c258d53b80a2d26eec9fcdc104f7124d876a3ad573a7f419b0d67a41a34dac9d8f28cf9519b9c2677c9d1e720667d5fce26091d64c6df8b46c98b58017de0d055651e8caa3a230f57aef214ae2f27fa85400e34ce7087538fb6b854a6ad534780052210b8b8c90b4de4c2afbad9f58a71770ec186cfa44b61b53876bf904972078673845ec3181caaccf11f71a8e2a502deaf144f16df1df3bd81277dfbf6e1ae5a17363725ec31759b743066fbd4cafb5eda6e09418bc375f42e0dcbd4624dbe26c5fabb77152124f400e677004fcf3c862a9b5576140cbeb800fcde4409caae06286cb643842687bc6b89738374c7c759c911d7bbec8613f1fdd996d4b970fb6f2a9290a84c34d5fea6b8006357c8e6e9d4048d5a8ec476dab0b55e8358ecf7d27c31da86681f3fa74d072b1150223eaa21c4027378a99c8a2dbdc07d4062c401e92eddfff82841292bda2798c4e2ee9e09f618cc181c91a4dbefa44c410dff5cb705ee005e3a0470c13baddf9066109797a3b51e73a0ef229796d330aee0c0160529a4ca3b39e861ee5c4a0f78619007ecdf32266c7c42f0c972b91cccc45f6f8688b2692f298721cb5da39cbca9a5adef6969a2592ba421680241f8a5384bb92e70acf79c2f41d1171dfc6e1939887b9c8ea94429bcbf3532919fdcfcd0f443d3c95515b41e3f9c84bdcd3de1fd481f98482f667f2d017e3579208341e9a225f85516c8ca133cbada77598b6f596e6151eca377fcc8029cc99a879b26d975684173c0874509117ece4136bd2d69848f858c05e8ba3421499d7fb5e3e7645fc135117d8fdd1dc46bebaedfbad4dc7cc23fad6e696fe349712cf7579b4e63b38cc7d02a4c6a33ee4117d7ccdb86ea02cd791756b2a3c516d59d39ed83a8c328823f1934731820c187624219b487ca86edcc2f61a064e4e8d17f58f4a71462f3f0cfef6be95c0eb3737616de5954096d761a51534b36d798c651541acfe2e5bed6e58c45c46e014923a342409d49e782054f2a4877332e0adb0663dce0e84df0ff0d71c4c5ef18d9cacc8b8d47c78d53fc7649bab719334601c79a345d2101e65d4c3f1616741b24fdd9f7d6569fb8de67799648b323856c2ca96ac91823aa12249934c7050505258c43763ac9b174d55fee71c7817d65056c30d7853500656040621eff9f291e9db198772451cd3d58bc9421f6191a863777edf49125543b22ba127ea3063e1989bc4635d50bb22270948ba594b2066d91e1589aa0c1f476af8b170820ae0d2409df83819579621161ba55cc8d020e1ea68cedbccc9a737746d824b06e12cd2e3730260fdc52a68b6e142bdb997d8f93f25d241360285e7372939828fd54c015ba90bb4d553558ffa2558a43098644357860785addc455bf1ef4ffa5aebb8f002eb0f6a8ddd6f45edcc7cfe88d7ae7141b9113a4ab851b5fa8ff39c7024d2b3202841266f256b1f4ce4e4ea8b83d0067555ddd56fb1f8ed6cb5ce7340cdc403fac1dd3b48c373629202c70f1f95c0001b4f94a9b4bad9e1c64429b541ff57a2f2ea0c1fb2af8f0e490fac415cc3bcdd5739f2069e2bfb873206b8c8811619f1db39386cd2dd748c6301239b4e873f3d4618a43c52ef7d8abc0ca127881dc8809dc20a8730718c64c5a132cf638ec1e3b3cedaae55763637b4c6341baeff17e14c1e3c34e5d23befdddec0c7e25f0ebfa90a0bca07a0f5446507b7fb9cf91cb3cb8d24a111f46e7dbe3ea62b384eebecaf8de49b15b17f9cf151c4c3d33220b3adcf9f87222813ff2125120e77c638ab19c1daf9631d53b3efee67c3c40c9c23495848d4c5ecafc0998bf2704bb78ade666f414e9c6bb100b90451d93397d062b741a3ef20e7e6ebebab8238e2adf415775179c866eb4a2f628924674b20f711d2515d3b024e7e0fd3af6e116cfb0030709fb9bed4a441646c03dfa0b11cb460415ccb3c0999b738a0b07d55be741f3d2646a3d9e22bbfeb46b87acb6fcc2d83cdf44b656d2269a0734259a738d83a7429bfc99da7238b9497fbb10e0f623dc1f793e3be2326a7e55677e20ebb152f71794fd70d7698d0cff1334341b3a8eee38d66a021cae6f8dfe67913fdeb8d46efd9b77f28950aee7ce112f8f0dfc04132a99d95dfa923d391e06a578056ec8d15ddfb0fe8cd2790ed55f8254c6351d471c7273c2e61c2694bf6b0a1173cdc2d422246ca5cc51a9b8669dc331fe33bfe330d5167a244cf041f8444775562eaa8ddb92e0623d4c689db0919ece87a7feeb1ddd2d034a0d884d6bc8b5fa5fc7df00cf667dfc82c9e98e9b57fbe07fed391729034c467b46d3d16ed72c8ded069a117ca2d28e71ef4c37f1b1939590af19433ff2a784ff38134aacc59719673c53ed047d79e0c36f3acc03f64458826107853ff8d02de9362ffada79feabc597d019de6afb6577dbbefcaeaee801a49b1718151690b43a4d70b2fcb73ece3e3ea5fe2a9616cc6a9a494b8c6c7121497688652eb854e7bae19af3e5ae1a8df75138ce18621d11f15054ac8971f6ac084bd90e3909b46db321916c9e3c8950f3cc8e0a10dd255bd6b359c73dfde7de6e01538d03609253968177af14d327337bc1bc4e866c75ab5b69125f9e9a758f061418110a615b97f733f899382e2a14fa0ff0d1bda3b7449e1f54362697aef064fc9dd50692ad2b9ec286a1b7c1f845b09f4b1ccd46142f9e272d018eabf76c23c26b1831762a8a2c9152ef0cc843ebba20bf979e851e76141f41e817cca0ac60c1baf239a6c920a244b0be031a3b65d76eebae15bd9d49c53dd3e4303ca379f26840901f1da886d330d8ab4408a25eaa280ddb545b968b4aa59ca6f9cecbf82984264a3232b97498be0729f3d97dbd64823393b42794ec1676244212af382c37d53d66019a5db34c3d8ac156cac852ecb1c57f0f6057815460eb9f666c368acca6dfee0d2b7bdacd2148d74cea3bbac8b528f00809f403b1964b9d199d2db93363a53732efd97c66f28cb51442dbfb1c912dce078944e02889f9395ff2655e2d7118a11848e3ca64efb5eca119240b537b5d4548a744d96962e9b2a99af73bc4d753df16fe75a08005da7773b7bef68a44f56951a3a93995c5d0516a8b2c39abd2057b3d86ba7fe511dc33292596fcc4d3c67df88812e26d3628527752a81c8ce6eba711ae7ea8748229caa2580e18937f45cbfb0041f0a9254eade41c95ef4f390db6174e6b0ba89af344c215393b627348ebf00901e343c1599c832bd2cfb751750808719f18119ab3fee6baf5fd4b8bf7c92a271ff24576984610abaf1b2297c18a2784f57d7aad26f52c0d60fa6cd2484492a1524dd14924eebf53348ee2dca0643ef47381541b91d1843817067a94bb7c79e4e08c4d81c7266afefeda4a0db21b0db629cedf13b4f6ba11e6480116a7c9780009ee0e1e2bd543dbb68bea356a6010a1e3ded4229c4d8035420833fc83337dce4bc483eec76f479f33be2d6a8fb013813952eb66bc247f37f9def7e9bea0f277f380079aeb48c192b9c2c24f909435586bcb994ae6f75eea111c8842b855f90df21e956f839aef89bb1b96899e343cc66814ac08eed4defe5e2a62c54cf83a27f0499224e02bac652aebe6529143372c483edcdbfbdd5a02106fd3651875f23724d2154c0138ace745d038d6cf6b233ae2bbb8313c8ab59dc896844da5a6610845d9147e57518ada61b7debcd66111d609587632cccb936751d099639735f09f77b2b9aa1177e95ac0f8070bcbeb01bbfb3e7a638811d919016ed628865e2405aa17f5129ea43a836a76ec7cbd943e20b1e388d47db2df3394785e27dd7efbe350ccec6057af7483303a6a588d4005c05ff7f3c90dc5b9a0f95c3c541ea40615f6b0ed6cd3787e608e7612b52199062991a63511b3055dfab25b18ab55948d9d529aff91a0d8aa7705afc187e32d1107c07623a309390f32c62e1abb830bb090a7dc4c6817b554720fce354563662d78cc2bcd0483149b304ab86502f0ad36ba2c262fa60b1b84be2499e56fd72129c0fdf1961214df8dd6c9b831109fadc7464df090aac96d221ab387b6eee3080ea0d94e195ca795b5d80c42946c0c353e39075bb1aa4f3fac36a77445cd57b11b3c7ef0e89aca911be35fbb72e1e2fff84b7ece66bb8b3dea52b0aff473475d298f730e79ff77e955856817514908baaec1b53f39f9d5dcbb1cef29536a483a1845d8538e5ec43f634914bae4f2319579adcf3f0fbb151c80b54f02be54be612f9417f916579863dc16da5af07fab05db2094c77612b445b7b9784198abbfc8387486449969118d3a83142022017d69b939d30fd6b1c5ba360c0e37f264b5d604c7ebdaf327874b22d4092dcf35aad93f0f0b3037f90000d4a1892dab827d3c2f1ddede948d330706ef17bf442f8228c168401541ea89a837f81cfffde8480e1b6014f2f5d3132ee740e04622600ad93dba41e39267f17554b29102390f8196094de19e8d966d3c03a9e3348494b54045629224612734a1304ed76d17a465c305675bf99b7aff1950dd2012e6cbe8d15a9e35a7c16081bf647e2766c7b8e9d12ef8a2c94fd7531f6d1a7ac7ce6bac028917a2a71c8eded4f3ce3be8c7d98422094e88414338fc25839a582b5965c71179ce3ddf7a645bb163f3265f4bc4ebe91ff25d86520696d237741ba73084070e23494d6a890e1ae748091a6936a3137d16dd14571f86ee118bcb17c8ecd2ed20c25cbab79a78c53fba1d5db4fdb15e6a124bdb2d41fef080e7318f78c67451beff04b58a182f592fec3feab6011be4663dca8b62ad2a8e40c716b3c8a170e1574517c44e146109d223529b0de480aa168b5a4b0522cea03dd26a684b08c58aa2e1fe41f44ba8837737a74a4681b0508d9604aa7484c5ec68ccb369c056702b03f75722033597ce5b55e5a88f14e3ee23b1c96f5d38604128cf2e901a745996f4a29d412e5ce1b4ee82cf2f248aa80f8c8f2eb7b6951f1b0910857a17edc319bc76b1a59fe05f9394a5b16a91ef15afba9ee2e115c9c9afc51a8d7c4a61cb335724855007f564fec341342d53717023118917304c2dfcf0bd79e456c94718b8388767564d6055aad1d505f36506fd6e8cb1a10f6fe8a67e772030dab78b43ec15fae0322fe991944be86cd0bd78ba00110474dec84dd9597a602c4a6c97a27f13159ff1e32fa63e28f63278b09c9d051cabf8bb49f2780d4c33ca061ff16c6288ff29d46a22e31c719dfaf6087590ea8197c066f9057e0c24b40f5eb205b730985daaf7e7d85fecd7449677c0ec1abd7111d795de6de4a6ab30cf266987dd55569f2a705ae16f51de738fcc4cc218f2cb9dab8af509ba26ed93ddc7a73028d9187527fa6d554b491faade05852eb50847affb81b58d2501989d5b4a9de4237b33bea154c4d016b08f3e1c39ee61382fb500be2123ccfcd30e37c88979a19d636c9dcf5fcec822e5558cb2939e40e1af039aa942b503c9fce95f8ff51a2977f58e4d5032f9099fb4783b7764dd1299a1a89f21c1f5e38038f219e9a4e9d6c6edaef70132ee1f2a580227b256c9139d339183e278277a9dd260bf7d8a9853c4185f9b6509ab9b67a3563ed0b9afca6f2db28258025d8c2036cc4cd9fb4f7007dca96597c26476def29191cb0c77c44df80994ad3c79833820ce2f1108d17a083fa857397d2839c37899ce2e634578b3eee0135f66cfe1aeb7bcb8732fedba16093a6e666321236f56e285058d00c1bba2db3263844e4b0ea013e7a8d370ecae9ac7a9620eaaa244b25be1eb6e71368f550736d3ca13747edfb6254c9a373aadcc30a148907d39e81bdda8a53e8644b39a1625de851fc5d759686c63c76bb162b244415175eb0ccaf3a63d4ad910369e30313bdeb175ace63ad33e400e642dbdcb472a336d82d2f818e6888e5aef472d03fd298a5d14b8da432b72e6c5f5694887a7e4e165bc41055379bd204298d65796d5351a9121f949904bd6c87510649127ef5797d432c6c759939c99026d57076db225d7d0df01ab9b9e3e1221e7e323ff752eb7988cd2b2bbfc8ae7fdd23c3f7cfd2ae2679014e986e3c01b6c66a67fbf69483ad3e89c1f2dd0867f411151e048d81212c0b5e2fbba9d317f283f97c9576180121f41856355b906a22c69869bc27729db5974c5b1257b35d186dcb4e9f7bc51dc331c0ff9baed5955e5052a6ac7743b"
let x = getShieldedOutputs rawTx
describe "extract Shielded Output bytes" $ do
@@ -362,7 +442,8 @@ main = do
let a = decodeSaplingOutput (bytes rawKey) (head x)
it "amount should match" $ do maybe 0 a_value a `shouldBe` 10000
it "memo should match" $ do
- maybe "" a_memo a `shouldBe` "Tx with Sapling and Orchard"
+ C.filter (/= '\NUL') (maybe "" a_memo a) `shouldBe`
+ "Tx with Sapling and Orchard"
describe "fails with incorrect key" $ do
let a = decodeSaplingOutput (bytes badKey) (head x)
it "amount should not match" $ do maybe 0 a_value a `shouldNotBe` 10000
@@ -374,39 +455,39 @@ main = do
let res = decodeUfvk uvk
let a =
OrchardAction
- (decodeHexText
+ (fromText
"248b16d98dfa33f7ba69a0610a63b606699da76c288840b81d7691ee42764416")
- (decodeHexText
+ (fromText
"17fcc27cce560733edaf91439a8020c4a029a4e7d5893ce024d5ff4b40bbd0a9")
- (decodeHexText
+ (fromText
"34796d541864832acca43f083892e98a46c912802d5643672d3f25bea177c61c")
- (decodeHexText
+ (fromText
"a6d2ca10e3fc7446e372266ef45ee3dc0ba373bd378e6bf3092519a7f272bd8c")
- (decodeHexText
+ (fromText
"08beafdf59110b5d045e4acc13731ef1a27bfa3a9cabe1d575640c18f18ee6697fbb132d36e982ae3eadf5f37fd35f42c2bb07def14759deab1fbe2f98dc1d5913e4a6ef388b714e2cfd6d89ba2302800e02ab5f45e0e02e3895448518cd8afd2c37bb48a66d8b988a37de9d0838d92876894a311bb9f314ba842e5c18ff7a3d8c7f0ff1a7209e2d661595db8f4a4aa267b9593258914bf63c09286eeda7c9b27ddbb4646208c0d03a8fbdc5d96633335a5a65316f5b25189bdce735bdea7e900de56d3b475ae51b7c35eb7ae79ba104baeb0a5a09d1cd8bb347ab34fb26d62ddbf024f5394710626ec0a665b9c917e65b00256db635145164a0329db7bc5358f435d573b2662adf8a6128801825ec8fb7d8aeef567d35c875ddd784fceb7620355e3f056a648b39b4b2d29a1f5e7b7c4ec5fd2b1874ff1e832b308f8644e83878d90582b9a6fd6c293e19dd3e24dbe1b4c15c96608169843d46551900a8cb787b15f0f1696b736dd4c8ebacf1e3288b14e469bdc004fa8557d6b1395700eaba59334906bb012f876e4cd7acd2157719ebd2e28bd0cd4ab4ac458f8848e1c30e729803dd47102200fe703932a15c3618862ec83b40d3aa0ec2343641bcb9afbf931ab21aa4afdbe7e51deca24283c2ccab0eef6e07aac5a4bf3a775bf7d2ddfc8d8766c3bf8e35df1435cf515d93c3b9549477bd9f53d133f05dd256fbcc0b13a63e3e7f8cce6301ab4f19c114f5af079f8c581537458e861b553218a890ea3e77fb99781c7088cd43c67c155ec611c1148721cab5fd0168e4a5ec390b506ec44145474c")
- (decodeHexText
+ (fromText
"1e40d33446d9f0f0fad40f8829c1ffe860c11c3439e2c15d37c6c40282f9e933dc01798c800e6c92edb4d20478b92559510eda67f3855f68f5ab22ca31e1885c7fa9d4c9ebfb62ceb5e73267bcad0ba7")
- (decodeHexText
+ (fromText
"63d0d6e8e92691f700bf8af246dcd4ae1041b13e3969f7a9d819a06e0f9429bc")
- (decodeHexText
+ (fromText
"fe362be160accf2794841c244e8d80bbeb80b9bc95bb653d297a98d32bddf5a05dd5f874891d55924a83f722f75f576f63796770c31074067694cffb2cce7a2a")
let b =
OrchardAction
- (decodeHexText
+ (fromText
"8921446787f1bd28fa0e4cc5c945ea7fc71165a25f40cd2a325dae0c4467d12c")
- (decodeHexText
+ (fromText
"240b08b7861aa78989c68cbedd0038af9b3e3456bdc7ff582d597df571d54da2")
- (decodeHexText
+ (fromText
"e1bc8ccba69ab9f429bf735417aa005cf439d27500b0d3086dbf1be764b42a36")
- (decodeHexText
+ (fromText
"c89c58ef8553e7d09ba4090654edd1a8c98763c44d3dfb9dad18286c7ef363ae")
- (decodeHexText
+ (fromText
"0eee1ca5a3a4959cd4b8bc277e6e633f950680c4acb978c14ad8d944a784f46771c9d666a203ca3ac693943d79dd23f8b76a734a62e81932cbe98e8c851f47a11aaef50249e53151f38f88262a4bae8cf26f5f8b2db1d165aff9b57b64713a677c167608585c038e34ca7bbe468e5f86475ccec0a4a8b9a43b56e342e77a6bd09415787c9f4a1c6f20599f57545f1ac32c3a338d7a5bb2d35456adb880cb65c1455969e10df5d94b8c74b244e7093b1a88cc10697a7c2f4d34b6eae3296e64b820573b4d52e06b4427af5b8f5d6722d3a93fd85da615fceac732976ad2c1be4150b4821c149521f5419ea0746fb132d47f593cfc8a3aab6b2b4480c12fadf21280ccd3142e7188d9e5aef3fcd8c5dc0c066dc975bead023ef7f89a486b615b146110ae68b703a8349a5fc225b26a08b2adaf36fb44c9ad1be59d7ced134eb84e3f0b4aec19b71b2d26e910628a11446b97c5e6bbf97e1befa4e04b5947f83c65161b92f58088d28e57adc2a2873e27008e29772c5803502842045cb355d1ea5a9d27c2683dcb38cb49d26af39625ba99b1342f700387b939e7ff6c129417ca8836fe1e96331e35c8bc0763879e8c17cd4535fbcb27a2785c0a47294e07cb54837bb997df34882ce0bececc6adca365c76fc7533cf0503458937dcfb6058b016dbbd399b9f0cca44cbc881016f4957b5e10daada3393d5b2a4cb15ed983506d4d264f9855ce2ef87a7d4a1fc03293a22c28a53c4455447d546813fa33008e5d2d81848825fae2f437ab9575ba99c230e78f4b23e575e7647beff0e4c4e2b0a1f7320e9460")
- (decodeHexText
+ (fromText
"d727aeec27bb0f7463c6ed4f5b3f4085cfd3e7218478db0dcebfca875e025320fb64bc4062251823859e963446cadd9924c559e5f981480df5a4f036daf5a8033d4c8241e128902aa1aeaf6adc149730")
- (decodeHexText
+ (fromText
"98e72813aeb6ea05347798e35379bc881d9cf2b37d38850496ee956fbecd8eab")
- (decodeHexText
+ (fromText
"cb9926f519041343c957a74f2f67900ed3d250c4dbcd26b9e2addd5247b841a9fde2219d2ef8c9ae8145fecc7792ca6770830c58c95648087f3c8a0a69369402")
let decryptedNote = (`decryptOrchardAction` a) =<< res
let decryptedNote2 = (`decryptOrchardAction` b) =<< res
@@ -420,12 +501,782 @@ main = do
let msg = maybe "" a_memo decryptedNote2
msg `shouldBe`
"Hello World!\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
- describe "Address tests" $ do
+ describe "Wallet seed phrase:" $ do
+ prop "Generated phrases are valid" $ again prop_PhraseLength
+ prop "Derived seeds are valid" $ again prop_SeedLength
+ before getSeed $
+ describe "Optimized spending key tests:" $ do
+ it "Transparent spending keys are valid" $ \s ->
+ property $ prop_TransparentSpendingKey s
+ it "Transparent receivers are valid" $ \s ->
+ property $ prop_TransparentReceiver s
+ it "Sapling spending keys are valid" $ \s ->
+ property $ prop_SaplingSpendingKey s
+ it "Sapling receivers are valid" $ \s ->
+ property $ prop_SaplingReceiver s
+ it "Sapling receivers are distinct" $ \s ->
+ property $ prop_SaplingRecRepeated s
+ it "Orchard spending keys are valid" $ \s ->
+ property $ prop_OrchardSpendingKey s
+ it "Orchard receivers are valid" $ \s ->
+ property $ prop_OrchardReceiver s
+ it "Orchard receivers are distinct" $ \s ->
+ property $ prop_OrchardRecRepeated s
+ describe "Address tests:" $ do
it "Encode transparent" $ do
let ua =
"u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x"
let msg =
case isValidUnifiedAddress ua of
Nothing -> "Bad UA"
- Just u -> maybe "No transparent" show $ t_rec u
- msg `shouldBe` "Got it"
+ Just u ->
+ maybe "No transparent" (encodeTransparentReceiver (ua_net u)) $
+ t_rec u
+ msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"
+ it "Recover UA from YWallet" $
+ ioProperty $ do
+ let p =
+ Phrase
+ "security expect junk hour people bind law hub between topic wink cliff spirit scissors auction idle figure option wide useful swift prison cushion round"
+ let targetUA =
+ isValidUnifiedAddress
+ "u1qsylqauvnhw8tsfe3cldcsj3mjrfqzgaf3mt8yzlkjuvsf5wzj223yvrt8q66qukfqcc80x3z0mk6ym6pm2f0hukzkp6t4wj78h85t6kfr2u9mqsfhdd73g3sc7ezy2ut3rtq5jmejatwv4xqqd6l8tt9fycer8kdw0gz6e607nkssqsc7kd7nk2yfz2hpvpqhdg39wxalpjzhe34j7"
+ let s = getWalletSeed p
+ case s of
+ Nothing -> return $ expectationFailure "Failed to generate seed"
+ Just s' -> do
+ let oK = genOrchardSpendingKey s' MainNetCoin 0
+ let sK = genSaplingSpendingKey s' MainNetCoin 0
+ let tK = genTransparentPrvKey s' MainNetCoin 0
+ let oR = genOrchardReceiver 0 External =<< oK
+ let sR = genSaplingPaymentAddress 0 =<< sK
+ tR <- genTransparentReceiver 0 External =<< tK
+ let newUA = UnifiedAddress MainNet oR sR $ Just tR
+ return $ Just newUA `shouldBe` targetUA
+ it "Recover UA from Zingo:" $
+ ioProperty $ do
+ let p =
+ Phrase
+ "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest"
+ let targetUA =
+ isValidUnifiedAddress
+ "u1trd8cvc6265ywwj4mmvuznsye5ghe2dhhn3zy8kcuyg4vx3svskw9r2dedp5hu6m740vylkqc34t4w9eqkl9fyu5uyzn3af72jg235440ke6tu5cf994eq85n97x69x9824hqejmwz3d8qqthtesrd6gerjupdymldhl9xccejjwfj0dhh9mt4rw4kytp325twlutsxd20rfqhzxu3m"
+ let s = getWalletSeed p
+ case s of
+ Nothing -> return $ expectationFailure "Failed to generate seed"
+ Just s' -> do
+ let oK = genOrchardSpendingKey s' MainNetCoin 0
+ let sK = genSaplingSpendingKey s' MainNetCoin 0
+ let tK = genTransparentPrvKey s' MainNetCoin 0
+ let oR = genOrchardReceiver 0 External =<< oK
+ let sR = genSaplingPaymentAddress 0 =<< sK
+ tR <- genTransparentReceiver 0 External =<< tK
+ let newUA = UnifiedAddress MainNet oR sR $ Just tR
+ return $ Just newUA `shouldBe` targetUA
+ describe "Sapling Change Payment Address generation test" $ do
+ it "Call genSaplingInternalAddress" $ do
+ let sk =
+ [ 3
+ , 183
+ , 26
+ , 151
+ , 89
+ , 0
+ , 0
+ , 0
+ , 128
+ , 199
+ , 189
+ , 33
+ , 169
+ , 114
+ , 194
+ , 50
+ , 0
+ , 139
+ , 140
+ , 162
+ , 100
+ , 100
+ , 35
+ , 58
+ , 226
+ , 6
+ , 47
+ , 232
+ , 34
+ , 214
+ , 11
+ , 173
+ , 142
+ , 40
+ , 45
+ , 163
+ , 190
+ , 207
+ , 49
+ , 130
+ , 158
+ , 113
+ , 232
+ , 251
+ , 79
+ , 98
+ , 77
+ , 195
+ , 196
+ , 40
+ , 42
+ , 113
+ , 133
+ , 35
+ , 211
+ , 68
+ , 146
+ , 104
+ , 5
+ , 56
+ , 244
+ , 162
+ , 55
+ , 239
+ , 55
+ , 112
+ , 37
+ , 38
+ , 189
+ , 183
+ , 121
+ , 201
+ , 1
+ , 60
+ , 158
+ , 151
+ , 141
+ , 123
+ , 250
+ , 95
+ , 169
+ , 123
+ , 208
+ , 56
+ , 103
+ , 74
+ , 85
+ , 49
+ , 152
+ , 207
+ , 245
+ , 216
+ , 58
+ , 37
+ , 0
+ , 127
+ , 186
+ , 245
+ , 234
+ , 47
+ , 68
+ , 11
+ , 78
+ , 76
+ , 12
+ , 171
+ , 37
+ , 63
+ , 172
+ , 90
+ , 111
+ , 94
+ , 88
+ , 152
+ , 211
+ , 53
+ , 243
+ , 142
+ , 16
+ , 195
+ , 142
+ , 50
+ , 14
+ , 13
+ , 32
+ , 91
+ , 5
+ , 82
+ , 182
+ , 121
+ , 136
+ , 109
+ , 125
+ , 165
+ , 125
+ , 123
+ , 226
+ , 54
+ , 60
+ , 34
+ , 62
+ , 111
+ , 167
+ , 88
+ , 254
+ , 113
+ , 204
+ , 47
+ , 181
+ , 97
+ , 18
+ , 220
+ , 46
+ , 51
+ , 160
+ , 62
+ , 16
+ , 199
+ , 143
+ , 184
+ , 200
+ , 209
+ , 124
+ , 154
+ , 175
+ , 29
+ , 216
+ , 48
+ , 201
+ ] :: [Word8]
+ let cAdr =
+ [ 31
+ , 232
+ , 31
+ , 17
+ , 196
+ , 178
+ , 208
+ , 227
+ , 206
+ , 199
+ , 105
+ , 55
+ , 147
+ , 23
+ , 151
+ , 206
+ , 117
+ , 59
+ , 249
+ , 162
+ , 218
+ , 140
+ , 189
+ , 17
+ , 60
+ , 116
+ , 106
+ , 56
+ , 64
+ , 203
+ , 152
+ , 52
+ , 155
+ , 133
+ , 179
+ , 118
+ , 47
+ , 161
+ , 70
+ , 155
+ , 21
+ , 22
+ , 41
+ ] :: [Word8]
+ let bscAdr = SaplingReceiver $ BS.pack cAdr
+ let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk)
+ fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr
+ describe "Zebra response processing: " $ do
+ it "Read block time" $ do
+ let blkdata =
+ "04000000c732575a3e94b7464c84b35935c2fca26e40d37b6f3278bda3d941877b192a0048cb59da8eeaf9f622d4537ec2a27918415f444d0bb143e8091b470a18a438c117fd96323fd1a9c775ed793154de0e8a2023b551431e89b5ce6f30ef047388f0bdbb0266fe81481f2c006046a8be3bca5b8819209d053a88184bb48753171accb807c38f46d00000fd400500b6a304cbc4e6caecc9b3a29ce42d71752bf5396944e28465e29d1bd7e7f3c680db3c4b85a1488d7c4e21a70c1cf50cfe4e7f70a4f1abb638f144afb90dfa69adfc7c981fd8792df19926da695356699ab76213094431bf6491b80572c310ab8e51c27cda39c771371dbf7adc60089ff5f9614577a92ce3a1e452d5d8280e3d970322e1fc5937c53369623d5e58e57672b48c2edc7a86f7d7de62e06668097547b782a01d3794a50f131695f7de3e4bf038b12abffd29b8eba618820e143ba0be57ccaa67f64511460027efb065c6ddff501376744c6b68ca01d884025e7f939521d230b1a46016d66ad3ab2262dbd477b38bbec234a19425b939dc0f1d868035c3ef99eee635c9dfd62235ede6d62f652fec499127a152e7be4f959a9bb7fcca9b335de98d14aee1330fe37f1ba28be26902a1cddbc3f67f2f6317093d3f278c1c0156e5319a0b35360e38719911b8012a8cc34893f05dc28a22787f3e0461836aef602110e621c8b9d78e991ccc0160deb8c8921f6fd13bd00d47a170b9d0b09ffd57e37467400c138823dce7c12027eb9f919b8d5f4072b331a042f6f5ebb814c6a903110cc97161787d1eb2716c8fb98d747abf321a0b079f3b6a1a0329337b4922a0d739886a03f0d475d41005733f26c675dd9694712d6ee07772a5e8f6b5871789ac14159beced65d3b2196725baa4ce04f059a7034ff0d7ce081e94d23e0115b9d8f0d99fa5b4f9771b4feaac881ac80f178c3575cc70858e40f53f1d0406253edba75e2bb6f40eb2ef4ece3df2b8dadbd3ac168b1743a0cfb68ba81ca4c838435002587852e81e0a0c3c77e11218df892ac0c2f343967b127fbb822712ceabc459ded7f3ca3e95ed5ddfe6cdfe5657da1b12ee47eb63af7a839cdbc1f52f51e349dbf2bd7d8529f814108723d97fcfda7563c4fbe3a24118352e4706f9f91406f11acde3a570e10a258fa6a5415f57a26988249a87e57f192d0408b6f56c2ef65d5e37597d144e8fe1014a4f552dc8c213b0a70b2ccff6747f6f1e0d598715d8cb0da37e0331c01da1f9e0505e9d1907628427d5d032a29fa0e14b75289b10c7d3109b590aec2a4debda9eabcbd3163f6df67efd92687d0c492a71d48b2995a658b34e96814e5674a62e3738efe53e0daf4bfe9d1031e45a246bd3b32c6d581b71c0b60ad711570bca3848c38ba5aed74d977a185d199db721c2677d4868e5afaa6cc2c7369898f58f93738681118d529194d41f0cb6103915d2e99422db387fb90046eae764a79dfe8733a7f68439d0f37e773d16ac7f0ba3a8ce0fd6746d038704b2a0a979659a859682581fee0be3fc50eed13e85e6b52f398d16b59f5f738c1eb289b3861d636b961d9220c1f76bf8a8be3ffd1e2472461202285aa1c5b7eac58564aaab0beedb65e1078517e031888aa3cb8843876aaf19311d5150529a10c9d9bce1538f7533aff126e518ef1ebd919f26aa3869ed1ced167cb1de6075897ad2dc461b9156922c4ee387f8e1635e95564d3964e47d39e6e82079e0220df00614ecd4fd31a5f3d13c9191fc65037ad3cd651f055cc795260f21bb0ba8acd7e3b65b5ff81d5a1b187a777c826f96637e00f8147c4bd43753fe9e0cd73564b53a0e9eab27745505ccd2e4a1a217307c66030ca115580ce6ecb9a8b043ca41bd2de15abd13838d130d3cfecd8d0e3bb6c982bf1f9cdc799c3256e2d722d3f13cd7acdb9d75d7e36483b2ae01e5dcb70db824eadd9c3955d79557d612a879fa6da1da479807640e37bf25a22091f1cf6fb5293e60e5616416fb2adb1d9892258e8ac6859978159338d3b6e2063b9d842a24d61d0616506d5088fe76025df35619febd57f92e30699a0df8731bdbad0775922bb12fb1697d9da833010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603204c2a010cffffffff0480b2e60e000000001976a914278aff0c0f8734638ce81aaef4ab0afddd36552888ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000204c2a000000000000000000000000"
+ getBlockTime (hexString blkdata) `shouldBe` 1711455165
+ describe "Raw transaction from faucet" $ do
+ let h =
+ hexString
+ "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02"
+ let h1 =
+ hexString
+ "0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000"
+ let t = readZebraTransaction h
+ let t1 = readZebraTransaction h1
+ it "Transparent output is identified" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let a =
+ "utest13dq4u4dnf3yddw8lq2n6zdclshra6xsp8zgkc5ydyu6k20zrsscmuex46qa4vh84rgd78sqnlleapznnz7mnzx9wv0unts8pv32paj8se5ca3kves2u4a89uy6e8cf4hnarxydxh7hq2e9uu39punfmm53k5h45xn9k3dx35la8j7munh9td7774m8gkqgc4mn40t69w20uu2gtks7a"
+ let a' = isValidUnifiedAddress a
+ let tb = zt_tBundle t'
+ let myTb = fromRawTBundle tb
+ case a' of
+ Nothing -> assertFailure "Couldn't read address"
+ Just addr -> do
+ case myTb of
+ Nothing -> assertFailure "Couldn't read transparent bundle"
+ Just myTb' -> do
+ let pkHash =
+ BS.take 20 $
+ BS.drop 3 $
+ (\(TxOut v s) -> s) (head (tb_vout myTb'))
+ pkHash `shouldBe`
+ maybe "" (hexBytes . tr_bytes) (t_rec addr)
+ it "Transparent input is identified" $ do
+ case t1 of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t1' -> do
+ let tb1 = zt_tBundle t1'
+ let txInHex =
+ HexString
+ "Km\237=.\228>T\DC3\\\EOT\249\163\ENQ\180s\215\215A\187\230\243\131\170cn\ETX\233Hp^\r"
+ toText txInHex `shouldBe`
+ "4b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d"
+ it "Sapling component is read" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let sb = zt_sBundle t'
+ fromRawSBundle sb `shouldNotBe` Nothing
+ describe "Raw transaction with Sap spend" $ do
+ let h =
+ hexString
+ "050000800a27a726b4d0d6c200000000c3c02a0000000111ac7f9600d034fec892f50600006b09ad88c990a8f3c5bffb6856beebd0ce356c5d1413c63a9a88db71c3f41dc12cd60197ee742fc75b217215e7144db48bd3fa55de211e0c8d7da70aba0efe17124b5250fc9fa095c8e50b53168142eb7ec402289a84ab762ddafb0f972fc07c61eb45249922752dfac17fd704a3b6156a6955286c04587f37fc781a9a0b28e92664dde2300c6321dd21d367c16b4c2b510e5d0a4358624e1ceb816da155e588726f783ed6bf396eaecada3d3bde7a0722419014c509a7fe264003af5388c5c8cdf2076137b26cf66383b41dea2af415f3d16767124616609b96789e2811f7bc8b36c0b746d225c0aa2c98b370fd39b2015eea43d523e5fbccc7ca127e59bc92ac8ad32d05243d278a26f07f33f45df23c3e8dd40d72af35a70068cc3f8445478c9cd3a56d3515e207a1b95c39c9d8b45e66827cf63e6018bf1012a47aa23ec0174735949f62036df3f677f92d3aa10365e2d358d92f77e9a6294c245eb8536493e8480f5215cdf7c863623727efa188fd8679f95318d0072915dc2be8e77b5778c48d3a2ea614990984c29dfaf7c582d269f0e9ffe6e3c475c99dbf5f8756c334b3c7b5954d68549c65fc2648bb389ba63397525829c48f7f32e43e866dfbc8ad86f54a867e592524f9c43b007328b78220cab4537c2d65f64c819ecc19ae09e17b3c84208cc169c2eaf8831d452dc83527a966b288aacce6f261aa8ed23e2c0613c48161c1eb0199915ee9c1f9cf0e7e5445539f5016b3ff701408b45557154a53e48998b49a2d149a67202caa6b585348b42fbeda770e084128760c742537743ba8cec4f997fe575db9cb370d89278c607a598ff06f1ece047855ea0a3d808d84272e22bcdeb2eb5bbe5754947be5d6fe42038d26f13313e039adf7e1091a901a6daa24b247b13c810eb5b50cd6040463c6bfa209a792d77eafb0f063167b11777d97ad05ca7eec1f1a45698e33f8e8b11a0ab0ad16ec10e4abba6de69255e0e00c7e712503a107012a46fcb9c5186a1fc5be96dcc279fa3dd18e69ee4a8ba75edaff105716b4751717aa818d514620ddd13a6716729417571a342dcc8a6f341d05db7614e729491095e4bcc6fbdec2908b6f74d7db81106ab2e1c0ee6217ba3cbb174232a42f2e6aba7e92eb2934dcd91c5a36eae1aac5160bf6076a39baeea84b8bad043c565eb8c4206a74b68c9342f9d7b1b31eec86bb9c5c73549313dd2cbe3bce55ecfdd952f3eb4dd5fa7c2000727b62a0faeb0fac1cf05246221c37560359c44610ee8f93cdd1179fc35fbd808152f507e790eb2140faccb2f6c9897a3a219d8752388e846c7277d82dc80850aeef2de46eb2d81563d53714d39e3553f5133abeae6c487d10664e1b10a90a5319adfa7471aa429095bd272ff4d3df227bdc6075e0cafba54201bddc44edcdfd2317f682f40a25e582273c4845b550b17724eca81e89268eafef9e98533b85bb2c54cdcb6beb4d6f2fc0459a2b36cbb43fcbab720678553fc930063c64a09445ff637c12d0ade4acdef1e19e8481a0b7c2dd968a2ceb7f50efbb76fe21f49d8829d8a26b10453626593a694586a828705c5ab02cccd60e351171aea3e1aaeda07cd960e0f5205019766ff3afe124a47f4ef16f0e671e1968e662c7974ee54872538c7019cc5396f165013f4c482d400ed4ff5fb34189c75e92d336cb25563867215dc3aa195f736e06657c9e27f1468acd580ee51ca44220be83c215c1223758a7676d376ea0d0aa74b73032b85a45f69e6f8d4ec3efb6861d5adc35f57e6112dfa7334ba6fbd64b14498a89d8596423125b6486d9208937e9aa010831edcc168be8b7abe7ac21cae4794fe5f019abe1715d5d8601b646767d396e1b7fb8ff50aa43d88a2426545270093ce80be1aaa849dd2edc0310e3cb0bc13a23108776a44e3250fe5aa89b6d62159206377e03e302e3dabff9ecd8675870c7d55e33cb915d7e73b765d1c61f9ba0d504429fe99516df0534cf6a5ad53424b9be39e4835dfb301ae5cbd9edd628f0cdb6c54bd4986a9fba65f60e833bf17b461746ffcc312d9584a55c55c42c8e760d017be56f54a4e1423648c3877f8a44e3a56c21bf2bf85550f3d8a8f040c8c6687aadb2c4d1e1613626a11d30fb25340f5f14b6a7752c5e6c7b866b10f1155360f35ddc8f07ed591fa3366948fbcde1a16c35a589457ded90b69675bad01a1fa9386e68dd2001498d41033a6effe882d9e864ea7991e0c71b3e65c158cba64014f800000000003b38927526d5fc5a1b2f16addcd4aa162272f280e977087de02ec2969f10d25ea3de9a5d3d3975b4b683897bf828be631813bd5b949ddf9c3c5b103b69aa483f5fc46485bc7a5dd51a47543f57d346e680e12d9429ef7c21cacf407656779235d205dffae23666e01d92fb18c063d1bd7cd00935f029149c7e20b3d72b1b951117f0629b1ef1e49047532a59fd8bc1817343d45e832078806eb10304b8e034fdce70464d33cf6c66e857fbda170573a7965f8eed58e6cb7edec4e9f8a666fbca0aeeca2d4d161caae23005f6e0adf6859c8f1bece409aa5d44d285a8e8e9fe86365af4e92384dce318724443e8a95cc790df47cdd3e8aa9ccf19acc5eb19b4cb517c3f63e59512c037a66312786cb6cd2bcd4480e0a21ac63a83b7b9c84d3207a6410aa51a60d15b5526e3ffb4cbb02ee9f43f7ce316ef01dd75544224481d4b8ad2a8944928e2b4432812d1b20aeae3b51808028321588952ea70afeef1709eff5a75463a2201299484ecc421c8452e1700ca9f4932b0aba6b5d46f36e80f2f0297eaf7f44a79cbd21046ca53073ee60d5f2ffe2352b2538326e3e7ed851e8a62612a9b974ceb5757835b974892b049a7a0c531390a4455657b5b5afc5f0f09f8bf838313f403c96c4c2c72c4ced6e642e86b0eff220c1cd3388ffc902af68f87a1b7e8920d7f014e6cd2694b926fcbef23dc645cd75b051add0fc2f99c0346d45c2521b27a00a13dc9754306222f54a2b4f3058e80e0821d6c78e5e80a509525631f34c343567513636855d1c318c49debc651212938c487e069c390868ce700c4d19e1e52eb7b536e1568d03421c3769b920224113bea4d345510bd0393efa1ec22b87e973c663be75c84e7278108ada31af0311796de927257f59823f1ee9955f0f39b618054476658c165ef443d169afe46007ac80a0efa3d678c2707a7a001812f02dac999796872d3328eeaab4b61cadb8be9c4ee0ff9104d417ba71076054735c12fabd5d78f6de83a57f46b8d071e3c273c60a226fa6d555fa3580002cf3b6f3e50a8f5616eef66e7bb28523858315b3cde381003ab885ece89973aa4e228cd5da0d24ebd876097e7664a2b903430963517bb895435703029ea30081a874d15f4997dec3b89cd728075a0b21d838a57052f02fad03c1a3f74427d149c6225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631585288b7ac6c67346b1d9ab1ee7d24e8cb7e35f2d2b5401a89af6a1648d19f8fcd14973c4c2db543d772b81bc363e707bc534f00b872df0e3d4c73e9bc513b2e6c49e07d4475f251c21d60cf8dc2c9aed2922e4efbe8cad80db14531a63e629f4cfb01df3f61fc470123a7caa141f104fb37d2712628648eec56759bd59cff1da5e7cac043539a2040b6d364f1449ec104bec6f487dd1f784dfe377d070fe704bcfaf4ca99cabf23f4ac1f58afdcd7c8a58ede5023034cb324722e349135c0125f49a7b4fcb22d81d641c06dd0cad8d0bcadd5c4ab7c2015ad7f98b1e4db7b89dd2aa3d057b13456f4ba3b0d52d3d0c4efb35deae8d1bfa39f593be4cd8311f6e89ac462a116ab4776576c1f87b7aff41ddc728fbc601aac334148307d9a3be7c11c4b122045ef2ff913eea3af62bff2b71433f3b65f8ea939fdc19877e98a70c8cce6b94eb2bf4d696392183f8224132b004a29db1fc0702b3c84e0d345d82cd622ff9fcf567b7dd88163ca21a32122f7c2c98c8d9d0861c02548a831e5e99a064b2be13774f76669b80f90cf8c1c0749dd646dabd68d18bb6e6f84dd8ee32acc2edce1c7d02b3b145f416b54b7bbeece8087eb03f75dd317caacfe8cb2c4573f4ff9beaa2c5ddc75b427db079c7951b88acdb8a7f4dbde154cd399cf579aa683e30bb430533b3241c8703bc6576666ffd299fb4d623f277b1f8be372f705ffe594644eed3ef2bb7070e843851b11252ae8aa8414ba17c94e63685815376246361ddc46301c995a2a6857004376bdee9c92f908595977109a0074ea3a8ea89131454429deb02d6431e3514e50632000117106dba12cae3925f260d527a7bb2cd6851cc91e5d4162a2b66b88540c1110cee4672b23b32a9269eeb86063df9d076ece42a9c7938c6fd5d037a4e4ac46942c365c75b75aa08c403690de155f96ddd23a3183653a9db614f2bf069620be62842947f6aa8abc340140aea22fa3b1fa7fb552a4f0a43fc89b3790cd3721967624451f94983d90d65063d4d9c60bad78f7bb11bdb8e6af9f806bcb34d7fdb8a2da4f6c3bc27ce4a48ad529f6a5b6e055f35c344a1dd2d6a085d1fa5bbfe9fcf6bda46b320e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e7f01c4200214e2969fb6c0fab8dc4ddb8475e28a21eb6b902d542dba5e4ad0b3b89b76ea777e6d9152feab5b0dd55f4f2d3db5f4e4013e8eb6f637f661f4f5b6ea8a73437437d14190bab1fc44df4fa78558c2b53e23283f4553101daf28f7c4ec5d9d93c47ace2ccf6d0514e0d33ea597b07f88ba4c272def6ba368f0239ba59003728fc20e9cc3e3c367a04f8faefb5a9725f0c8fa298aae306089908168cd53ea4fe769175863360f8838dbb3a185612d35b97aabd00c4fcc40245349196190a01f3b74fe6ac7f1dc854be928108bb23c67742fe4845b79b6286d99771d85bd9145b20d94b38ad9b273ccbad0ddeb5b7ba1ac322a20619b4d1ca9476ce741a0cca4c90b3b783f3b3d5064a7c15f72b97836db164d77661d415b12df263b1ad2e2efe92c5ccc50ba10c2d5d2debf89132649aa3163a23648f9d53bf14f183b920d0379ea04897a6b9245aa22ed2e4d913bace308307ea05d31753a43f62968f2653a6aad5fb483e4e78bbc42b4b853d92c81bb9c4c03934dafc8daf9ad16b3252d29028a9e5ea4278e928570c1bd112d6900df3da90a8eea441fcd1a73a1d982b010e6e7f73023dc36fbff9600f788da14b1410b31fff4ab2cd895f318aa9cc7baf9aaff1066ae4be169f663b6bdca53fa462174b56c6883e1b4b84df12b1f246de72e1af85f913000407ac30410149d1e4255ce94bc26b6ae62223f238be472f0dc9c812d137d88a7fa3fc0808693f9add9db0827d40d3dfca3dac9000a12b56d8803aff5517682e9cda163128fe166033397b79afee3f74528ccc31e3c5289812ee730cf5011ae15b0c86740644806048ac2282b7ddaed2e469c289cc70689a385e2c0459affae17a3e6d3815433363be215da2c110369a6ae12325d3c40340df4d77d09106f75d51d0c481bcf8228a4152c4dc45026f4e8de1270e04b60ac57ba0bf116fa003cc65d8ecc210e757365287203582608ffffffffffbc221f01e2f8306f906e82bb3b0952ca54890e06591275b726215e27c828761bfd601ce344fd29dfbfb9b37d62c86723a0d6a75fa6d2d8bd0c6ce822109d0ce816c8add2ea6343855d5e9ad63f3a730020eb46909af6d606ee3b5811cb807b9bfe2b2033d9ac8732b2b0eb9b28bec9eb6133f6c20b4c05e1dfa77155d289bbaa3ea6042e65326d32224e00b96f4a7af04461e1dd662284d566d6a9de2e435caca9c51fee6b665d6a1f641509b176cb29ff3fc67401e766f5ca1fab7584b36396eaba2f05e5ad0cbbba29c5372d5443f9afb4e2411f1284da03f9cbd2827ca725afe395024bc9436910053f8f8c4cef37acfc78cc05d7de0c77fc1492ef46469d93c49aadd42036c5fd6ef8d0c70d7c57e0e0e9f75ac94bbfaac9373ddc202f3393bc0f37b0a7769fca64300cd3594a29536692e575c9ef4e5dd544e108cad4ff895b24b431a86f1da264ede7b4b452e600035a38c902557b1ebb2abead1783765e778e7bec298ac9e021c82fbca82acf80094093afaa499f55a164237303d2ddfd0f8726320a0260dc2d59961c33c2d89ba40e65d46075b2d3206d76b27d00575a963489d2f168a53a0b5f04227d4cf935a2e82917c018270b7714b5598c838ed82e9ddfbdaa4448858db050cd14c500ac0e5fd566610e1d4ae2534565715cc96e499e5c190be37eea9d8fd23814658038fcc761b887568097da83232ee39b47e73c9ac854b8e70f38f6999e6f889a16ade97b9b206eae237f597f8ba0d2f2bf8f3794ea782f2264e9688a717f20431734d1bb989bfb4e40d3f4e8ccf1b8e938802a27402bd3a02d3d073e555acc5c4d941d5fbb3fef890b2e59a6a745b2e966c1903a988d586b53f63bc2a9253c7afb73f204fd3aa4a82332b0b010da159f29176ba146769d340399225d61bfad40ee21f765c641552d41b46bf5113de89318a6b0ab66ce02aea59ee302d8db4b2b1b4c71693b5159fd53627844f3b24288f585f536a52a3dcc4c71227b3a1489a5d31fc37e5a708bd724a889efec04c0c57730352493c5aead0eeb412b03a7514c24f4f3e659d529796218bb825c901da92869bc2b93afd359e24c8d55006d883e2380be6aa77d55218e831a7d5c4d566e293e4110b02ccc0508902a3d876dfd31f83b7b51b7d0d82d5838f6ed96ebb7dacf438526387a0b5053b522db03e5241b8069e9d43042922d4cb2a8c1112b05ccaae37e23511cdc90c6895713f7574f615df37c425d7cfa368ec72703a464b1d347c6db13d6496ffbb868594a330c213fd222a0ac618f1b312f4d6f5dd078ceafa20691aec35fd3b8d6479a63b82bff06a6eb619ae87ade1a5542b9582b1effde9e5d1db3c619458fab459ea2f91232089b716afa2de4ac9830830ce49284de17556de89400e2555960fee44db6379085636a5e4b938b1cd067f8d5bece171d463b432225a1290c306727187e1d2d482662c783736700be93e3f9304e0113d244e8fe4e1b38be3ec342a5a5c062cf1e20f3dc60f519947156221ee244b7e14c39f495720e4b727daf2e39c2e352b0a247660550ca8f82796db4e424be367d3d1ab70eb9b37649d9634c456d889354be44a45c61c37b5def297fc36a76d9dd676c2f643d997ab97b7735f914fbdd38f86508406a8de7dac15a1cb5ac5d9f90eb1f8b2301906c52d5c21674fe09e7f3b9c5990250efb5a890c420aeb1c5fe137755416d69aecc7549168876eb00f71865d04c946f11976cae4085eebd4e73be71ed8505470b0bfb446d735fe1afa33380ceeece76a8347e8fe2f58e34a579fd57468968d712218cbc99a68c3377a6ac542888542fc36fef2cf55c0a99266766abe6ecdbd00675c0935afe8cf8d938c1df6f6f4c849d72f354da1f09b6e7fc9b497544cf6930050d6ab8980e09feb965eebd50e91efebdb234e533372a67445de2088803328f429a8d6923899b849fb7f0af2132808544cb6bf99d0b18abe3df119bb5fdf5818e836064e5a0b329339c35ec7481628a71d35a7f5021001f72bc2b98a15aaabb4c5c462b44db100efb3a3589ec4b9f5e7900dc293443d9e655c8a50e51a00980be62f3821174c918500f9f06a25e109d3f7d63d588271a2a502e89a2d084350af0089aa4ca95be15133fac659f79df3bdde5384c2c25cdc4f94cffccfc125aba0fcdbfaa5fa9a18821f17a807aae39e4307f3b6d207a45f81877a988615473bbab0a580ae41e911fc54edd3b0cfa2391ba4c88945d2fdfd63c9a9905c2f847beb833d429af910fecd4a301e09244743d21439a66ec026ccf1ef1fd5f89d8600eaa5e07328c315205c005ed4caa4514df692a59db7b218e7543499323d4f90004398e425f77dccb3e35374bdbfc1daf35a4eed54758a1cdbe35a52d597f9cf0b3414d00d2dd5cf8dcfe51b2af90ddb2c0c59b391bead7b8fa2073061b4dd04406f2e7cda52ff000bc15dc02bf2bf22838e0ef641845bc4ce6c143d56e9a3b833e5cf4186978bf7c0e13b40e5e7f1d0ef28a3b388fa16619c13bb47d5eab57fb1c4ecc0b2ebfbf26200075b0cf5137e8f13703b7fdebd72218f32b8a0050c6633b5f1a3b9c62c83e81fc1c55741e759c1274d77a029eb7af4c3fd689331b69f80343332f541d1026a66c49fc5bd7012847d4d48be2b41064293785105872300f2ef8cd8f8b9892e96309456ecebfc893404da002867264d0a3caab84867488bb269e1ecc346d1ef7c1c76b6d16882dc5ea9dbee597dfa2d3be8e79091236dd750ad10a50e2f9f6adc1722f6131cc4986b41cd4acbe479f6ece8eb0892345662a2a0064ed74cd74957e796c4fac9c7145b0103e60750bc351f9d6d72faf2858ef23b107c07caff3940696ccf74e41001f17aa29edb068eeb6ec0b0bff10fab2571356ff5a3cff63767cad097f9dd4a5919700bd7e24182acdabf903171788800f17d5005dc07d6d5bed1dec81e13bb33196852dbcf978b45932bc28e60a83900502b11f09ff61feb208682500654cc9d2b42b60ecf99724bec398dd6829bd44a30151ab5d05c39284be657e7dde539668b5dd0b8ddcb331c4fcacb7b9c9520cc01c421c9955d7b168a9416e36c6e436e6d328d9e0fadd441fbd0d1d5bdbebbe9c19a3607746d06282c0d440ac8a243c47c27f04ac2d24620ee83fa6b1319fb4a137d1c82c436676f8d75f1b9d99b01c6f9cb4a91027631cc5717d07961179bac417ef8ae8bec969828872794cdda7b6f2e7472c50497a35782ca5841d5419ef3805c9fb30b8205f8fad2226e67048a4923de049ff3ae1f804b3ed33637c81e7530e970e2f51f9bc435e2d4ce3ebc8aea207115a4ec369a4caa7a3fc814224e8fa0e9a85f1c928ba24507d2ba16e176ad8cac7c125b5edef7d8efe1423c857949a1768748377a988d68098502720e75e138abda3f73eb9b2afdd50ef2f8d20ed5f36343fddeb59a8a591582d2d4c66ec0a89dccab3bc6cdc5aaf93a7518dd1c3a92ca486b6833bed70294d6d04b02803e4841a7faf9ab06a9c05ef672ba2b61c5e004bdfd6e490da458ccfa58be7dfef9dcc0dc79179f14659d8c4456045e48ce0039982580b9d7240209d4e5767b0bb391ef1f8929bf06d5e3c8226ecbdf8844902eab10f22b9f23381ba1b5f5c241a614b0a3cde02559669e0520ba5116f58e12fca16603e129e2e5315d581acc0d2c6ff9caaa7742b6c3e28d37f0766ed08a33f39cff6919af762d4ad2168614a190700aa87a95759951364d8445e680cc9ad0b75827810546a9510926d97835d62a65a3e5a375150b6e9a96fdca74a59269b2dd788320b96d92186f3226031684c45ab69c26cb754296a2a40c0026eb95c4f0b64db9872710d731d42c7510922dc4459600886d44586099fb957f0b6158ad703d11569699664e839d8acff3a5261f8b72cc43763f02568dea203b65be9cb06161a09d17203f168cd8469d805475dccc7c76ae2ef216e068f39ec7344f05c3d2ad794cacca891b3d0bfed335c1519203d289f74b896cb6c33c15ca0791ffeaf11c9d91b95438be68769f91d08997b77905d5a89460a3619dd44d5d92b5f88542521763c815ce15bf4f34c018297d777a602e27276021619a82a21be68cb5f4b321bca1baa7befd5cdffc80121a1fa379004f441148057d6f1f66863921c3bdd3117bb5d9ad52c45ac9a45391ec83cee3c6dcdfc40ddb3ed25a87f793430dc440a6d33218a04426b5bb7031d33b2439f01ab2fc2f64a43d2a482b1428ff5417d2d33c818166c20effbf46811d50d9d05f7ef1a37d249f64689cb37d8cbca2dd7086e141caea4c00b35286c9601f5f1cb6186452405c4a975e1c13cc10cf33e5908287f5f3fe36af89f374d172403dadf01bd06055582753b0a0a387361b91f233e6beb112b9b1eaf0a835b4d14c82477ef46868d09d03601a88f4da2dc7cef2a04a62e61311cc0284e8e48c571f622bf5ee95992ef425227d6b9272b6727b10005c53778ae7a6b155f3a7f00086e1e952527cee0d40ddd5e0caefdb19a082ea43d85dec0168d5641b570fbcbbe9058e49ed4dc546fec49d1c2041b6343dee68a09beb95df54eacdfe2792307c88b20408692eef15bb3e370ac877e927cf58ace1931e2dde4ddbd228de70fc718a5bb30d68e0196bd2d00c1a3ef138adf1640403c513c07a0e8aefdca2bfd3a9404f4c9af683bf52980c43d48912ac5347c438100eb50613a082270e1bdb9a1a2241310fcf422611ff6b67cca4c9988489d344215b9b8f8b6247903edbd5689733427c555656a916d106fe6ebba66c25c1c659d27f86b712bfac6705301f03584da09b9da428092504dae0bda0b95cb7efbed8224349462ddd2a6966cffc455ddf78d3964d1b320dddd31e3e7edd4aa40afbe0d1d01ef0b1b2eab7a266201b3ed078199c33167da82795a3dca83aa06e921a4802fc34ff1e66bf31b2e047734ae2eaf765184498e127a296c5226af7a7a03ab5815efd8ee2660a4ae67a26b88d90665f5143b503998374a0175f2e2f6ac5bd25f21fc62ff01bf96ea49a15b1585441c91bde7e634ced455dec7d3a6ba115ff3b12bcfe04cbecf6af5afb4fc5b98392d2fc2deb203ad0c5f3b7ffc2fe116d7a4c40487f5d56421b031f17ef5592e9d2f052fbd8e62dc004b9caf7ced9204fc49cb2ba40ab94bffebc09851f785230ff2137e992a04aa7f63305c9d2e3b6f07f94a09b17fb79eb6cbd5f76f84ec738e80b75a229df1215931a14bc22f14a7d85a2f2f9ba9e7cb01be437e6117178c7640b766eed27820449bb3a477d3bc9f75acd433701d2a01d2dd0d7d29deaafce62cc16b83e0fa72229c045af4f16d9cf33e1e3e8a137e7da245a9906e0968fde8037a6c6a6de0ca425650c10ce8311fc1290f02ad70256bec8a70501866c727c1abd2ec9e38bee97010e2f6727888ec24d15235f58458f6436545aee826d06569824efc250d8064406cbeb735e6e2a5c0c04b2fb267952091813646018655d1bc38828d4ff512fa4c127245ee0436ad4f39272cf0024eacfeefc8adabb6947d7ca619a5fdf16e45cce1bab1b93881eba5827332e4f6449871e1281e94b61e8e4a0827314b502af91e4dc372034dc4a76beabd158014fe0909c4218308ec23258a09de7c2761bb90c07736faa900e44c1d46252fdeaa69dff7de1b235950537043d28b8f55faac068f7cc307716524d691d11d2f9243ed14b10686e7ad6507d91ba599f76e4b82b8b7b3aeb91355cae7595b913b280b6e2063544a350d855e06732d72bbeae44c4aeb3c2617b137ae71ec54f0073b0aea31ddc7f9cc230542410be56f25ff12bfad25a02753a051bdbb793fc1388ef1a294b525afb5d1f00909d9b267ae4ccc7e62cb6418edb56a9752619ada13cf24c7c383c68f33daf425bca04b9b991cafaa21932e248ede5f0ba666ddc92dfc894627ef08847726002572e51a952e48c875a9f3daf5529b80734622d2ff33c1361dbcf2db76b5a76408eaf5060428763de6150cb883997488cd2936b1f92ff9bbe5eea42c55f090e11affba3154c01261267b65ab13ceebd58ed5676ccd1340e922109ec3a5b7dc115511486c308db7de52b192f642f2e9f5344b66e57d0f380b2bd318c435a9c095db5133ed53c9dde09879743b172c3f3464a0526cc3083d04c8da3b467eb9197d696e3888e3cd59bdd01261b529eb7da5ac57143c130195f5b5d7c0cf75c2bdbfc9e98b56b449f8e9b4eec249a140a3bf22498bcba318c35a7394acd36f79d67942147df440b6c249ad6f4a650ad12479fc7dbd474b34415fe6ed5b1ae62116f2d0738e84cb74814365c2eaae75d75c1e97ed02e65012e3f78505e42017786cbbaa290db6104da5df3ce5eb84dbe2f4cf2a0a36e5391814e4d05b2fb4a876636367e9c44fc975e77e4f56e62edc1cecfe6bc75550c00f2a0d3629f3b2314cb8dbda80b03437fd88eff616a3a4a03000ba3936b2d33229b09823de81235d67577d92e61a9bc8f31f5656db69ff54c97c1e410d2281c40e8ea799a3a8fe19383398856c12fa95693d545567d3df118494c401c9d77236045bd62a651af5c4842f0b119996312a2b555cb4799bec2890ba4a4624ce3eaf38e988510a7f5bf9573611d0b9b88b8606cfd1b7e57355a28110f9f0a8b46a2f235f9470ba45f0063556084640f79d837c8071b34408ff7bb78d08ffcac535fa3de0685b5daa94a89ced1a1c6d32ab63bbbb1b50c1d6033e9e4c377d5eccd0533b53587f4728291d7bba94c9dc15c64df469f29bdfd5cc621fee43bb33b5969111de0aa7cd65dcf0c927edf37ca242bc64f810c9c7c6ea9652f876d05219ea660688d07d7e0e85625e47afd3652b4bf13d5e28dc94076b12f7640edfe303bd5a1dd07ddfcd29675632f62843b8268a3291f38c53fb115f5325f8e71b5f728319296d7181d8dd5df7524b1d0908edc699c48fa5868f5e7f6015f649b6d82998d43e061cf94fbfc3562c72dccfd085fd0842be56d141c081d4431d7efe66c3c9581e68f74c739bec99b003890c5f9e37497353d6937da0a355128c4ba0d657177518157e811454c0000a340b6ea94d644fc967a3b1934806e4ffb2b6c44fd68f0d0771854cd92db6a4cacb4a51cba72e98077b8de6867885151c9d5a6875764e230f2ca135d546f10057a2f243c0227c92ce23cb1c219cc8225ced38a987ec5c80148bb8fcd36c5fe12d1b097cca1c669739ed04641d14a5614852b71e600f39f100b48bc384befa6e9184eda4515f30e48f2d8e7da182994a2d8039a5f167743e3a83ae225772c31dc15ed64829b285dce99d29d8ffc28b77bae6a5db570b676327eaca9a081330b3d950b25cf9b0199dbd9d734407a7b6788eb1c7b190b326161f86133f7061573dab25df10bdb9ad0327bcd8e6376d60b011265bb48603bf2730e8737fd3675bb1fbacf0971ac3818eaef2e48d264d80b74033992b6dccaa3309b771cacbabc31f1b5df9c5ca982c8cdc813956b9c7afa7651e781f31161bb61e68141b0bc9bb46de37e468170cdeaea20c730b81eaef4a7d51506a6e8d40e6355aeb1e3b51d43cd39ff4eeba80cdb70a6a0d85dbadcd64a80898df76cedb8506902f2e63206225147df250d4226fd9cf1c6f5cea87167a81630d7234e4048216a7758da572a26702d5083b1e1906281bf92eedfc2f988ec2828dd7079430bd2f0111edcac208beea9c4bb01ad70123a7d335d191fabfd6c637e9899fbf50262addadf5a3cf4a138954738b456bb9c8605625b2aae94128991e91c0e62828ad204e522ba6a55ab1c3064d56514da5c46943a1d8421fac70d584daea333df201203be96e5b6effe1725e541deedbedfb1c99d73d857321b5c79bc7e5078d34763f5ecdf30bf8ce3509263ff8516cc11b0d4e7892442f6756e23f05a98759de5209589c86c716d28ec30376b1aa2603a35615d9f712aa112c19b4364cdd55947c12dd8704ed1e6d804a6f3947fedb1c22e6b5d9ec4182ce82248484f1c077d8fb18ea2633caeff87daa396089a27078c0670fcdaba1d61091e2565950799be18b329f57e4cf4c80010c48ce4d970bffb525ba672ef04cf1ccd1feaf4d91a7f4b60011945364a4888d78e0538abfc2c85c4079e0895718a5802098fbca9570219d11d695164cc61e58502be4f8f3a3a2f4b5765d870ff98bb237b86180fef9029d0093b7fe806893d1a25440698b239a1ff3db8f7c8a687739f3d180d81fbe75bb323585016d69348c8d20c9704547fedfbcb90f82bfd3337e280f81fa7cefc7fc2b71645de84bb5e75a52227b773ed01aa743182c1716e3b8fb62984300696c2c29a14cdeb8033ff2952105b54d50e4a2bfb590f59a794dc6e3cd293106f806e838451e7a45d953a214dc1d630563b895880aabfce34ce9261c87a6be925eb37316382a0f8c5440254be1aad69e7685730d42bbcd0576627c38848bdc92b16ef61366fb6632202f483f25032043f06f85009b4e13015865a387bfdd7c6aa4436b32ab04041848b968bb97e0d19a0c0e0c0899c1f463547e9250ba8d13109dc52e216b614f1193a6f44d58959e8b4acd4511d9d1c4adedb9aa9c3cdaaa5e65c6641b8194bca4a574ea98a907b79f0e10b1d729c9a1e42a87b8df7abf1d73cb835a3df2469b47696d9512576d9aef15ae943e48cb86c535bee7370589f9b288f1062f9c16fd7d5e9033b8a1efe20773a313c68ee4219f9d449fbd9e67f55d276f6a21c305edddfeefae11444635ef64f2a9e69e6de505fa9dc82822033265a1d86a1a301695d0ae9310d9e29b253f31af878631b6e750e150004b1aea96be32d6a4380e2952c0b67ccdbc2d363cac6cb66bde674cc0e413a195459503b78c9ab8a606da64a170c63479a96dd3051b1e97153be047747cb20fccaeac1512d28b8126136b95a4379f59382bef28ff51f6b04ff085ee6762b7772b81dcdb4efcb95b52175d52325c8e954c6864096e93b87ea0cbf575ac028eccc698a5fc6f4a07ceea0ade09f88abd662057f7fb3a520d2c473cccb2a7047fc28f797936c16d993f2011e9524fca9d46832acdcd6ef1a8027bbe285d916bd31617d59ed53c7e1289661106b25a937c68e755d23cfa9491056c2c3e12c503c4d2c72ed942e55f2359243c85569cc240b7d9a81eb5e7836431cb4b5c5e26fd627e24a9e7d1487c4e7f64232ca1f23048158092016ef5e7647e0ae4d313a6422be0e998446fbf79cb7d70b33962e0f6c2ceea814a8ce8d8ea72186fec4f5dcc63ef82a577f5a8e84579623f6993b5b6c76e4a5390b5557d7b8a085229827c08f2ee968dfa49997e59ed068d4118b0339605305b8a2b1ba3a54cd9eb915acf75eeb0748da73e2b20c81de83de1b74181574e8648315a12a39276361845bbeaad5604229d7f4e3a08af4a9eaf93fbb8db90b0015992587e6520cc603516374320ab70b61c9ac5c1e8e81c97b1fe15916f7791b7f657c27f5fa5c0db41245a9615a4b50d3802fbdad494b6eb22abc3bcf030622c7e4067dc6941b961ec685fb554acde232580c42608d4eb9c0dd0659567348696cb99e33efc5063455cc0820f886e4b3b2cce680c0f77dd77098eb87d7f35474f1e5f2dba3329dccba450c6f97f5d2298056afd4218aca1ee835621896838a791e2e04d17f0b4f7f3a3f30a0fb717e054bf11f775e20d7a5aac4841c31cb8d3570855504be775c4582963bb2538189e204a8bfdfa59955a2bac2705dd78a6f8c9f3259326febcf70a337166ee934856fc8234a2d61b044578b940179b590b40363159538658a9b39ce9d39d51aface156c9149f47912f50dfb5b8e241c2e60f78bfa45e2996a981bf51afe1989bb2ad6a8cd6ecab3424893eb760aa4ac1ad724b15d7859915bcd9d625624570e454b5e1eb69b441c729d42a2755bfc3fc8d7e7a5c38fc07b576ae9904eae0104652be03e99b8e98183b175596708f40aced3e814c940b590a26d34206808438a41d169a6a33b6b47f3d3c4a2c8ae643d8cf184833cd6c9a0de44070557be274e0cbe89991ee768df8a98bcf180159187fafc44d9a91e3fabbd81e2d66ea303b963059c215562aba48b8e76c215b06757bbe70d2452fc57d003f4a5081fc4c4849bccf0ac8cf9504f34fb2c383eebad5a6d105c16d83a902b1adbd347dff749bec214d209cfddc171b6ee5ea3b201c07c622d2b7e4ecaf989ff8ecf7b4682b83b0c84d0766547e77ea1d63cb8e45e86b6df0e912b4ac70c3e15784484d714f8873e91ef724ec0b5396e2700d35607cd73847cb966bd95a18ce5b3d32456a76cb1a96172034f8391f2b63c305132e745cbe5426c5c31a30e77108459507674ac0aa22c2e6df15f11285529ddf11e1865d4b3616fe0f04aa186eb8d9cfcce8440fac9fee9cf0fc1872f4a0ad28236f3e66d8123d80d08590d5ad6d31dd69ea10b762b3698ece119d190d53f004b65c947d46b3967db026d76b9460bd78f67077eb78e8828f2d6a2fe2452ccbe006f201977461bf38e3b136e04293b65b07bb1f08070bbd8cf284b76ee4b21faa01c880ee6376b9746f03c9ad034fefce1fd5df27ad9073299966771be915e68636"
+ let t = readZebraTransaction h
+ it "Sapling component is read" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let sb = zt_sBundle t'
+ fromRawSBundle sb `shouldNotBe` Nothing
+ describe "Raw transaction with Orchard" $ do
+ let h =
+ hexString
+ "050000800a27a726b4d0d6c200000000ff8e210000000001146cc65bd6d252d09b8eb0a8ab0aab6d7a798325aefc1d3032fc6d31373a85a25a3a16b447a698f720ade1bc290a74d85574b5b20515391035a67f8d5883dc65ea3ba4a17b009d6f325d41072b3ce240270959a7ffd040e5f16c697d8148973c62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d23301c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668f0d8ffffffffffff9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900c734b62e56792f6c8bed48e4f108a817e83d64d6a59e38cfdb55c0f8a89bc7507c89326266f7ac03a3941f448cb879bd792bb116d0be8876c0856a76ddec0f0c02e16f0338626013ee5f6037fc6a3c69fa291204039d04d17c11295ee3024aea8f5d381e9b7eb3f938b6f9182bf4f889f1e53e30f998b1cdd23f45cfaa20aaef058248cc2e1c487fcdf54a4bc22a68a17cb6fa7b2fbf333b99feb84643d321398b675634929602126b2fb40171e514769bf82f18c267ce9cda0c24300caa9a5a361144d3b7b9ab2243ee9811d9b2e72c8bb1d145cdfcf6b29994a969b41c47208f5dba8d6d871e490e9b970afec4d8bca40ba51825cdc78cc7cde6b6f235a4105b1d1b5e2765efd753095ce770f070b02cce3316721b9345680c146c2f428c0bbca90d5a8cd0a1c4c31cbfa8ec165ea9f9c71d2d05e3cf8bae5e779786f179c45a3cd8087d820cae812aded04f8acda9068af80ea834f79f1bd03bfd66f8a19074649a85ce877df1a621a867debb423ec0d19015b326fcf6f143aba34029c1da2fc7b099378a366c38c9609ef6a9d9e175e21b0c1ab94a84e28ee7f1a00e39cb6fb59f44e4567e9f85f8f98158263c52ec433c042397c784edb07c28d2bca036f59090e819157375d610acb1993a4107b48da13a371f5383429baee209b2c0cc150fcef79a042749668ba1f89ad24a8c746142191ed0e8fd63624a331d98d50daa84ccf9043076947cf5115b9f8787acd36000c5e72c8d783b29bb28a3e46036d0a592ce8a158ee5a7ac210be72d3a6185c13645d96a8446021b64043ab8b589a20091c152e7d5a993ba94770eea988e289e1536d0d81dbc7046ca9c6d918446bf970894f073c920006681ccf6d1a3f138519c68eba0296069e42dc60f2bcd0f17c400efe4f4e87de8606606dc4fdf31494df4d454d14a440b1d9db4265c7aa9bc8683c68cb149f2cc826427575e2af82e842199a9cb9fdc7243b3bc12f1a71c37eac5cf88ba830cb95728897fa4c177a290d6b2b3814173262da14db9b4ef39fc54f888a6ffef4221ae672fb03bc78ebef479360a682ddb12ea0369a428a6c2960ff8327e9a2f5e5d98ce1eae748db8f6a4631c789b4d751d6b99c97c149a813998d44a7b57ba06c8bcb8a6c73c6388cdcfeb1346cec8fee7bdebf2a2388d9722183eb2d2e0e183cdd092152ef640880f4514f3c5e836cc3a8249413500630aa8da85f9e3cd92bdadbb69a2bab8d71f0b3ec5832a7ddbddd67b34c33b2e12a0c8468e852e4a8f7df45657e9632088aa7c6c5048a2686019cfec33b27fc88e23759938dd55a5dff589c1c21a37da617609e9d8be37dbf9bd6e84ee160fe10268171d969e4611afe9d3482ed4b132dcdd11ee516f36d512a333da20266fd984caebf4937fdfd18ed07b4a45771cf5c8c16c6b258b289a07d136a22acc766011f366c420bafb8fc1a10e42219bede5a3d1166c525491ab60bbd1f973fd3fb2e94cea888e24d5fb0adce51faeda75d62de70094d4b36d38d03cd824d284fad577c3ead4d98bcc8ceccd18174a889b22380bfcc12656e764ea0b8fe1409971283008ed02cbef89d6f544c62c3b001bfe96723fda9190deecba534d69cfa358036fdaf16127b89f925c52d4e750919ffb7182b6a8ad13d0a8e00e0b906978dd24ee11869c1a63837a80e46e1216e2e273aba07aa5b0d97558db0ba7f9ac4c89403c65f1719394e479311f5cf84746e6be6f1abcac03194aa8bf1735811198b5df90dd6cac345779c185c24beda0101b932048dc4144af664a63acc0c395052882ee1f18bd0ddf13bb583861923bc00ed5ae815b964698ca097eda1c4281e039139fa3091890244f926cc4ab773ca8a35d5263d3bb48fd6ac53a4bb4d7d60b36446dbc714c35b5e13a17c5b0c70f67207839d1f7404604aff63b2fa83a4da7dac92aac96b3f250412f8d04a9e298004313b02edefd076c67d8a1316355777814e2e1ab03690e426b672d32ff65c03c592ecce6a70e34fea2e15b9a6b4fd092d027199caf27e84e25c09380b38a5eb8985355b3259aa1d94be74269b84f953053b02ba3be9df872ae5fb2d893188575bdfe222ba267b5461a0d0be274a7d9e6ee51490d98e4cd97978804c4f0f8e9f4908fd8c102b01080f5a02b7578591e95d60f3f56d8e48514b1ce7ea6894f55a32c8ac8564985d18c6b82f8dcde5b315624e9321bdd49dd350c87907cc373c0238a79321e6250e38a0ceb2c060ecee6708c11cb30a49687da9923bcdf011f9aca27e6eb5a8477a2bae2dcff9884cc2349b51a66b5179ed2d8f69e4bbba74c694194e83d04a8566228227eb732a95180c6788483d1f259d52c52fe43357656d50a1cf2902c3124d60d15fc85f0447a1203f824c1106452cfec1c92b18de003f82a0000000000001cbd27436a221a53d08c4838831d1bc60ff7e93df41a51412ef6096eec98bb28fd601c53a5371b23a497062635b5cdde715c23840d37f1cf328f0a2ba96260357689ae3f84a80dbdca1520df68513be1285177d3c0da664c64944de78d8b8d5864f5ac15444cd3204adc4fe487503066c18fbbef8d0515248b0a97577f5aea1d255788ed4bb66d4d56303efe135063392c312b4671963daa20e0ade262984e11263a1588eba3cf829e6131ab506e6a850aacce603e8ecfd6e794c90a772603d80fd2aad6027b34854072a0d23079252adb1ba637bbc650ed4afd35d977e1498d998020bc1c814718b48ba7378a92c56827d3c2f20daa231fa51f0a9188520e2a11149e162489f0d6dbd27cf94fd5775311d3dfbcfeb431bafc3515bbb8c4ba4488c320dca0dfec548fe9f46d8810b3f6b16bb3e3eb0ea130747d3d127c5953ca8d561f8d425a35dc3f2cd831743139fbdcada42308b524313782e23b32d5d54a265eae408623e3b2779fe60e13cf47d54dfe520f9f4e57c68aed31f78629a9074d72ab87bea993a38f95ab40df3ef01735e7d44ad365a786e0d3032f1c1dc4e6839c974185dbe63f8725e79831ebe269f94c96705639ab38d5d0700da04c6a9f686e1ea13391885287ba43cf3ccef1c2227918f15ed55441c45adca84153530bbfea3cf37adbf84831a2bfcbf0ca4a4bbd90e623789fe993dc17503ec11b1ef3049f27b27ff778af364d634a46165cda1dd8241cb88740bce74a73e7e3d656df2dee05bb561a85e64671b191ec802c5bfaca49b8168e44271cf13df756395896ff41a99654f55b6951f20d04b2007938a420218db8e37445ef3267130e288e3270b13a92596a26043e1ae84f3934cdb13363bc2843f74a0f6608a36b52c985132aa427c56b7275a864b3c76502c37b8abb8d0286b3199c78492ba8103f5a23c6cdca2292c75d7d6d7080108850807f78af3dc7e418371c6b8951bd89b79fa586af4e16096b08ac1f4dc2b1e4feaa5c040bb002b57311523197b6e2bef5b79ac9c9b4a339be6f6bf7fbe9b5c93862c87be6647949c70bb2c7e268e2ab39cbadd69de628376b3af744eeabc85b599bbdd09defacefa443e05c9b5f259a7783743fecf1a749c57cacc85703269ed67db1d8d475f6fe25d66f84a77379411ba123d98fcb3ae4eec306489a08372893616a91268ea6bf34ddbf0fdef1360ab9e82f4ac80a24e41f439af06fadc223c61f445b7261eda5e1320e269d1277631ee2245cf930244bf8c04050c514e2d59035b80827586cbfeb7da7a59c1208aa86390b9dc7a9b6ef38879ba4deea5eef47c5c98d9167594cd730abdfaf082090efe759d1b13199d739c112ae324ba24b275bf1d89867b81f4580a7ea3a8d3d07b45e2de6c1c7099de3606873b13f3083ecd1e84456c9a1b1d358075c68b1a7cf0b1f26031a2909e226f5da7877d0085b879165ec4b1d9abb7b0732ab4a6f22d9a7bbd0d494ef3f9af4903dc733fe92c6b2f557d1406d223a93e8ad6e579ebcde9c39a5652ad31335df924e5b6a09a0191821b4a0c8f886e2d7860b75ae79ad9dfbebf3500c8b9762dcd131eb5c8b866b5efb4fbfdcc5e31605c2b7d2ff8db5198a6c41bcf880065ff232ff8f84ca3f8022d3428359dc9fb19f57a6ad3f3d174d8a348879a754b37095f01d9a7f6f873798b97dfc5d7c7eaf0383b3fdccdcc11b30dbb3a0fe3186a36c4ddc9674624e38a81cca60a9bbc1b124021b61a383b7547d6af187022c133ba9d6dadf711a3af3b0255b859b214ef6c5dec592248fc94339a64f19196ca0fdad80f7f8e3d78b1f783b1f038008d0d106bd86e23e33ae5728872d42a555bb36d0e3303f0b4ab41180f4251590ee3ee244b77191c31b9f3f990f71c6e237b9dbcf7ca21c9b4c2446b856c67861785bb9edb920b8f530a7a088313ef044419a879f26db137e1557d079315844f9f60bae03d8cffa7a28bd2857a001fd5d2d999fca95ff91df0e228567f6c9ff592b77b7ccdc93a951f7e34910361a8f4fb517e1c9fb956a3bb50ddf37ed37e8d26adfc0f71e059ba95ec77a1e34e1b3420c893f89b79fc72e3a1d864fd35526727f939badc29740f5ef9c0bb9a3f72e1e08b2ef2ab366f80d8e14e03e92162736e2ccb7cde82b2af08de8a6a81c03c63e2396974ec29fb122d818a2d2d5b29d11b704d3ac3b39431099b7b2f6aae04d28a2182b55380503127e4986ee9e8d5c0c2058e09e4592d08d013a4f088e45403720160622236bd56fbd9cc240efceb1b23c19ceafec49e9d5776ca9da5f7810ac979cf6ec5f678c09257abf79c9ab55dea00054e11b62c0a0ed4363d0a96a37ae1a323aa93bb1af253885afb684ed30caf5cf3b37afe6a6463a16f34cc28b4530c6bc6281f597bea476cd9a773205b96d47ed4b0bfeeee39b7ea44ff194911bcddbb161c2c0ce9488978b99e880d8e43624dbb4a567483ed293348d752634b2f46219575175e3c249b8e4e853142b66491aa1c142e7bb558955747cf2bd61ac802a2a4784d9fe080f771dd537d0ff928b3b04029d9ac03175c2d535ad7f3c123eff30c0437b32dd9fa31b2976e369b89b79a2a95e31aee15a462c5fe25fd937ce6b0795808d16163f2cea8f19b7c83913cb4a793576aa5c0ddfb6415326d8f2be0621017616c85ee46aa768b077dbec72311cb8a0f78ba0621a277a79af6607063839a52c6825a20e1c5818b24628862aff5fbbfe87311866b9956eeea7412ef69a3e4da84699b8d8b45c74ac96c3356989fd4962ba79fc26a92488304fd9f42486266b433eeb57368d26ae91ba4e7ed812581f790314cc7f44639aa7f6775618111369e8e2d68a6ab24388824bbbe3c3b0fbfb88635c1fc3216af1af40eba3555c0b0390a18ccba9e68afbcd21ecb212aaf82846f0a55793945902c48cac5d19332e23248a464529f4cca177137c508b6b13637e7df523254f24b8343d19164174202bcb00d5fe618b760c374f69b5065b1f9acf91af95abd7eb271586cae14fc835f633aacb4cd2ecd0f0ac08b688ef4d13b8a7b4c487ad46485bde0e340309672dc38af275e6ab525971409a39eff0ad134b1674db5d1f9725874e36d8730dc034b0a596c6e0a26e521c199d3e3f86815a64d148ffc394290b19f15390934b5d0da27dc8365360511628b93ebc375d2a531e4f4cfa031eeb501afe96d201c7b6078bbceb8e5d8615599f4c613bbb81a88f4eeaa57a9c008125073a30154044c422eaf2a32cecb15aab0774bb44e52b1792d154b8036ff9224af53e023fc011ab251d47d7d76e55c5015db1926d43c56d055feca1259de53ce98a2faecb5843ce17a3e83ccf678f5d13a8321f278a670c684e62b720e1eefd0abb2e9d0a3ddea81d5eb6e380a1c22af5587daac852e93a86f5e4293c18bb26b32035c7e5ca20cd2e3eabd3de0e55092a79a42c7ccc0aef033d6683043c2a29531a2ef1f503595c0e464ac042153c4685b062275f88bab93cc017f1ef9dc6f8aeab7b0b234f1c543420324512554f1786c82b37836238a4dfdf86f97d09ae466eb39d5f3ab159e2060be309d1284be133ab40abd61468c1706f7f9e57a7bc747479693a03edc8863dd196fd7cb2721260e42f4f606389c4972c74d357e7467b61cb7f455562015d29a59c7cafe0df03f26b77bca81c2bbac8cbccf8a65190b0c4e5ca832e82ce4e11044433aa397106cafc05634ad778270d20d8a13068586bc6b582ea24524fd921a5ee22dbae5296ee86d80f12b78bba26f8b42c1401b75d5cfcc4775c5cd1cb0a9248dabc8f82d216787b2a2780f7d13a5ee8c6ab56399a8dd5db3a152677d01eb8ee98d1927ca5069e0d1ba3907971a2199ba3634b48e570dd97d93729a6c43e4f359e2d89795218d52270a338a1f511b1f008cd04553c1a89caf987fd18c329be7ac2282084ef1789615d7eb7afd2261f606d3953b8863abe57796289e7761b01c3ca0faf2291287f9ad7027d7f0876b5f77d2a7f87dfa6ad4db905d4bcad042f403824aee3b4f8d7b5ab027fe1eda9d683db24f56f694b0b10ec72ba0df40bdd6e52b4a7b8d064ad46c7490c4705c14b06ef55222435d2d6316c7dfee83d225eaa431c11a4b85b0bafbe66fda1abadaec8eabcc2f8c688a7b9cb2942597f20cfafd7520892c535bbf6359a6989a84dd89989d95b8e5222c3aca2e8b0f8881d759e450466b75b5f36b7b723b0a212edf52abd591e7e545a3974b8b31b84b523af7b47e3804b5a268c86ae0bd7c80bc6b578b79f749eccdb4a00813925ef40259ac10bbd0fc4f2fc536c30f7c1efe68a52bee22f57021d23f445211b36fee6302202c9b62c6cf9064a2df424563f9f805e51c4092482253e4c258d53b80a2d26eec9fcdc104f7124d876a3ad573a7f419b0d67a41a34dac9d8f28cf9519b9c2677c9d1e720667d5fce26091d64c6df8b46c98b58017de0d055651e8caa3a230f57aef214ae2f27fa85400e34ce7087538fb6b854a6ad534780052210b8b8c90b4de4c2afbad9f58a71770ec186cfa44b61b53876bf904972078673845ec3181caaccf11f71a8e2a502deaf144f16df1df3bd81277dfbf6e1ae5a17363725ec31759b743066fbd4cafb5eda6e09418bc375f42e0dcbd4624dbe26c5fabb77152124f400e677004fcf3c862a9b5576140cbeb800fcde4409caae06286cb643842687bc6b89738374c7c759c911d7bbec8613f1fdd996d4b970fb6f2a9290a84c34d5fea6b8006357c8e6e9d4048d5a8ec476dab0b55e8358ecf7d27c31da86681f3fa74d072b1150223eaa21c4027378a99c8a2dbdc07d4062c401e92eddfff82841292bda2798c4e2ee9e09f618cc181c91a4dbefa44c410dff5cb705ee005e3a0470c13baddf9066109797a3b51e73a0ef229796d330aee0c0160529a4ca3b39e861ee5c4a0f78619007ecdf32266c7c42f0c972b91cccc45f6f8688b2692f298721cb5da39cbca9a5adef6969a2592ba421680241f8a5384bb92e70acf79c2f41d1171dfc6e1939887b9c8ea94429bcbf3532919fdcfcd0f443d3c95515b41e3f9c84bdcd3de1fd481f98482f667f2d017e3579208341e9a225f85516c8ca133cbada77598b6f596e6151eca377fcc8029cc99a879b26d975684173c0874509117ece4136bd2d69848f858c05e8ba3421499d7fb5e3e7645fc135117d8fdd1dc46bebaedfbad4dc7cc23fad6e696fe349712cf7579b4e63b38cc7d02a4c6a33ee4117d7ccdb86ea02cd791756b2a3c516d59d39ed83a8c328823f1934731820c187624219b487ca86edcc2f61a064e4e8d17f58f4a71462f3f0cfef6be95c0eb3737616de5954096d761a51534b36d798c651541acfe2e5bed6e58c45c46e014923a342409d49e782054f2a4877332e0adb0663dce0e84df0ff0d71c4c5ef18d9cacc8b8d47c78d53fc7649bab719334601c79a345d2101e65d4c3f1616741b24fdd9f7d6569fb8de67799648b323856c2ca96ac91823aa12249934c7050505258c43763ac9b174d55fee71c7817d65056c30d7853500656040621eff9f291e9db198772451cd3d58bc9421f6191a863777edf49125543b22ba127ea3063e1989bc4635d50bb22270948ba594b2066d91e1589aa0c1f476af8b170820ae0d2409df83819579621161ba55cc8d020e1ea68cedbccc9a737746d824b06e12cd2e3730260fdc52a68b6e142bdb997d8f93f25d241360285e7372939828fd54c015ba90bb4d553558ffa2558a43098644357860785addc455bf1ef4ffa5aebb8f002eb0f6a8ddd6f45edcc7cfe88d7ae7141b9113a4ab851b5fa8ff39c7024d2b3202841266f256b1f4ce4e4ea8b83d0067555ddd56fb1f8ed6cb5ce7340cdc403fac1dd3b48c373629202c70f1f95c0001b4f94a9b4bad9e1c64429b541ff57a2f2ea0c1fb2af8f0e490fac415cc3bcdd5739f2069e2bfb873206b8c8811619f1db39386cd2dd748c6301239b4e873f3d4618a43c52ef7d8abc0ca127881dc8809dc20a8730718c64c5a132cf638ec1e3b3cedaae55763637b4c6341baeff17e14c1e3c34e5d23befdddec0c7e25f0ebfa90a0bca07a0f5446507b7fb9cf91cb3cb8d24a111f46e7dbe3ea62b384eebecaf8de49b15b17f9cf151c4c3d33220b3adcf9f87222813ff2125120e77c638ab19c1daf9631d53b3efee67c3c40c9c23495848d4c5ecafc0998bf2704bb78ade666f414e9c6bb100b90451d93397d062b741a3ef20e7e6ebebab8238e2adf415775179c866eb4a2f628924674b20f711d2515d3b024e7e0fd3af6e116cfb0030709fb9bed4a441646c03dfa0b11cb460415ccb3c0999b738a0b07d55be741f3d2646a3d9e22bbfeb46b87acb6fcc2d83cdf44b656d2269a0734259a738d83a7429bfc99da7238b9497fbb10e0f623dc1f793e3be2326a7e55677e20ebb152f71794fd70d7698d0cff1334341b3a8eee38d66a021cae6f8dfe67913fdeb8d46efd9b77f28950aee7ce112f8f0dfc04132a99d95dfa923d391e06a578056ec8d15ddfb0fe8cd2790ed55f8254c6351d471c7273c2e61c2694bf6b0a1173cdc2d422246ca5cc51a9b8669dc331fe33bfe330d5167a244cf041f8444775562eaa8ddb92e0623d4c689db0919ece87a7feeb1ddd2d034a0d884d6bc8b5fa5fc7df00cf667dfc82c9e98e9b57fbe07fed391729034c467b46d3d16ed72c8ded069a117ca2d28e71ef4c37f1b1939590af19433ff2a784ff38134aacc59719673c53ed047d79e0c36f3acc03f64458826107853ff8d02de9362ffada79feabc597d019de6afb6577dbbefcaeaee801a49b1718151690b43a4d70b2fcb73ece3e3ea5fe2a9616cc6a9a494b8c6c7121497688652eb854e7bae19af3e5ae1a8df75138ce18621d11f15054ac8971f6ac084bd90e3909b46db321916c9e3c8950f3cc8e0a10dd255bd6b359c73dfde7de6e01538d03609253968177af14d327337bc1bc4e866c75ab5b69125f9e9a758f061418110a615b97f733f899382e2a14fa0ff0d1bda3b7449e1f54362697aef064fc9dd50692ad2b9ec286a1b7c1f845b09f4b1ccd46142f9e272d018eabf76c23c26b1831762a8a2c9152ef0cc843ebba20bf979e851e76141f41e817cca0ac60c1baf239a6c920a244b0be031a3b65d76eebae15bd9d49c53dd3e4303ca379f26840901f1da886d330d8ab4408a25eaa280ddb545b968b4aa59ca6f9cecbf82984264a3232b97498be0729f3d97dbd64823393b42794ec1676244212af382c37d53d66019a5db34c3d8ac156cac852ecb1c57f0f6057815460eb9f666c368acca6dfee0d2b7bdacd2148d74cea3bbac8b528f00809f403b1964b9d199d2db93363a53732efd97c66f28cb51442dbfb1c912dce078944e02889f9395ff2655e2d7118a11848e3ca64efb5eca119240b537b5d4548a744d96962e9b2a99af73bc4d753df16fe75a08005da7773b7bef68a44f56951a3a93995c5d0516a8b2c39abd2057b3d86ba7fe511dc33292596fcc4d3c67df88812e26d3628527752a81c8ce6eba711ae7ea8748229caa2580e18937f45cbfb0041f0a9254eade41c95ef4f390db6174e6b0ba89af344c215393b627348ebf00901e343c1599c832bd2cfb751750808719f18119ab3fee6baf5fd4b8bf7c92a271ff24576984610abaf1b2297c18a2784f57d7aad26f52c0d60fa6cd2484492a1524dd14924eebf53348ee2dca0643ef47381541b91d1843817067a94bb7c79e4e08c4d81c7266afefeda4a0db21b0db629cedf13b4f6ba11e6480116a7c9780009ee0e1e2bd543dbb68bea356a6010a1e3ded4229c4d8035420833fc83337dce4bc483eec76f479f33be2d6a8fb013813952eb66bc247f37f9def7e9bea0f277f380079aeb48c192b9c2c24f909435586bcb994ae6f75eea111c8842b855f90df21e956f839aef89bb1b96899e343cc66814ac08eed4defe5e2a62c54cf83a27f0499224e02bac652aebe6529143372c483edcdbfbdd5a02106fd3651875f23724d2154c0138ace745d038d6cf6b233ae2bbb8313c8ab59dc896844da5a6610845d9147e57518ada61b7debcd66111d609587632cccb936751d099639735f09f77b2b9aa1177e95ac0f8070bcbeb01bbfb3e7a638811d919016ed628865e2405aa17f5129ea43a836a76ec7cbd943e20b1e388d47db2df3394785e27dd7efbe350ccec6057af7483303a6a588d4005c05ff7f3c90dc5b9a0f95c3c541ea40615f6b0ed6cd3787e608e7612b52199062991a63511b3055dfab25b18ab55948d9d529aff91a0d8aa7705afc187e32d1107c07623a309390f32c62e1abb830bb090a7dc4c6817b554720fce354563662d78cc2bcd0483149b304ab86502f0ad36ba2c262fa60b1b84be2499e56fd72129c0fdf1961214df8dd6c9b831109fadc7464df090aac96d221ab387b6eee3080ea0d94e195ca795b5d80c42946c0c353e39075bb1aa4f3fac36a77445cd57b11b3c7ef0e89aca911be35fbb72e1e2fff84b7ece66bb8b3dea52b0aff473475d298f730e79ff77e955856817514908baaec1b53f39f9d5dcbb1cef29536a483a1845d8538e5ec43f634914bae4f2319579adcf3f0fbb151c80b54f02be54be612f9417f916579863dc16da5af07fab05db2094c77612b445b7b9784198abbfc8387486449969118d3a83142022017d69b939d30fd6b1c5ba360c0e37f264b5d604c7ebdaf327874b22d4092dcf35aad93f0f0b3037f90000d4a1892dab827d3c2f1ddede948d330706ef17bf442f8228c168401541ea89a837f81cfffde8480e1b6014f2f5d3132ee740e04622600ad93dba41e39267f17554b29102390f8196094de19e8d966d3c03a9e3348494b54045629224612734a1304ed76d17a465c305675bf99b7aff1950dd2012e6cbe8d15a9e35a7c16081bf647e2766c7b8e9d12ef8a2c94fd7531f6d1a7ac7ce6bac028917a2a71c8eded4f3ce3be8c7d98422094e88414338fc25839a582b5965c71179ce3ddf7a645bb163f3265f4bc4ebe91ff25d86520696d237741ba73084070e23494d6a890e1ae748091a6936a3137d16dd14571f86ee118bcb17c8ecd2ed20c25cbab79a78c53fba1d5db4fdb15e6a124bdb2d41fef080e7318f78c67451beff04b58a182f592fec3feab6011be4663dca8b62ad2a8e40c716b3c8a170e1574517c44e146109d223529b0de480aa168b5a4b0522cea03dd26a684b08c58aa2e1fe41f44ba8837737a74a4681b0508d9604aa7484c5ec68ccb369c056702b03f75722033597ce5b55e5a88f14e3ee23b1c96f5d38604128cf2e901a745996f4a29d412e5ce1b4ee82cf2f248aa80f8c8f2eb7b6951f1b0910857a17edc319bc76b1a59fe05f9394a5b16a91ef15afba9ee2e115c9c9afc51a8d7c4a61cb335724855007f564fec341342d53717023118917304c2dfcf0bd79e456c94718b8388767564d6055aad1d505f36506fd6e8cb1a10f6fe8a67e772030dab78b43ec15fae0322fe991944be86cd0bd78ba00110474dec84dd9597a602c4a6c97a27f13159ff1e32fa63e28f63278b09c9d051cabf8bb49f2780d4c33ca061ff16c6288ff29d46a22e31c719dfaf6087590ea8197c066f9057e0c24b40f5eb205b730985daaf7e7d85fecd7449677c0ec1abd7111d795de6de4a6ab30cf266987dd55569f2a705ae16f51de738fcc4cc218f2cb9dab8af509ba26ed93ddc7a73028d9187527fa6d554b491faade05852eb50847affb81b58d2501989d5b4a9de4237b33bea154c4d016b08f3e1c39ee61382fb500be2123ccfcd30e37c88979a19d636c9dcf5fcec822e5558cb2939e40e1af039aa942b503c9fce95f8ff51a2977f58e4d5032f9099fb4783b7764dd1299a1a89f21c1f5e38038f219e9a4e9d6c6edaef70132ee1f2a580227b256c9139d339183e278277a9dd260bf7d8a9853c4185f9b6509ab9b67a3563ed0b9afca6f2db28258025d8c2036cc4cd9fb4f7007dca96597c26476def29191cb0c77c44df80994ad3c79833820ce2f1108d17a083fa857397d2839c37899ce2e634578b3eee0135f66cfe1aeb7bcb8732fedba16093a6e666321236f56e285058d00c1bba2db3263844e4b0ea013e7a8d370ecae9ac7a9620eaaa244b25be1eb6e71368f550736d3ca13747edfb6254c9a373aadcc30a148907d39e81bdda8a53e8644b39a1625de851fc5d759686c63c76bb162b244415175eb0ccaf3a63d4ad910369e30313bdeb175ace63ad33e400e642dbdcb472a336d82d2f818e6888e5aef472d03fd298a5d14b8da432b72e6c5f5694887a7e4e165bc41055379bd204298d65796d5351a9121f949904bd6c87510649127ef5797d432c6c759939c99026d57076db225d7d0df01ab9b9e3e1221e7e323ff752eb7988cd2b2bbfc8ae7fdd23c3f7cfd2ae2679014e986e3c01b6c66a67fbf69483ad3e89c1f2dd0867f411151e048d81212c0b5e2fbba9d317f283f97c9576180121f41856355b906a22c69869bc27729db5974c5b1257b35d186dcb4e9f7bc51dc331c0ff9baed5955e5052a6ac7743b"
+ let t = readZebraTransaction h
+ it "Sapling component is read" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let sb = zt_sBundle t'
+ fromRawSBundle sb `shouldNotBe` Nothing
+ it "Orchard component is read" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let ob = zt_oBundle t'
+ fromRawOBundle ob `shouldNotBe` Nothing
+ it "Orchard CMX is present" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let ob = zt_oBundle t'
+ case fromRawOBundle ob of
+ Nothing -> assertFailure "Couldn't open the orchard bundle"
+ Just s -> toBytes (cmx (head (obActions s))) `shouldNotBe` ""
+ describe "Raw transaction with Transparent inputs" $ do
+ let h =
+ hexString
+ "0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000"
+ let t = readZebraTransaction h
+ it "TxIn parsing" $ do
+ case t of
+ Nothing -> assertFailure "Couldn't decode"
+ Just t' -> do
+ let tb = zt_tBundle t'
+ show tb `shouldNotBe` ""
+ describe "Sapling commitment trees" $ do
+ let tree =
+ SaplingCommitmentTree $
+ hexString
+ "01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
+ let cmu1 =
+ hexString
+ "45e47c5df6f5c5e48aa3526e977b2d1b57eda57214e36f06128008cb17b0125f"
+ let cmu2 =
+ hexString
+ "426ef44b3b22e0eeda7e4d2b62bac63966572b224e50f97ee56c9490cde4910d"
+ let tree2 =
+ hexString
+ "01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
+ it "Commitment tree is updated correctly" $ do
+ let t1 = updateSaplingCommitmentTree tree cmu1
+ t1 `shouldNotBe` Nothing
+ it "Incremental witness is generated" $ do
+ let t1 = updateSaplingCommitmentTree tree cmu1
+ case t1 of
+ Nothing -> assertFailure "Failed to append node to tree"
+ Just t -> getSaplingWitness t `shouldNotBe` Nothing
+ it "Position of note is obtained" $ do
+ let p =
+ getSaplingNotePosition <$>
+ (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1)
+ p `shouldBe` Just 129405
+ describe "Orchard commitment trees" $ do
+ let tree =
+ OrchardCommitmentTree $
+ hexString
+ "01d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d01bb83047c72eb4f71813d00dee37082169546df2d7097bf7fd187ef6a93063b281f015e710ed46b53b48b12733652e150f9dcbc7e7b571cf64f294cf903864c78882f01cac32bc901f501f714a028f7ebe44c1dd8b42661be1c96730066a6fa6ede653600000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
+ let cmx =
+ hexString
+ "d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d"
+ it "Commitment tree is updated correctly" $ do
+ let t1 = updateOrchardCommitmentTree tree cmx
+ t1 `shouldNotBe` Nothing
+ it "Incremental witness is generated" $ do
+ let t1 = updateOrchardCommitmentTree tree cmx
+ case t1 of
+ Nothing -> assertFailure "Failed to append node to tree"
+ Just t -> getOrchardWitness t `shouldNotBe` Nothing
+ it "Position of note is obtained" $ do
+ let p =
+ getOrchardNotePosition <$>
+ (getOrchardWitness =<< updateOrchardCommitmentTree tree cmx)
+ p `shouldBe` Just 39432
+ describe "Extract Sapling Address - UA Valid" $ do
+ let sr =
+ getSaplingFromUA
+ "u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l"
+ it "Extract sapling address" $ do
+ case sr of
+ Nothing ->
+ assertFailure "UA invalid or does not contain a Sapling receiver"
+ Just t -> do
+ print t
+ t `shouldBe`
+ "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60"
+ describe "Extract Sapling Address - UA Invalid" $ do
+ let sr =
+ getSaplingFromUA
+ "u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l"
+ it "Try to extract sapling address from invalid UA" $ do
+ sr `shouldBe` Nothing
+ describe "Decode a Transparent Address" $ do
+ let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e"
+ it "Try to decode a valid Transparent Address" $ do
+ print ta
+ ta `shouldNotBe` Nothing
+ it "Encode and decode should be the same" $ do
+ let ua =
+ "u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x"
+ case isValidUnifiedAddress ua of
+ Nothing -> assertFailure "Bad UA"
+ Just u -> do
+ let tAdd =
+ maybe
+ "No transparent"
+ (encodeTransparentReceiver (ua_net u)) $
+ t_rec u
+ (ta_receiver <$> decodeTransparentAddress (E.encodeUtf8 tAdd)) `shouldBe`
+ t_rec u
+ describe "Decode a Sapling Address (MainNet)" $ do
+ let sa =
+ decodeSaplingAddress
+ "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60"
+ it "Try to decode a valid MainNet Sapling Address" $ do
+ case sa of
+ Nothing -> assertFailure "Failed to decode MainNet SaplingAddress"
+ Just s -- Sapling address decoded succesfully
+ -> do
+ let sh = encodeSaplingAddress (net_type s) (sa_receiver s)
+ case sh of
+ Nothing ->
+ assertFailure "Failed to encode MainNet SaplingAddress"
+ Just zsh -> do
+ print zsh
+ zsh `shouldBe`
+ "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60"
+ describe "Decode a Sapling Address (TestNet)" $ do
+ let sa =
+ decodeSaplingAddress
+ "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5"
+ it "Try to decode a valid TestNet Sapling Address " $ do
+ case sa of
+ Nothing -> assertFailure "Failed to decode TestNet SaplingAddress"
+ Just s -- Sapling address decoded succesfully
+ -> do
+ let sh = encodeSaplingAddress (net_type s) (sa_receiver s)
+ case sh of
+ Nothing ->
+ assertFailure "Failed to encode TestNet SaplingAddress"
+ Just zsh -> do
+ print zsh
+ zsh `shouldBe`
+ "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5"
+ describe "Decode Sapling Output with spending key" $ do
+ let p =
+ Phrase
+ "cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest"
+ let seed = getWalletSeed p
+ let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0
+ let txHex =
+ hexString
+ "050000800a27a726b4d0d6c20000000005bd2500000001efdca98904a8b34a0fcfefc47240249a18a530289f2ff6a15bb9885abf6e541aa6c9a851b63df5cbd7473b5b5bc5efbc4a3130c15ce18b92b87b67de2cc72846babecb9030390f0ab02d54960636633695c0c1631f9e87625b92976b12492fcb02d260bd68e7c21cc8ffaad1c3ea5182dee524a09f7b5babcd7d6d85270d6d0d3d48aa620513b632cc17eca91e237b38b1212739e8df6ddcb85c2314cd76c52e26791e2d99fd7ea468b2fb4df3e9bce602daa6a6ad8a57fcee27ce5eee3da998902b5ecf4651adaed9bfd17d0d6de3f4d8c1d391f91c571a341f105e3afd913c25012c623f274406cccda52535a0b4ab883b763d1c7405e6aac941575a965ef0b9b0114588d9d7a2be82a3eb11c662c1fc85a31e8f07fa2c2a9dcb295d5530e893b2aa43c04a508eb182257c791aa4f75d6f5d77a8fbace89666ee80b572015ae4c33b8510e88a8a338da305237d1fc5d267a4ea6914d6f9cacd1a5e158a1acd2722284ff71fd870d92143872def753e2ecde9c04c3b80b513609d47a523243508548c8873426c52eaab76b9cf2b90e63fe9c5485eddfcacac65ca811c634df22a01610b22770ba3d294f566de7e0764641985b62ce2cd71735999111bb323b14d87e482201798672881da255062dc68f912343b911154f7c96442a01be25219f0315bc78c1c3693f80e9a9cd062eee2de8a37cf073f505d936f1be9ef9a78f95e8f70ef20e0e23c2c59c37cd94cfdc5c0d0d14849aea4aa6b6a7b33a0fc57a2a91a5950ba92fec1465db65b02e36fff82d2cacb4cc9038d1a8f30559a6211c52507f2dba723d54e08e82b127cd63fad97e44292e90aa87a1e798504d0f67f242561eecc3ab40911472d75457afecaf9a026e9e0ebcd07c7e78c0bd2963674fa51d6d2c157791c5d60574047a0a4ef496f1465117ed6b9408a107cd77daa0501bb49f548b97d6eb45ace8c0620b0361031f6031af5ff53015c6755fd3c76e9e45c8694d0f38722aa9bd31987fb503a0fef5f3f97033a19212e9d549b7f7de12c2dd3f1b5db353cb40c486a0ab00ecd80d1bc6ef96ba9855bbae57c6bb9965e1ac01d4d2cccd150a44a1e511bf8f9956e7472339306f7c14d90e61e4b01d6e3b98a6b5008ce102b4646d31fc11af90d4834dd8e0005fdc8ca2310fd6cdd4180def97074fedfe645e3c9f399ca9bd282b7743d943c95e4c76a7deae192be69b80f71f7f3dd9ff5199f7c82f743030ffdd69fe88463ef6efd62d329662ab48325d4d67bed52bcdf16d1991adf5fdeadb02e81bf627d5c58d013277f1ab04fec34985917a7495d4056d47986ebcdd18c08cd9f6ba3a1487f646c1a8d99714db89d3d7708cb3455d72943a609f3db3097b7ab295316c2bcf88c73ca159e995acddcc149fd186bd82cd34f02af8e4b68ad39db8621ae92db4d41f53d2567948a423ed2a7fe38b1ff77c0b1cf46d90077bd062b3c2edb9caab921b01c546866a376d29feeaee6ff8e7583c8b4a4f631c4cb339172e5b217ee75793f7608ea6d600c680f86ce63a89a5963ec5e20ade9464f3142c0100c8ce4426b34f887b68c9658cad2dbf8a1c5da4eb7b576bab32b5af129437cd34e3c8b8d17bece85cb661ed20224539ed3eb1d61eec727deb79585f8630a807acd44978dd6ec190312d1c8dacc4cf02783136d730ab0216f7105a9f11d090c5a6e69eb726ba5e785de2f99dc7a8051250544cc661b56913715fdd31cbb5fff3f0b5000a325db631ff60ad241051511dd32e322900df8f5707a59bbe8c1ac85f4be97cfe6700a9193458fa23a70b6639f0bdfd879493520d8e4e593002f5cd9d4bb60aad67610cfb03142a5fc9cefe08cf76a23706288779cefa08ba26e33b9ca58109e0e8abf150dceec584ceabe74e3c838f86a4c3920a37d8dac148317a3c3e132ecaa551ddb7085a8702dc970b5dee5264ee1bf8bc89e92912ff1f3b4e5d10627f3e55b7f6216c0249cb2aabd1267762cad1a88b6c81bc42ac47b6f53e5598ea1dce228165f0e748c0af90eaa421813dc38a521daf83e688c298ddc0a1adac1418f04a7739a6d4f7fdfae64320d8d8107f5bacb87596b7f777a85002d9dcf66c930bfa427a6508198d8cf625279b3c6b438e098f04453d9607bb83271b2ce1f11bcb4c59f0edc4135cf4d5bade66c422c654f9dff4fa720589975d05ae7b10b1d6d2ed72c26534be10a229605f4e946c21abdfd629b1ec18e5819a1be5c89fbf446c65b39fdb32ad5cd9079beee1b73bb3b3a37c3cc10270000000000007a2aed01922521f492d80b45e7d267745f3423c1760982418b9f69390660fb12a6ec696bed9fb392633c496f576a910a0c4d5e37ad76839d951d4895f76948c72fcd580590e014c9b0b3a091ec19ab40a5d59131024055636725f98c29303bab5718006e7154379227c58d18b04938b43c8fa4fe793db395754c3270d71f90dd0fd907c39c848446f99de6efefebe414f981c29f2eb448d1d15ff314ee0c763b66dbf6917e44e18310e0fa1340069512867157552c696a3f89c909668e0698e83825b1b0d73a38a2d99531a8c053a48acdcf9fa9bafcd8ce1f3c3b784bbe23775be45176a5b0a42978008ba5c60020714ba3c0174c9a068b45081267609f7ca31e32d7367cdb0ffb88c7cb762e55b48e4edc639da5ec739a1a5ae0d7b9a16000adb66352c377e3a316a18c66f3c994c0e62b81b3ebc485e4e3438426f1a32a3a875483def774eb45e13df7a14c9ac4f9ac22bcebf3515a6dafe9aad745451708180fadcce8870302e65d90954fd9cae32e3374c8734385c6b4bc3581d997e0f20e6db537a39c8f9b5e54e0a2bcc41e260387e4faabc2f09e7613e26c472d295ad56ee97affb96021f6e882eed25481628f5fab8bb4705cb7df163eb73ab45323dfaebf8e2108fb8c444833d640203455338459c1a68a0a8314f476ab6b230547ade1d2f2df907bb9fb0d2b6b0361e4d8a20118d8328274c71f84ee6aeb761c36fdae537fa747ab9a251fae846197d356b90913b00440e2c034c7cce498a01c5495dbda102d9151fb8f84bc55728a72d2fb5edf660d7df5a155212f6c10e12649028b172f8e4ad32e9f2898cf8e352c7898a2bdb30ff22e903371ec4fc47c49f1b56f0bae3975d80063f251203a5e15afa9af7bfd278e7af0cb5fc3d522639e8f4e3db7d8d5c483dfdc4207c36f0fa72df98f8970abeb8d872cd04a4113e6863db9dd9c72ce08ac8bb01da342fd43ac27a02a4b2db787efe8c98fc55a7730e43f98a85a13942a95545f630191fe4e879b515999c0a63f9eb318d6083de8bed00600"
+ let txHex2 =
+ hexString
+ "0400008085202f8900000000000045b52a00e80300000000000001638ca17ebf9c8fa40eb74be6cc0f702c8a009695b0ae1debc87f4623baa2fdb3f7d7639e3fc8c696cf5ee19d3efccf50e38546de089c4d95a4a3a4a5dd53ef4465bf1d0f111e8a87f1f499ba1d7acaa523f344046ed86c1a42d412fdfb6803cf8e155df11d1beec2d11aecc2cda622b4028db0691fd61d74969a96021bec1f45855f06c8e2c1e02aed41c4549f2e4227572f873847542e1e29077269460e2885cc0a1775378a20e7b1ef40d1e358020ea3c24991ea5bc150932d538b126ea8cdbe5fef9728efedf40912b75634c4ba8288a2ab6f09ad4b8081d395bb4dc49f221011891a8b4af3cc831bf5bdf013950d6a4c4442d314f62d18913a2e18f041cac9535ca8a973e38c3e9796f4eac80d4d88a39c701b17aaf452c19410fe45188d86d91fdee3799ba7bce793c3d0bfba9139c006eb4b279b48dbed04ff75564a068d4b022ac523d40f0a4c65d955873e7498df63bbd87b2518aaac084c8bd8d44d620a07d994ee09ae8e1792d47393b6b570ae1a9122dbf7bcc1f494120d20ca0c028e7443befa6ec5956c3b0c593c6065c7ee909f90de6188e38c24e57d8f0f4cc8fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858d322258e2bc7f6072ad014d8b249567b14276280bab8f2fe6b62ead86cda2dbf1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d86459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa08385f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62d283384bb097120b47b780b4a60b5c04d53e90c9ff2e781ac9b039384fbd48d1d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843e007aea2e07f40f139e24d0e497eac26727a2b9aad4f223887cd423b9fd6cf58353f0b011ea73b2a9843c25529df7b88f2357fba75353218012d1ba4b89b45d50564e9e7f16101c69c4232d2de683bd0960cc87a016dce7bb088720f8f6c092bdf2f2954e937168b780b2f8a7828d49f5b0d8809f25d74e6374065c4596c5a8ef2d2c4a18a0bbbb2ae9cb6d0f6332614270c4438ef07effd094df92b74cbb828305debaac629cb01a98e32d16e04d0b8c52c8a9a2c526481fd627d453e97bf33ea717775dc06abb09f0b95a435daf64e41d198ef6c686baffe891e9a1da8df943b12d1b5f192387b5b367db75593ace3399439621ec9d4195c188be12781cb165e559161381482bc513bb9a35d3746c30d88cc475642d63016c8a75347c1bb706bb3276218e6269e6e1a1c8912b077b5e82235ba837899c6c23bec2917457442f9f607d3d63b8c8ea5f065ca5e7a4ec037665a40003d9a0c95d97234ac9d50efa385cadfc79820a017abfd3f678ba5d7a8b890f5ca6c10ffbea407c6ac18638bde1806f739b7fd6ed49c665884f8cd4cdf77a9c02cad0ffa8971cdbe664ea7539521c87d9658808b112d1ea22b155208dfd693a48d5dd703279192ae5d9acab817c53193e17542db27d63764daccefb0be38b2bbdbd01345919e2f08811f99fb54a33033f6abc76c5ab4ebc9056f0e8d3005f80ffaf403ea744ff86a30fdc23f319972c4ccc81b3f6a23ad9fb00e480cd0b7d138e9db6e7cd25e424d29205782b06e4b0c5b932820ee2035fe657ec36489f60829d8c31dfe36accbc0fb78176508b6024b60909dba12747745d454c150f54945ef6a9a085e3a6b6539ace61f767e1e5fb1054355bf26622674d58d4b5037457138e50abfefe92b515c74c608fcd2a5af50eab0bc85d57ba3bda52fa00c9c15d44a24586b2cacc6be00209ae49202430645d4a5022703b1cace87829dc783d5092d97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e889200fa5344b9491cd5afeb2dcc03364b8a9380fa11a5c4ea09bb60d13aa217acc38fe3e83b5569bf75576dacb84cfc28cec75ee724fcb8b5d4fdb12948b1098ebc07"
+ let tree =
+ SaplingCommitmentTree $
+ hexString
+ "01fef9aa4cfdc8c26eb2693907e96eccaacd61ed04ed7860f1d83260e6e383b936001001bdd6575663c970df26d8eb84b0be6411f65337912c90b1a0a8ba2b9303326d350000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
+ let sk =
+ SaplingSpendingKey
+ "\ETX\189\201\190\128\NUL\NUL\NUL\128$q\CAN+P\196\229VM\129:+\SI\171W\248)\216\196\215\141\229\239\141*Y\175}\253\232s\156(K\179\GS\139\232\246\ETB\181`l\226i\156\211#\tdNT\248\138i\220\136\209>U\150cl\f+\201i\132\250\220\208\137\178\196\238\141\\,\208\194*\245\145b\FS\244\164\230\194C`\ACK\140\220\194\ACK\190\224u\FS\167CK\181\DC2\r\US\152\237\238\137\209S\238\240D'\248 \255\164F\254)n\a\231r\128!\177\168\210\141\234\STX5\189\b=\191\ETX:\USgvB\155 \162\234\200\DC1$z\252\177C+\252"
+ let so1 =
+ ShieldedOutput
+ (hexString
+ "8e7443befa6ec5956c3b0c593c6065c7ee909f90de6188e38c24e57d8f0f4cc8")
+ (hexString
+ "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")
+ (hexString
+ "d322258e2bc7f6072ad014d8b249567b14276280bab8f2fe6b62ead86cda2dbf")
+ (hexString
+ "1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d8")
+ (hexString
+ "6459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa083")
+ (hexString
+ "85f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62")
+ let so2 =
+ ShieldedOutput
+ (hexString
+ "d283384bb097120b47b780b4a60b5c04d53e90c9ff2e781ac9b039384fbd48d1")
+ (hexString
+ "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")
+ (hexString
+ "e007aea2e07f40f139e24d0e497eac26727a2b9aad4f223887cd423b9fd6cf58")
+ (hexString
+ "353f0b011ea73b2a9843c25529df7b88f2357fba75353218012d1ba4b89b45d50564e9e7f16101c69c4232d2de683bd0960cc87a016dce7bb088720f8f6c092bdf2f2954e937168b780b2f8a7828d49f5b0d8809f25d74e6374065c4596c5a8ef2d2c4a18a0bbbb2ae9cb6d0f6332614270c4438ef07effd094df92b74cbb828305debaac629cb01a98e32d16e04d0b8c52c8a9a2c526481fd627d453e97bf33ea717775dc06abb09f0b95a435daf64e41d198ef6c686baffe891e9a1da8df943b12d1b5f192387b5b367db75593ace3399439621ec9d4195c188be12781cb165e559161381482bc513bb9a35d3746c30d88cc475642d63016c8a75347c1bb706bb3276218e6269e6e1a1c8912b077b5e82235ba837899c6c23bec2917457442f9f607d3d63b8c8ea5f065ca5e7a4ec037665a40003d9a0c95d97234ac9d50efa385cadfc79820a017abfd3f678ba5d7a8b890f5ca6c10ffbea407c6ac18638bde1806f739b7fd6ed49c665884f8cd4cdf77a9c02cad0ffa8971cdbe664ea7539521c87d9658808b112d1ea22b155208dfd693a48d5dd703279192ae5d9acab817c53193e17542db27d63764daccefb0be38b2bbdbd01345919e2f08811f99fb54a33033f6abc76c5ab4ebc9056f0e8d3005f80ffaf403ea744ff86a30fdc23f319972c4ccc81b3f6a23ad9fb00e480cd0b7d138e9db6e7cd25e424d29205782b06e4b0c5b932820ee2035fe657ec36489f60829d8c31dfe36accbc0fb78176508b6024b60909dba12747745d454c150f54945ef6a9a085e3a6b6539ace61f767e1e5fb1")
+ (hexString
+ "054355bf26622674d58d4b5037457138e50abfefe92b515c74c608fcd2a5af50eab0bc85d57ba3bda52fa00c9c15d44a24586b2cacc6be00209ae49202430645d4a5022703b1cace87829dc783d5092d")
+ (hexString
+ "97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e8892")
+ it "Sap output 1" $ do
+ let pos =
+ getSaplingNotePosition <$>
+ (getSaplingWitness =<<
+ updateSaplingCommitmentTree
+ tree
+ (fromText
+ "fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858"))
+ case pos of
+ Nothing -> assertFailure "couldn't get note position"
+ Just p -> do
+ let dn = decodeSaplingOutputEsk sk so1 TestNet External p
+ dn `shouldBe` Nothing
+ it "Sap output 2" $ do
+ case readZebraTransaction txHex2 of
+ Nothing -> assertFailure "Failed to read Tx"
+ Just tx -> do
+ let sBundle = fromRawSBundle $ zt_sBundle tx
+ case sBundle of
+ Nothing -> assertFailure "Failed to get sapling bundle"
+ Just sB -> do
+ let sOuts = sbOutputs sB
+ let pos =
+ getSaplingNotePosition <$>
+ (getSaplingWitness =<<
+ updateSaplingCommitmentTree
+ tree
+ (fromText
+ "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843"))
+ case pos of
+ Nothing -> assertFailure "couldn't get note position"
+ Just p -> do
+ let dn =
+ decodeSaplingOutputEsk
+ sk
+ (head . tail $ sOuts)
+ TestNet
+ External
+ p
+ dn `shouldBe` Nothing
+ it "Decode Sapling Output from Zingo" $ do
+ case readZebraTransaction txHex of
+ Nothing -> assertFailure "Failed to read Tx"
+ Just tx -> do
+ case sK of
+ Nothing -> assertFailure "Failed to generate spending key"
+ Just sK' -> do
+ let sBundle = fromRawSBundle $ zt_sBundle tx
+ case sBundle of
+ Nothing -> assertFailure "Failed to get sapling bundle"
+ Just sB -> do
+ let sOuts = sbOutputs sB
+ let pos =
+ getSaplingNotePosition <$>
+ (getSaplingWitness =<<
+ updateSaplingCommitmentTree
+ tree
+ (fromText
+ "d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843"))
+ case pos of
+ Nothing -> assertFailure "couldn't get note position"
+ Just p -> do
+ let dn =
+ decodeSaplingOutputEsk
+ sK'
+ (head . tail $ sOuts)
+ MainNet
+ External
+ p
+ dn `shouldNotBe` Nothing
+ describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do
+ let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e"
+ it "Try to generate valid ExchangeAddress from Transparent Address" $ do
+ case ta of
+ Nothing -> assertFailure "Failed to decode transparent address"
+ Just t -> do
+ case (tr_type (ta_receiver t)) of
+ P2SH ->
+ assertFailure
+ "P2SH not supported for ExchengeAddress generation"
+ P2PKH -> do
+ let exch =
+ encodeExchangeAddress (ta_network t) (ta_receiver t)
+ case exch of
+ Nothing -> assertFailure "Failed to encode Exchange address"
+ Just addr -> do
+ let eadr = decodeExchangeAddress addr
+ eadr `shouldNotBe` Nothing
+ describe "Witness updates" $ do
+ it "Sapling" $ do
+ let wit =
+ SaplingWitness $
+ hexString
+ "01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e57ec972a9b9383dc9cb228980d2d7752bb2abebc4a604ca48c5457039d2e05b000301392bed8592185dde5ab7fc81aed75e98fcf041f1a3fda55ad0b0b139ba9380130001808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15"
+ let cmus =
+ [ hexString
+ "958ccdc752f2f593f6c1c8e2d7201348cd896e54c6d3c92200bdbe8b859eac44"
+ , hexString
+ "e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447"
+ , hexString
+ "0c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e18"
+ ]
+ updateSaplingWitness wit cmus `shouldBe`
+ SaplingWitness
+ (hexString
+ "01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447010c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e1803000121c06ee1f1584f79d50785797a694c742be2ded600367ab7d54f3ed49e3adf7201808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15")
+ it "Orchard" $ do
+ let wit =
+ OrchardWitness $
+ hexString
+ "016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e000200019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301"
+ let cmxs =
+ [ hexString
+ "712ba86615ff4447e8d7c7b59f3873f03c03a173438b8e4c8d416756ed4fae10"
+ , hexString
+ "c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e"
+ , hexString
+ "ac20b8170b008888c19fc6e16f5e30a5ef1653e5219d0cd0c9353c3aa8f79823"
+ ]
+ updateOrchardWitness wit cmxs `shouldBe`
+ OrchardWitness
+ (hexString
+ "016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e0002010cfb50d8c877eb39e9c07082a032dd99d34be7c19fa7f30e9fecf5f14736240f019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301")
+
+-- | Properties
+prop_PhraseLength :: Property
+prop_PhraseLength =
+ ioProperty $ do
+ p <- generateWalletSeedPhrase
+ return $ BS.length (getBytes p) >= 95
+
+prop_SeedLength :: Property
+prop_SeedLength =
+ ioProperty $ do
+ p <- generateWalletSeedPhrase
+ let s = getWalletSeed p
+ return $ maybe 0 (BS.length . getBytes) s === 64
+
+prop_OrchardSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
+prop_OrchardSpendingKey s c (NonNegative i) =
+ genOrchardSpendingKey s c i =/= Nothing
+
+prop_OrchardReceiver ::
+ Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property
+prop_OrchardReceiver s c (NonNegative i) (NonNegative j) scope =
+ genOrchardReceiver
+ j
+ scope
+ (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) =/=
+ Nothing
+
+prop_SaplingSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
+prop_SaplingSpendingKey s c (NonNegative i) =
+ genSaplingSpendingKey s c i =/= Nothing
+
+prop_SaplingReceiver ::
+ Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property
+prop_SaplingReceiver s c (NonNegative i) (NonNegative j) =
+ genSaplingPaymentAddress
+ i
+ (fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c j) =/=
+ Nothing
+
+prop_SaplingRecRepeated :: Seed -> CoinType -> NonNegative Int -> Property
+prop_SaplingRecRepeated s c (NonNegative i) =
+ genSaplingPaymentAddress
+ i
+ (fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c 1) =/=
+ genSaplingPaymentAddress
+ (i + 1)
+ (fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c 1)
+
+prop_OrchardRecRepeated ::
+ Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property
+prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) scope =
+ genOrchardReceiver
+ j
+ scope
+ (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) =/=
+ genOrchardReceiver
+ (j + 1)
+ scope
+ (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i)
+
+prop_TransparentSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
+prop_TransparentSpendingKey s coinType (NonNegative i) =
+ ioProperty $ do
+ k <- genTransparentPrvKey s coinType i
+ return $ xPrvChild k == fromIntegral i
+
+prop_TransparentReceiver ::
+ Seed -> CoinType -> Scope -> NonNegative Int -> NonNegative Int -> Property
+prop_TransparentReceiver s coinType scope (NonNegative i) (NonNegative j) =
+ ioProperty $ do
+ k <- genTransparentPrvKey s coinType i
+ r <- genTransparentReceiver j scope k
+ return $ tr_type r == P2PKH
+
+-- | Generators
+genOrcArgs :: Gen (CoinType, Int, Int)
+genOrcArgs = do
+ i <- arbitrarySizedNatural
+ j <- arbitrarySizedNatural
+ c <- elements [MainNetCoin, TestNetCoin, RegTestNetCoin]
+ return (c, i, j)
+
+genSapArgs :: Gen Int
+genSapArgs = choose (1, 50)
+
+getSeed :: IO Seed
+getSeed = do
+ p <- generateWalletSeedPhrase
+ let s = getWalletSeed p
+ case s of
+ Nothing -> throwIO $ userError "Couldn't generate seed"
+ Just s' -> return s'
+
+-- | Arbitrary instances
+instance Arbitrary CoinType where
+ arbitrary = elements [MainNetCoin, TestNetCoin, RegTestNetCoin]
+
+instance Arbitrary Scope where
+ arbitrary = elements [External, Internal]
diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal
index 30803ce..361d33e 100644
--- a/zcash-haskell.cabal
+++ b/zcash-haskell.cabal
@@ -1,42 +1,47 @@
-cabal-version: 1.12
+cabal-version: 3.0
--- This file has been generated from package.yaml by hpack version 0.35.2.
+-- This file has been generated from package.yaml by hpack version 0.36.0.
--
-- see: https://github.com/sol/hpack
name: zcash-haskell
-version: 0.3.0
+version: 0.6.1.1
synopsis: Utilities to interact with the Zcash blockchain
description: Please see the README on the repo at
category: Blockchain
author: Rene Vergara
maintainer: pitmutt@vergara.tech
copyright: (c)2022-2024 Vergara Technologies LLC
-license: LGPL-3
-build-type: Simple
+license: MIT
+license-file: LICENSE
+build-type: Custom
extra-source-files:
+ Setup.hs
+extra-doc-files:
README.md
CHANGELOG.md
- configure
-source-repository head
- type: git
- location: https://git.vergara.tech/Vergara_Tech/zcash-haskell
+custom-setup
+ setup-depends:
+ base >= 4.7 && < 5
+ , Cabal >= 3.0.0.0
+ , directory >= 1.3.6.0
+ , filepath >= 1.3.0.2
+ , envy
+ , regex-compat
+ , regex-base
library
exposed-modules:
C.Zcash
+ ZcashHaskell.Keys
ZcashHaskell.Orchard
ZcashHaskell.Sapling
ZcashHaskell.Transparent
ZcashHaskell.Types
ZcashHaskell.Utils
- other-modules:
- Paths_zcash_haskell
hs-source-dirs:
src
- pkgconfig-depends:
- rustzcash_wrapper-uninstalled
build-depends:
aeson
, base >=4.7 && <5
@@ -46,16 +51,22 @@ library
, cryptonite
, foreign-rust
, generics-sop
+ , hexstring >=0.12.1
, http-conduit
+ , http-client
, memory
+ , binary
, text
+ , haskoin-core
+ , secp256k1-haskell
+ , utf8-string
+ build-tool-depends:
+ c2hs:c2hs
default-language: Haskell2010
test-suite zcash-haskell-test
type: exitcode-stdio-1.0
main-is: Spec.hs
- other-modules:
- Paths_zcash_haskell
hs-source-dirs:
test
ghc-options: -threaded -rtsopts -with-rtsopts=-N
@@ -63,7 +74,16 @@ test-suite zcash-haskell-test
aeson
, base >=4.7 && <5
, bytestring
+ , haskoin-core
+ , hexstring >= 0.12.1
, hspec
+ , HUnit
+ , QuickCheck
+ , quickcheck-transformer
, text
, zcash-haskell
+ , binary
+ , cryptonite
+ , secp256k1-haskell
+ pkgconfig-depends: rustzcash_wrapper
default-language: Haskell2010
diff --git a/zebrablock.json b/zebrablock.json
new file mode 100644
index 0000000..5665f92
--- /dev/null
+++ b/zebrablock.json
@@ -0,0 +1,20 @@
+{
+ "result": {
+ "hash": "0041ee9cb0e256a73c92bb72d830143c402ea350152f56f19f74d23cf51418fb",
+ "confirmations": 3583,
+ "height": 2767099,
+ "tx": [
+ "d169ec3eda57dc750edfc1aa6b8ffb4ed2065780bfd5964de34b529503ec372f",
+ "987fcdb9bd37cbb5b205a8336de60d043f7028bebaa372828d81f3da296c7ef9"
+ ],
+ "trees": {
+ "sapling": {
+ "size": 129349
+ },
+ "orchard": {
+ "size": 39382
+ }
+ }
+ },
+ "id": 123
+}
diff --git a/zebrahexblock.json b/zebrahexblock.json
new file mode 100644
index 0000000..19d6b90
--- /dev/null
+++ b/zebrahexblock.json
@@ -0,0 +1,4 @@
+{
+ "result": "040000006deacb36066fbbf99f00436c5a384e885c0cecff393b922ccfb22ed81b9e3600722039ae03cfdbee344a18774168795a936a720db144408a9abd92c55eb4a276f1b96e93c15bc2943a1f2483e466c456d2ea22ad82eea3d5788dbb5448bcb30c33d0fd658002451f6c005c8fa5b5d0cb7a24d326de3fd34422e22718a0c5e23972800292ed830000fd4005022b23ffe50b150fe854961e44e5d752975857fbc5026bc06a7550448e858e71dc7d927b44a04a325df027973f510de1ebb56327d7ed086b7419ff71db198940760dcede94a775fd67f515836ec4caa7d17bdc6e0ec8924c9dc74945496e673d104b85d35df91ec33942c84b7404579023b24d78b30bcde7563740f3a10a159918daafa18d955b0bb74c94782195ff5195936b21e6ec853a8ef269b21ac325675445b1815dfab18904ad6e2ad19ec8a753d1f0fd3b9171e0ea29f274bb261b028508d984fcdb9ed3df6e77ba3eb038d606d517b7461ebee25b57de47b2f2dde11c7504bacd271f2f44e405f79faaf99962145aa537b2b1f5f4f55ea30657f48ae51f0aff8f1bc1669311607cf941717e561b5e7e44bd48adc9aafba49d947deabe07e475c52e3eb13d485e60790db1a04540b4dfe992578b9758ed42ad8bee87a60b71cc88357f2d654a368cad751455057ed219766206094c899100ffc2c1b306409931d608f99a4ba850403eeaf092ee8b47b24dfad9fe18520d3ecaa98d7cf915f270629ab3fa8214f439cd0a160df22be1d99d7a979bbbd1fcb2dfd8ccfafa1207691013f2a524e1613150e0c679ac5950cb08211defa839fc537d5d941c6141e9d724ff468b9759ec7eb825132a2a78f5b9c413ee9674b44dd25f8bb65d1e839a24b36ee0eca01129935e73437e4a08edc8a2ff97d10752867f0e46cbc0ba2ae7459a7f9fbf41da3ea7be2618caa57728aae1d378f2a408f9569da556d0fa630c183d94dd542052dbc9b52ef75585ca16cf5793240f61b15dca17bead2cc524856b36c12a74c7187480090e8c01f6c79c3cafe4e50eac4c89f6991df7ff040913e3f70058aaa123f015a86b433b8a621b555f4d1abacf785c477257cc3f43ba13220e29ef653ab6be34ce223455e9bb5d775a19200267035efe861ce55503545cc85db40f59cbb4f049154983509e19877d181c3ffeff7c8fc24f1ea3f22e2513de60f340a8c7d019b76175055bc87f94dfb6381a5319e237ab5d269e1fdbe7a9e38d7fcadbf32528e1f295f5fafd1bde5b09a24c0d04d2a401e8efd352ac51b24da627ed62c85c5fefdd1019b0f9ffd86a2c30ed03d2b1b3181e261f7b05accb95a8b1109b72dd1759b7158a1b7a2c913b0fd6b5ad1dba25740139070648641739985e9e82036e9941c15580832dc4258f425c44296cc7b0938910b29cbdfcdc48df4b753744297fc656397ff504c50ea77f0cf229eb25849621e417275bb58f0b773a173d69bdf1339438b5cd5a44871436b8f56e37b059ae06ae61517e2e2857d5b422846f15d440d46fe8be4217836c545d56cfa75d46539cd0b71311e40abe30c21766ca99310bb5bab07061d69b250fc99f855c7b1218275dbe69d2d1b0aba51815126e08f68553d99de9078aad6a732b16618578127604aa0c565b2a5f0f1f15f9916ee72ffb43ace3a615bf74c461ac150fc5fa3537c78537d3aec997a1b426df2db27ad4b2dad1d34cabe61c16a9b9118ec017b650c4fc1669ecb725330c1849136e503d719b081157704377b8c778cf79031e1d25d98f4d3eb537aa564191404132231db19a64102a4c37395a86c6d557715379e39b6ec3bd7e5e4611ceeb45198e577f3efd92dea57f694ccbb20b0b44082a22cb61c758c063c862156ba6093aeed8986e1f0afd3a26f46022d515af226bdac94fb0f61f57708c2285c39c8df04a65cbdbb3fa06a53482131ed1bd8a66ed9fcdb7a995a37de76a67d75932deb989fdf565084b10954caca8919dc125483d722a02207838ccb94bb9df367e23ae37efe8c55d23bdf9cdb31d5defbc0fe25fd6dd1bf53bf30112c3babb9161d42cd5a58330cc4a44c999b2bfc701a45e7ec3b5ef493c9e1887020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603fb382a011cffffffff0468b6e60e000000001976a91401cb2de6a35b807a6e75d7fbfcc8107b2b9d39f488ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000fb382a0000000000000000000000000400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02",
+ "id": 123
+}
diff --git a/zebratx.json b/zebratx.json
new file mode 100644
index 0000000..83f6b4b
--- /dev/null
+++ b/zebratx.json
@@ -0,0 +1,8 @@
+{
+ "result": {
+ "hex": "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02",
+ "height": 2767099,
+ "confirmations": 3582
+ },
+ "id": 123
+}