Compare commits

..

259 commits

Author SHA1 Message Date
d45bd7dcf3
Milestone 3 (#101)
This PR contains all the enhancements needed for the completion of Milestone 3 of the Zenith Full Node wallet.
- Implementation of Rust Sapling parameters
- Implementation of native Haskell commitment trees
- Optimization of transaction creation

Reviewed-on: https://git.vergara.tech///Vergara_Tech/zcash-haskell/pulls/101
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-11-21 14:19:43 +00:00
5349498737
Merge pull request 'Upgrade to GHC 9.6.5' (#84) from rav001 into master
Reviewed-on: #84
2024-07-01 21:07:52 +00:00
812ff0aa70
Upgrade to GHC 9.6.5 2024-07-01 16:05:33 -05:00
874db2907e
Performance enhancement for transaction creation 2024-05-16 10:42:44 -05:00
4165cab701
Merge branch 'dev040' 2024-05-13 14:25:46 -05:00
9dddb42bb3
Merge pull request 'Implement fee calculator' (#81) from rav001 into dev040
Reviewed-on: #81
2024-05-08 18:51:34 +00:00
148abd95d0
Implement fee calculator 2024-05-08 13:49:39 -05:00
4ca5d07d1c
Merge pull request 'Fix bug in Orchard witness calculation' (#80) from rav001 into dev040
Reviewed-on: #80
2024-05-07 20:21:19 +00:00
37b485288d
Fix bug in Orchard witness calculation 2024-05-07 15:19:54 -05:00
302a2f87fa
Merge pull request 'Remove debugging' (#79) from rav001 into dev040
Reviewed-on: #79
2024-05-07 17:17:29 +00:00
2108f46afa
Remove debugging 2024-05-07 12:13:54 -05:00
22c0fe3749
Merge pull request 'Transaction creation updates' (#78) from rav001 into dev040
Reviewed-on: #78
2024-05-02 19:59:25 +00:00
1e31bb73c2
Update Rust debugging 2024-05-02 14:55:24 -05:00
c583451c83
Update debugging 2024-05-02 14:30:59 -05:00
6a632d45c1
Change error messaging 2024-05-02 14:07:54 -05:00
31c5cda65a
Improve error message 2024-05-02 13:38:12 -05:00
d5e17afc99
Debugging parameters 2024-05-02 13:34:26 -05:00
c7f2bca2c4
Remove ovk from transaction creation 2024-05-02 12:48:30 -05:00
2d14ef6b22
Update to use commitment trees for anchors 2024-05-02 12:35:36 -05:00
652e95c361
Improve error messaging 2024-05-02 12:10:53 -05:00
c144a6d624
Add more debugging to Rust 2024-05-02 07:31:01 -05:00
5fd33e7e43
Add debuggin for create transaction 2024-05-02 07:22:12 -05:00
8dbcf58a93
Merge pull request 'Fix typo in constructor' (#77) from rav001 into dev040
Reviewed-on: #77
2024-05-01 21:15:50 +00:00
bea4d2f071
Fix typo in constructor 2024-05-01 16:14:41 -05:00
61a35a034b
Merge pull request 'Add transparent key derivation' (#76) from rav001 into dev040
Reviewed-on: #76
2024-05-01 19:30:48 +00:00
bc074d0386
Add transparent key derivation 2024-05-01 14:24:18 -05:00
4a48769eeb
Merge pull request 'Add functionality for transaction creation' (#75) from rav001 into dev040
Reviewed-on: #75
2024-05-01 14:14:40 +00:00
6b391ef7d2
Merge branch 'dev040' into rav001 2024-05-01 14:14:28 +00:00
787cf40629
Add functionality for transaction creation 2024-05-01 09:12:58 -05:00
91707e13c2
Merge pull request 'Add Read instance to Rseed' (#74) from rav001 into dev040
Reviewed-on: #74
2024-05-01 00:17:18 +00:00
26a79443a7
Add Read instance to Rseed 2024-04-30 19:14:30 -05:00
c9f13643e4
Merge pull request 'Implement transaction creation' (#73) from rav001 into dev040
Reviewed-on: #73
2024-04-30 21:56:10 +00:00
ad9e2f074d
Implement transaction creation 2024-04-30 16:54:15 -05:00
f456332454
Merge pull request 'Upgrade of Rust crates' (#72) from rav001 into dev040
Reviewed-on: #72
2024-04-29 15:31:41 +00:00
201df05e9b
Merge branch 'dev040' into rav001 2024-04-29 10:29:09 -05:00
8901d97c64
Upgrade of Zcash Rust crates 2024-04-29 10:27:45 -05:00
619c80c816
Merge pull request 'Add witness update functionality' (#71) from rav001 into dev040
Reviewed-on: #71
2024-04-26 01:00:18 +00:00
24ae69cddb
Merge branch 'dev040' into rav001 2024-04-26 01:00:01 +00:00
af9806d72d
Add witness update functionality 2024-04-25 19:56:29 -05:00
00400c433d
Merge pull request 'Update note position calculation' (#70) from rav001 into dev040
Reviewed-on: #70
2024-04-24 22:28:05 +00:00
b74cd5f354
Update note position calculation 2024-04-24 16:04:56 -05:00
586cb4646c
Merge pull request 'Fix Sapling spend parsing' (#69) from rav001 into dev040
Reviewed-on: #69
2024-04-24 17:02:40 +00:00
6cf5d85c0b
Merge branch 'dev040' into rav001 2024-04-24 12:01:13 -05:00
f4612a7310
Merge pull request 'rvv040' (#67) from rvv040 into dev040
Reviewed-on: #67
2024-04-24 17:00:41 +00:00
173445612d
Merge branch 'dev040' into rvv040 2024-04-24 11:59:58 -05:00
3296909c82
Fix Sapling spend parsing 2024-04-24 11:57:00 -05:00
ebfac8438f rvv040 - decodeExchangeAddress modified to return an ExcahngeAddress object 2024-04-18 08:30:14 -04:00
f39b376380
Merge pull request 'Fix Orchard nullifier calculation' (#68) from rav001 into dev040
Reviewed-on: #68
2024-04-18 01:19:55 +00:00
7d20edd08d
Fix Orchard nullifier calculation 2024-04-17 20:14:03 -05:00
1b2c6dce24 rvv040 - Update CHANGE.LOG file 2024-04-17 20:51:12 -04:00
e6d16267ee rvv040 - Functions to create and decode an ExchangeAddress added
New DataType ExchangeAddress added.
2024-04-17 20:36:04 -04:00
4027907896
Merge pull request 'Update version' (#66) from rav001 into dev040
Reviewed-on: #66
2024-04-17 16:24:30 +00:00
e75175852b
Version bump 2024-04-17 11:21:03 -05:00
4efae180f5
Fix Rust binding for Sapling decode 2024-04-17 11:18:56 -05:00
915cd68042
Merge pull request 'Add Orchard commitment tree functionality' (#65) from rav001 into dev040
Reviewed-on: #65
2024-04-17 16:00:03 +00:00
e98ca65409
Merge branch 'dev040' into rav001 2024-04-17 10:23:30 -05:00
5abc57081a
Merge pull request 'rvv040' (#63) from rvv040 into dev040
Reviewed-on: #63
2024-04-17 14:26:38 +00:00
f94a1f83cb
Merge branch 'dev040' into rvv040 2024-04-17 09:25:46 -05:00
a56a4b1e15
Add Orchard commitment tree functionality 2024-04-17 09:21:47 -05:00
06aff8c787 rvv040 - Add encodeSaplingAddress function
Added new tests for encodeSaplingAddress and decodeSaplingAddress
2024-04-16 18:51:14 -04:00
914eb6341e
Merge pull request 'Remove debugging from Sapling decoder' (#64) from rav001 into dev040
Reviewed-on: #64
2024-04-16 20:16:46 +00:00
4f2b88974c
Remove debugging 2024-04-16 15:13:41 -05:00
058bbfe3f2 rvv040 - Added a decode Testnet Sapling Address test 2024-04-16 14:48:36 -04:00
382284a8ca rvv040 - decodeSaplingAddress Function added
SaplingAddress  data type added
2024-04-16 14:39:56 -04:00
cb985d26cd
Add debugging for Sapling decode 2024-04-16 11:19:53 -05:00
1232c3e36f
Merge pull request 'Test Sapling decoding' (#62) from rav001 into dev040
Reviewed-on: #62
2024-04-16 14:46:59 +00:00
99d772d2c7
Test Sapling decoding 2024-04-16 09:43:00 -05:00
23b8de31f1
Merge pull request 'Update Sapling commitment tree reader' (#61) from rav001 into dev040
Reviewed-on: #61
2024-04-16 01:03:01 +00:00
09ff2ed5ac
Merge branch 'dev040' into rav001 2024-04-16 01:02:38 +00:00
5402fcc072
Update Sapling commitment tree reader 2024-04-15 19:58:26 -05:00
d754498ec3
Merge pull request 'Add nullifier field to DecodedNote' (#60) from rav001 into dev040
Reviewed-on: #60
2024-04-14 22:37:52 +00:00
1b98120376
Add nullifier field to DecodedNote 2024-04-14 17:35:49 -05:00
0e52adbea4
Merge pull request 'Add new function to decode a Transparent Address in HRF' (#59) from rvv040 into dev040
Reviewed-on: #59
2024-04-14 14:27:17 +00:00
15b2f98f1d rvv040 - Fix transparent address decoding -
Function was returning the "digest" instead the "transparentReceiver"
2024-04-14 08:37:19 -04:00
95f20adb56
Merge branch 'dev040' into rvv040 2024-04-14 07:01:51 -05:00
e781ed6bd0
Add test to for transparent decoding 2024-04-14 06:57:36 -05:00
deacf373e1 rvv040 - Add new function to decode a Transparent Address in HRF
The function  retunrs a TransparentAddress object.
2024-04-13 18:27:59 -04:00
7b45bb9a6f
Merge pull request 'Add Sapling nullifier calculation' (#58) from rav001 into dev040
Reviewed-on: #58
2024-04-12 18:18:47 +00:00
6a2849aae0
Add Sapling nullifier calculation to decoding 2024-04-12 13:15:41 -05:00
0f19e376dc
Add Sapling commitment tree functionality 2024-04-11 16:01:29 -05:00
ce6a13e333
Merge branch 'dev040' into rav001 2024-04-10 11:39:27 -05:00
7efb9d0148
Merge pull request '#57 - Sapling address in Human Readable format' (#57) from rvv040 into dev040
Reviewed-on: #57
2024-04-10 16:35:39 +00:00
b6af8630b8
Merge branch 'dev040' into rvv040 2024-04-10 11:34:40 -05:00
30ca143dc9
Update changelog 2024-04-10 11:29:59 -05:00
cea8a70f44 rvva040 - Convert Sapling receiver to Human readable format
Function getSaplingFromUA -> Now returns "Maybe T.Text" instead of "T.Text"
2024-04-10 10:27:09 -04:00
3f918f734a rvv040 - Sapling address encoding to human readable format
Change Bech32m to Bech32  encoding for Sapling address
2024-04-10 09:55:41 -04:00
27b291c49a
Add function to append nodes to commitment tree 2024-04-10 07:06:04 -05:00
1f8a4cbd2b Branch rvv040 - Get Sapling Human Readable representation - In proccess
Bug - Bech32m error calculating checksum - to resolve
2024-04-09 20:23:16 -04:00
ea937f8e51
Merge pull request 'Add Orchard action decryption with spendig key' (#56) from rav001 into dev040
Reviewed-on: #56
2024-04-09 18:09:46 +00:00
7c5d4cf99f
Update version 2024-04-09 13:06:23 -05:00
4f9601f8d8
Add function to decode Orchard action with spending key 2024-04-09 13:05:57 -05:00
817c52dacf
Merge pull request 'Add Sapling output decrypting with spending key' (#55) from rav001 into dev040
Reviewed-on: #55
2024-04-08 18:01:25 +00:00
e00cf21709
Add Sapling output decrypting with spending key 2024-04-08 12:55:30 -05:00
2709d42266
Merge pull request 'Correct the parsing of the tx hash in TxIn' (#54) from rav001 into dev040
Reviewed-on: #54
2024-04-05 19:14:13 +00:00
5814cb7c9e
Correct the parsing of the tx hash in TxIn 2024-04-05 14:12:39 -05:00
a06fd08172
Merge pull request 'Fix TxIn parsing' (#53) from rav001 into dev040
Reviewed-on: #53
2024-04-05 17:52:02 +00:00
3c2e8fc994
Fix TxIn parsing 2024-04-05 12:47:17 -05:00
f375467fee
Merge pull request 'Implement identification of transparent output' (#52) from rav001 into dev040
Reviewed-on: #52
2024-04-03 21:21:08 +00:00
09411adfff
Implement test to validate transparent outputs 2024-04-03 16:16:42 -05:00
2d39576dc8
Merge branch 'dev040' into rav001 2024-04-02 14:06:07 -05:00
938ccb4b97
Add test to identify transparent outputs (#50)
Reviewed-on: #50
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-04-02 19:03:50 +00:00
8b1965b46f
Merge branch 'dev040' into rav001 2024-04-02 14:03:11 -05:00
2f18c15c86
Add test to identify transparent outputs 2024-04-02 13:42:45 -05:00
204275a9b6
Merge pull request 'Implements FFI for transactions from Zebra' (#49) from rav001 into dev040
Reviewed-on: #49
2024-04-02 17:09:42 +00:00
98fb89b7b0
Merge branch 'dev040' into rav001 2024-04-02 11:36:14 -05:00
6ad879c155
Implements FFI for transaction deserialization 2024-04-02 11:23:05 -05:00
474aadb5cd
Merge pull request 'Update Zebra block deserialization for block time' (#48) from rvv040 into dev040
Reviewed-on: #48
2024-04-01 13:09:47 +00:00
93a04c09d3
Merge branch 'dev040' into rvv040 2024-03-31 06:43:17 -05:00
58b74a4c9a
Update getBlockTime to use HexString 2024-03-29 15:51:48 -05:00
dbae2150da
Advance bundle FFI 2024-03-29 13:54:21 -05:00
7cada685ce getBlockTime function added 2024-03-28 15:06:02 -04:00
4b064af4de
Merge pull request 'Implements parsing of transparent bundles' (#47) from rav001 into dev040
Reviewed-on: #47
2024-03-27 18:25:47 +00:00
52950885c1
Implements parsing of transparent bundles 2024-03-27 13:22:34 -05:00
899b79f155
Merge pull request 'Correct the parsing of OutPoint' (#46) from rav001 into dev040
Reviewed-on: #46
2024-03-27 14:05:17 +00:00
0fb02b2514
Correct parsing of OutPoint 2024-03-27 09:00:00 -05:00
1674f9b592
Merge branch 'dev040' into rav001 2024-03-26 15:40:11 -05:00
bb9d336dc3
Implements low-level transparent components (#45)
This PR includes components for the deserialization of low-level part of transparent components.

Reviewed-on: #45
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-03-26 20:39:31 +00:00
f593fefd7f
Add types for low-level transparent components 2024-03-26 15:37:04 -05:00
ee27860299
Merge branch 'dev040' into rav001 2024-03-26 09:56:38 -05:00
4e86a2f5a4
Zebra Raw Tx deserialization (#44)
Implements features to deserialize the hex representation of of a Zebra Transaction

Reviewed-on: #44
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-03-26 14:56:10 +00:00
921d34383a
Merge remote-tracking branch 'forge/rav001' into rav001 2024-03-26 09:53:17 -05:00
b0df0480c5
Implement Transaction hex deserialization 2024-03-26 09:50:49 -05:00
03d53aa303
Merge branch 'dev040' into rav001 2024-03-22 13:04:35 -05:00
f099544162
Account for missing response fields in Zebra response (#43)
This PR adds code to account for the missing `time` field in the Zebra response for blocks and raw transactions.

Reviewed-on: #43
Co-authored-by: pitmutt <rene@vergara.network>
Co-committed-by: pitmutt <rene@vergara.network>
2024-03-22 18:04:04 +00:00
07fa36ed7d
Merge branch 'dev040' into rav001 2024-03-22 18:03:45 +00:00
5b6ce3f29b
Account for missing block time field 2024-03-21 19:27:09 -05:00
aa10e09595
Implement Sapling spends (#42)
This PR add support for Sapling spends in the communication with Zebra.

Reviewed-on: #42
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-03-21 20:15:49 +00:00
d659368cff
Merge branch 'dev040' into rav001 2024-03-21 15:14:13 -05:00
9c8a851ead
Implement Sapling spends 2024-03-21 15:12:22 -05:00
d7752e9866
Improve response handling for Zebra RPC (#41)
This PR includes changes to handle differences between the `zebrad` and `zcashd` RPC.

Reviewed-on: #41
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-03-21 18:26:13 +00:00
69bce58345
Improve exception handling of Zebra calls 2024-03-21 12:52:45 -05:00
adc7150b81
Merge branch 'dev040' into rav001 2024-03-20 14:34:11 -05:00
3af235377b
Adapt response handling to Zebra (#40)
This PR allows for the Zebra behavior of not having a `result` field in the RPC response on error conditions.

Reviewed-on: #40
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-03-20 19:33:36 +00:00
517b736c9a
Allow for missing result in RPC response 2024-03-20 14:16:12 -05:00
d1eaf1de4d
Merge branch 'dev040' into rav001 2024-03-20 11:29:31 -05:00
1af152dc31
Upgrade Zebra call (#39)
Reviewed-on: #39
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-03-20 16:15:30 +00:00
e7050f03c0
Upgrade Zebra call 2024-03-20 11:13:02 -05:00
f228eff367
Merge pull request 'Improve dependency on Haskoin for downstream' (#38) from rav001 into dev040
Reviewed-on: #38
2024-03-15 17:30:15 +00:00
5a6f31573c
Merge branch 'dev040' into rav001 2024-03-15 17:29:59 +00:00
bc1ee2430a
Improve dependency on Haskoin for downstream 2024-03-15 12:26:28 -05:00
6e86f2caf0
Merge pull request 'Complete Unified Address generation' (#37) from rav001 into dev040
Reviewed-on: #37
2024-03-15 16:31:50 +00:00
d1b0436af2
Add documentation for new functions 2024-03-15 10:26:06 -05:00
d5c50d58c5
Merge branch 'rvv040' into rav001 2024-03-15 10:11:27 -05:00
c7c4d664dc
Remove duplicate import 2024-03-15 08:15:08 -05:00
0a98246855 Transparent fix 2 2024-03-14 17:47:09 -04:00
2c58d1260a Transparent address fix 2024-03-14 17:44:18 -04:00
5b08026102
Merge pull request 'Add Read instance to Phrase' (#36) from rav001 into dev040
Reviewed-on: #36
2024-03-14 18:14:37 +00:00
c93809489b
Add Read instance to Phrase 2024-03-14 13:12:18 -05:00
749472e515
Merge pull request 'Fix #34' (#35) from rav001 into dev040
Reviewed-on: #35
2024-03-14 18:04:47 +00:00
d0ba74c7a1
Fix #34 2024-03-14 13:02:02 -05:00
4963eea68b
Merge pull request 'Type safety improvements' (#33) from rav001 into dev040
Reviewed-on: #33
2024-03-14 17:42:17 +00:00
46a28128fd
Bump version 2024-03-14 12:39:31 -05:00
4cc4c37960
Improve type safety for Orchard 2024-03-14 12:35:13 -05:00
d2619123c0
Improve type safety for Sapling 2024-03-14 11:30:54 -05:00
23472ee1c4
Improve type safety for Seed and Phrase 2024-03-14 11:13:10 -05:00
8a293f4e79
Enable for internal/external Orchard receivers 2024-03-14 11:12:31 -05:00
5815c5c326 Merge with dev040 2024-03-13 15:37:23 -04:00
0dcf6d3e9a
Merge pull request 'Correct Sapling receiver generation' (#32) from rav001 into dev040
Reviewed-on: #32
2024-03-13 19:25:43 +00:00
6e1048cd4d
Adjust Transparent generators to use indices 2024-03-13 14:12:28 -05:00
c5feb10fbc
Update Sapling spending key with coin type 2024-03-13 12:50:39 -05:00
e69a26e984
Merge pull request 'Unified Address Generation tests' (#31) from rav001 into dev040
Reviewed-on: #31
2024-03-12 21:06:06 +00:00
4f20160c36
Enable Unified Address generation tests 2024-03-12 16:03:35 -05:00
5b6f5fa8e2
Upgrade HexString 2024-03-12 16:02:35 -05:00
1d59e8f5eb After merging with dev040 2024-03-12 15:57:43 -04:00
5db66dcf39
Adjust indexing of Sapling payment addresses 2024-03-12 12:22:05 -05:00
8d437105c9
Merge pull request 'Implement test for sequential address generation' (#30) from rav001 into dev040
Reviewed-on: #30
2024-03-11 21:18:03 +00:00
30c3827cab
Merge branch 'dev040' into rav001 2024-03-11 16:09:11 -05:00
ddaabcc5cd
Add test to check that Sapling receivers are unique 2024-03-11 15:48:27 -05:00
8baf27e988
Merge pull request 'Implement Sapling child key derivation' (#29) from rav001 into dev040
Reviewed-on: #29
2024-03-11 20:25:54 +00:00
89730a8219
Merge branch 'dev040' into rav001 2024-03-11 20:25:01 +00:00
db92dd7de7
Implement Sapling child key derivation 2024-03-11 15:23:29 -05:00
46efe9cd21 Commit first version of genSaplingInternalAddress function 2024-03-10 22:19:51 -04:00
4edd789a40
Merge pull request 'Add signature for internal Sapling address' (#28) from rav001 into dev040
Reviewed-on: #28
2024-03-10 15:21:48 +00:00
4443ffeec5
Add signature for internal Sapling address 2024-03-10 10:20:10 -05:00
9cfed00380
Merge pull request 'Sapling Spending keys and receivers' (#27) from rvv040 into dev040
Reviewed-on: #27
2024-03-10 15:07:09 +00:00
1e795dbcea
Merge remote-tracking branch 'forge/rvv040' into rvv040 2024-03-10 10:01:19 -05:00
1f58f111a6
Merge branch 'rav002' into rvv040 2024-03-10 09:44:27 -05:00
7538bbfa19
Revision of Sapling receiver logic 2024-03-10 07:47:26 -05:00
477817f37f Find_Address method implemented 2024-03-09 10:28:36 -05:00
a549c8be9a Merge branch 'dev040' into rvv040 2024-03-08 15:32:36 -05:00
54b1567011 Default Paymebt address and DiversifierIndex created
x
2024-03-08 14:46:41 -05:00
e25d759b5e
Merge pull request 'Allow for optional shielded receivers' (#25) from rav001 into dev040
Reviewed-on: #25
2024-03-08 19:45:02 +00:00
ff89bbdac6
Merge branch 'dev040' into rav001 2024-03-08 19:44:43 +00:00
d3cf05d00e
Update version 2024-03-08 13:42:18 -06:00
9c4e26c9f2
Implement initial changes for ZIP-320 Rev1 2024-03-08 13:35:37 -06:00
5c4b715a24
Merge pull request 'Update UnifiedAddress to use named types for receivers' (#24) from rav001 into dev040
Reviewed-on: #24
2024-03-08 19:12:19 +00:00
6e31d83963
Update UnifiedAddress to use named types for receivers 2024-03-08 13:09:13 -06:00
db5a694e7d
Merge pull request 'Implement QuickCheck tests for Orchard components' (#23) from rav001 into dev040
Reviewed-on: #23
2024-03-08 18:47:01 +00:00
6c2dfa02fa
Implement QuickCheck tests for Orchard components 2024-03-08 12:44:10 -06:00
47e3cf71cb
Merge pull request 'Implement generation of Orchard receiver' (#22) from rav001 into dev040
Reviewed-on: #22
2024-03-07 22:11:59 +00:00
b2f56941a4
Bump version 2024-03-07 16:07:50 -06:00
72e87577a7
Add generation of Orchard receivers 2024-03-07 16:06:33 -06:00
f1174751fc
Add new types for Spending Keys and Receivers 2024-03-07 16:05:59 -06:00
e371fcdb72
Merge pull request 'Add constants to library' (#21) from rav001 into dev040
Reviewed-on: #21
2024-03-06 21:37:04 +00:00
977f4e791d
Implement Unified HRP constants 2024-03-06 15:35:03 -06:00
b568ee5ff4
Pin dependencies 2024-03-06 15:02:48 -06:00
d118a83993
Add constants to library 2024-03-06 13:05:00 -06:00
0e6f7503d4 Sapling Extended Spemding Key generation from HDSedd (64 byte array) 2024-03-05 22:10:05 -05:00
dbccb23b45 Merge branch 'dev040' into rvv040
-- Include orchard Spending Key and new types for working with
	-- zcash network and keys
2024-03-05 17:16:27 -05:00
7b65d322e6 Commit 003 - before merging with dev040 - with orchard spending key 2024-03-05 17:01:17 -05:00
815b4f16b4
Merge pull request 'Add Orchard spending key functionality' (#20) from rav001 into dev040
Reviewed-on: #20
2024-03-05 21:13:14 +00:00
c13d321da5
Bump version 2024-03-05 15:09:57 -06:00
3822e9f2ff
Add tests for Orchard spending key 2024-03-05 15:09:35 -06:00
53716685a8
Add new types for constants for Zcash protocol 2024-03-05 14:44:19 -06:00
694ab8fd17
Add new Orchard spending key generation 2024-03-05 14:44:00 -06:00
f09c3c500b
Update versions of Zcash crates 2024-03-05 14:43:33 -06:00
9acbe5b98c
Fix compilation for Spending Key 2024-03-05 07:36:45 -06:00
a6c358cd5d Commit for debuggin process
Erros with Parameters for rustWrapperSaplingSpendingkey function
   x in Sapling.hs module
2024-03-04 18:46:39 -05:00
c1507f36e0
Merge pull request 'Implement UA encoding' (#18) from rav001 into dev040
Reviewed-on: #18
2024-03-04 18:01:38 +00:00
0b1d9e0920
Implement UA encoding per ZIP-316 2024-03-04 11:59:07 -06:00
53a5b62b6b
Correct casing on Rust code 2024-03-04 11:58:04 -06:00
2ae18463fc
Increase verbosity of Rust compile 2024-03-03 15:29:50 -06:00
97b338bddc Commit for revision... 2024-03-03 16:19:06 -05:00
3cc0e96c44
Rust updates for bech32 encode/decode 2024-03-03 08:23:48 -06:00
9a5ad756b8
Add Read implementation to UnifiedAddress 2024-03-02 07:53:48 -06:00
a52d2231f1
Merge pull request 'Correct type of blockheight' (#17) from rav001 into dev040
Reviewed-on: #17
2024-02-28 17:20:35 +00:00
cb44b6d4e0
Correct type for blockheight 2024-02-27 19:06:58 -06:00
67d6e85d05
Fix typo in cabal file 2024-02-26 08:57:12 -06:00
419f041ca9
Merge pull request 'Correct cabal versioning' (#16) from rav001 into dev040
Reviewed-on: #16
2024-02-26 14:51:12 +00:00
2ada9aac41
Correct cabal versioning 2024-02-26 08:49:47 -06:00
d0d9a1837b
Merge pull request 'Update .gitignore' (#15) from rav001 into dev040
Reviewed-on: #15
2024-02-25 23:53:17 +00:00
050c36e76d
Update .gitignore 2024-02-25 17:50:47 -06:00
ef93147f22
Merge pull request 'Update the installation settings for use by other packages' (#14) from rav001 into dev040
Reviewed-on: #14
2024-02-25 22:17:51 +00:00
105e0afc70
Merge branch 'dev040' into rav001 2024-02-25 22:17:03 +00:00
6e11ae7779
Update install instructions 2024-02-25 16:14:04 -06:00
6b870d91f8
Correct needed libraries 2024-02-25 16:11:10 -06:00
26b21e882b
Remove Makefile 2024-02-25 16:10:52 -06:00
c28addbfc3
Merge pull request 'Update installation package' (#13) from rav001 into dev040
Reviewed-on: #13
2024-02-25 21:36:30 +00:00
9713203e2b
Update installation package 2024-02-25 15:22:53 -06:00
75cc947dda
Merge pull request 'Fine tune the cabal configuration' (#12) from rav001 into dev040
Reviewed-on: #12
2024-02-23 01:29:24 +00:00
a3d6da12d7
Merge branch 'dev040' into rav001 2024-02-23 01:29:03 +00:00
ee7e32feb6
Remove flags 2024-02-21 20:32:36 -06:00
f818586b33
Set the Rust tool-chain 2024-02-21 19:33:28 -06:00
9d7a88be43
Add shared flag for cabal 2024-02-21 15:44:03 -06:00
3fa771f9f5
Merge pull request 'Add static flag for cabal' (#11) from rav001 into dev040
Reviewed-on: #11
2024-02-21 21:21:01 +00:00
faaa84caa6
Add static flag for cabal 2024-02-21 15:19:23 -06:00
4885d7525f
Merge pull request 'Implement custom build with cabal' (#10) from package into dev040
Reviewed-on: #10
2024-02-21 19:06:58 +00:00
c0e8fa85ce
Implement custom build with cabal 2024-02-21 10:51:03 -06:00
5c4786f1d5
Add tests for Seed and Phrase generation 2024-02-20 07:35:05 -06:00
f39838f24b
Pin the Rust nightly to avoid ahash error 2024-02-19 08:29:39 -06:00
73d8125b83
Merge pull request 'Add host parameter for Zebra nodes' (#9) from fix080 into dev040
Reviewed-on: #9
2024-02-14 14:22:16 +00:00
f95ebc7b00
Add host parameter for Zebra nodes 2024-02-14 08:19:34 -06:00
09cee9a064
Merge pull request 'Implement Zebra RPC calling functions' (#8) from fix080 into dev040
Reviewed-on: #8
2024-02-12 18:01:06 +00:00
75c95b03dc
Implement Zebra RPC calling functions 2024-02-12 11:55:42 -06:00
c4f345b1de
Merge pull request 'Implement Read instance' (#7) from fix080 into dev040
Reviewed-on: #7
2024-02-09 19:35:35 +00:00
49438bc99b
Merge branch 'dev040' into fix080 2024-02-09 19:35:12 +00:00
790ffec717
Update change log 2024-02-09 13:28:15 -06:00
88a5189eba
Add Read implementations 2024-02-09 13:24:34 -06:00
c91e23ab9c
Add function documentation 2024-02-09 13:23:58 -06:00
6ea8698ccb
Merge pull request 'Implement HexString' (#4) from fix080 into dev040
Reviewed-on: #4
2024-02-06 19:33:02 +00:00
c001fb5343
Implement HexString 2024-02-06 13:10:06 -06:00
d5728525ce
Update docs 2024-01-18 13:21:37 -06:00
beebabcf49
Update license to MIT 2024-01-18 12:55:23 -06:00
11a951f62a Add a new line character at the start of the script 2024-01-17 18:10:53 -05:00
0858b805d0
Update version 2024-01-17 07:45:27 -06:00
ca8010145b
Add functions to generate seed phrases and seeds 2024-01-16 16:15:05 -06:00
72e3700aa6
Merge pull request 'Encode transparent addresses' (#3) from dev021 into master
Reviewed-on: #3
2024-01-12 15:54:07 +00:00
dce171d830
Encode transparent addresses 2024-01-12 09:46:26 -06:00
abd605e6eb
Licensing update 2023-12-20 14:03:42 -06:00
27 changed files with 6207 additions and 897 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
*~ *~
librustzcash-wrapper/target/ librustzcash-wrapper/target/
stack.yaml.lock stack.yaml.lock
dist-newstyle/

View file

@ -5,7 +5,307 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [0.7.5.0]
### Added
- Sapling commitment node functions
- Sapling Merkle path test
### Changed
- Upgraded Rust dependencies to latest versions:
- `zcash_primitives` 0.19.0
- `zcash_client_backend` 0.14.0
- `orchard` 0.10.0
- `sapling-crypto` 0.3.0
- `incrementalmerkletree` 0.7.0
- `zip32` 0.1.2
## [0.7.4.0]
### Added
- `MerklePath`
## [0.7.3.0]
### Added
- Function to create an Orchard hash from a note commitment
- Function to hash Orchard commitments
### Changed
- Modified frontiers to use `HexString` for ommers
- Optimized `createTransaction`
## [0.7.2.0]
### Changed
- Modified Sapling commitment trees to use Frontier
## [0.7.1.1]
### Added
- `ToJSON` instance for `BlockResponse`
### Changed
- Updated libraries:
- conduit
- data-fix
- happy
- happy-lib
- http-conduit
- iproute
- mono-traversable
- network
- secp256k1-haskell
- strict
- typed-process
## [0.7.1.0]
### Added
- Type `OrchardFrontier`
### Changed
- Modified Orchard commitment trees functions to use Frontier
## [0.7.0.2]
### Changed
- Modified witness update functions to skip the process if no commitments are present
## [0.7.0.1]
### Added
- New error type `PrivacyPolicyError`
## [0.7.0.0]
- Implement `wagyu-zcash-parameters` in Rust bindings
## [0.6.2.3]
### Fixed
- Decoding of unified addresses with no transparent receivers
## [0.6.2.2]
- Added JSON instances for `ZcashNet`
- Added JSON instances for `Transaction`
- Added `ValidAddress`
## [0.6.2.1]
### Changed
- Modified the parsing of raw transactions for shielded outputs
- Upgraded to GHC 9.6.5
## [0.6.2.0]
### Changed
- Performance enhancement for transaction creation
## [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
- Type to represent a transparent address/receiver
### Changed
- Full decoding of Unified Address
## [0.2.0]
### Added ### Added

190
LICENSE
View file

@ -1,178 +1,22 @@
Copyright (c) 2023 Vergara Technologies LLC MIT License
======================================================= Copyright (c) 2022-2024 Vergara Technologies LLC
Bootstrap Open Source Licence ("BOSL") v. 1.0
=======================================================
This Bootstrap Open Source Licence (the "License") applies to any original work
of authorship (the "Original Work") whose owner (the "Licensor") has placed the
following licensing notice adjacent to the copyright notice for the Original
Work:
*Licensed under the Bootstrap Open Source Licence version 1.0* 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:
1. **Grant of Copyright License.** Licensor grants You a worldwide, The above copyright notice and this permission notice shall be included in all
royalty-free, non-exclusive, sublicensable license, for the duration of the copies or substantial portions of the Software.
copyright in the Original Work, to do the following:
a. to reproduce the Original Work in copies, either alone or as part of THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
a collective work; 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.
b. to translate, adapt, alter, transform, modify, or arrange the
Original Work, thereby creating derivative works ("Derivative Works")
based upon the Original Work;
c. to distribute or communicate copies of the Original Work and
Derivative Works to the public, provided that prior to any such
distribution or communication You first place a machine-readable copy
of the Source Code of the Original Work and such Derivative Works that
You intend to distribute or communicate in an information repository
reasonably calculated to permit inexpensive and convenient access
thereto by the public (“Information Repository”) for as long as You
continue to distribute or communicate said copies, accompanied by an
irrevocable offer to license said copies to the public free of charge
under this License, said offer valid starting no later than 12 months
after You first distribute or communicate said copies;
d. to perform the Original Work publicly; and
e. to display the Original Work publicly.
2. **Grant of Patent License.** Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, under patent claims owned or controlled
by the Licensor that are embodied in the Original Work as furnished by the
Licensor, for the duration of the patents, to make, use, sell, offer for sale,
have made, and import the Original Work and Derivative Works.
3. **Grant of Source Code License.** The "Source Code" for a work means the
preferred form of the work for making modifications to it and all available
documentation describing how to modify the work. Licensor agrees to provide a
machine-readable copy of the Source Code of the Original Work along with each
copy of the Original Work that Licensor distributes. Licensor reserves the
right to satisfy this obligation by placing a machine-readable copy of said
Source Code in an Information Repository for as long as Licensor continues to
distribute the Original Work.
4. **Exclusions From License Grant.** Neither the names of Licensor, nor the
names of any contributors to the Original Work, nor any of their trademarks or
service marks, may be used to endorse or promote products derived from this
Original Work without express prior permission of the Licensor. Except as
expressly stated herein, nothing in this License grants any license to
Licensor's trademarks, copyrights, patents, trade secrets or any other
intellectual property. No patent license is granted to make, use, sell, offer
for sale, have made, or import embodiments of any patent claims other than the
licensed claims defined in Section 2. No license is granted to the trademarks
of Licensor even if such marks are included in the Original Work. Nothing in
this License shall be interpreted to prohibit Licensor from licensing under
terms different from this License any Original Work that Licensor otherwise
would have a right to license.
5. **External Deployment.** The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in any
way such that the Original Work or Derivative Works may be used by anyone other
than You, whether those works are distributed or communicated to those persons
or made available as an application intended for use over a network. As an
express condition for the grants of license hereunder, You must treat any
External Deployment by You of the Original Work or a Derivative Work as a
distribution under section 1(c).
6. **Attribution Rights.** You must retain, in the Source Code of any
Derivative Works that You create, all copyright, patent, or trademark notices
from the Source Code of the Original Work, as well as any notices of licensing
and any descriptive text identified therein as an "Attribution Notice." You
must cause the Source Code for any Derivative Works that You create to carry a
prominent Attribution Notice reasonably calculated to inform recipients that
You have modified the Original Work.
7. **Warranty of Provenance and Disclaimer of Warranty.** Licensor warrants
that the copyright in and to the Original Work and the patent rights granted
herein by Licensor are owned by the Licensor or are sublicensed to You under
the terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an "AS
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
limitation, the warranties of non-infringement, merchantability or fitness for
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS
WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
License. No license to the Original Work is granted by this License except
under this disclaimer.
8. **Limitation of Liability.** Under no circumstances and under no legal
theory, whether in tort (including negligence), contract, or otherwise, shall
the Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss of
goodwill, work stoppage, computer failure or malfunction, or any and all other
commercial damages or losses. This limitation of liability shall not apply to
the extent applicable law prohibits such limitation.
9. **Acceptance and Termination.** If, at any time, You expressly assented to
this License, that assent indicates your clear and irrevocable acceptance of
this License and all of its terms and conditions. If You distribute or
communicate copies of the Original Work or a Derivative Work, You must make a
reasonable effort under the circumstances to obtain the express assent of
recipients to the terms of this License. This License conditions your rights to
undertake the activities listed in Section 1, including your right to create
Derivative Works based upon the Original Work, and doing so without honoring
these terms and conditions is prohibited by copyright law and international
treaty. Nothing in this License is intended to affect copyright exceptions and
limitations (including 'fair use' or 'fair dealing'). This License shall
terminate immediately and You may no longer exercise any of the rights granted
to You by this License upon your failure to honor the conditions in Section
1(c).
10. **Termination for Patent Action.** This License shall terminate
automatically and You may no longer exercise any of the rights granted to You
by this License as of the date You commence an action, including a cross-claim
or counterclaim, against Licensor or any licensee alleging that the Original
Work infringes a patent. This termination provision shall not apply for an
action alleging patent infringement by combinations of the Original Work with
other software or hardware.
11. **Jurisdiction, Venue and Governing Law.** Any action or suit relating to
this License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and under
the laws of that jurisdiction excluding its conflict-of-law provisions. The
application of the United Nations Convention on Contracts for the International
Sale of Goods is expressly excluded. Any use of the Original Work outside the
scope of this License or after its termination shall be subject to the
requirements and penalties of copyright or patent law in the appropriate
jurisdiction. This section shall survive the termination of this License.
12. **Attorneys' Fees.** In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of this
License.
13. **Miscellaneous.** If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary to
make it enforceable.
14. **Definition of "You" in This License.** "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this License.
For legal entities, "You" includes any entity that controls, is controlled by,
or is under common control with you. For purposes of this definition, "control"
means (i) the power, direct or indirect, to cause the direction or management
of such entity, whether by contract or otherwise, or (ii) ownership of fifty
percent (50%) or more of the outstanding shares, or (iii) beneficial ownership
of such entity.
15. **Right to Use.** You may use the Original Work in all ways not otherwise
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.
16. **Modification of This License.** This License is Copyright © 2007 Zooko
Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate this
License without modification. Nothing in this License permits You to modify
this License as applied to the Original Work or to Derivative Works. However,
You may modify the text of this License and copy, distribute or communicate
your modified version (the "Modified License") and apply it to other original
works of authorship subject to the following conditions: (i) You may not
indicate in any way that your Modified License is the "Bootstrap Open Source
Licence" or "BOSL" and you may not use those names in the name of your Modified
License; and (ii) You must replace the notice specified in the first paragraph
above with the notice "Licensed under <insert your license name here>" or with
a notice of your own that is not confusingly similar to the notice in this
License.

View file

@ -1,14 +0,0 @@
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

View file

@ -17,7 +17,10 @@ A Haskell library to interact with the Zcash blockchain.
``` ```
./configure ./configure
``` ```
- Compile the Rust and Haskell portions: `make` - Compile the Rust and Haskell portions:
```
cabal build
```
## Use ## Use

137
Setup.hs Normal file
View file

@ -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

13
cabal.project Normal file
View file

@ -0,0 +1,13 @@
packages: ./*.cabal
with-compiler: ghc-9.6.5
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

208
cabal.project.freeze Normal file
View file

@ -0,0 +1,208 @@
active-repositories: hackage.haskell.org:merge
constraints: any.Cabal ==3.10.3.0,
any.Cabal-syntax ==3.10.3.0,
any.HUnit ==1.6.2.0,
any.OneTuple ==0.4.2,
any.QuickCheck ==2.14.3,
QuickCheck -old-random +templatehaskell,
any.StateVar ==1.2.2,
any.aeson ==2.2.3.0,
aeson +ordered-keymap,
any.alex ==3.5.1.0,
any.ansi-terminal ==1.1.1,
ansi-terminal -example,
any.ansi-terminal-types ==1.1,
any.appar ==0.1.8,
any.array ==0.5.6.0,
any.asn1-encoding ==0.9.6,
any.asn1-parse ==0.9.5,
any.asn1-types ==0.3.4,
any.assoc ==1.1.1,
assoc -tagged,
any.async ==2.2.5,
async -bench,
any.attoparsec ==0.14.4,
attoparsec -developer,
any.attoparsec-aeson ==2.2.2.0,
any.base ==4.18.2.1,
any.base-orphans ==0.9.2,
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.2,
bifunctors +tagged,
any.binary ==0.8.9.1,
any.binary-orphans ==1.0.5,
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.character-ps ==0.1,
any.colour ==2.3.6,
any.comonad ==5.0.8,
comonad +containers +distributive +indexed-traversable,
any.conduit ==1.3.6,
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 ==1.0.0,
crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
any.crypton-connection ==0.4.1,
any.crypton-x509 ==1.7.7,
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.4,
any.deepseq ==1.4.8.1,
any.directory ==1.3.8.4,
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.3.0,
any.exceptions ==0.10.7,
any.filepath ==1.4.300.1,
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.6.5,
any.ghc-prim ==0.10.0,
any.half ==0.3.1,
any.happy ==2.0.2,
any.happy-lib ==2.0.2,
any.hashable ==1.4.7.0,
hashable -arch-native +integer-gmp -random-initial-seed,
any.haskell-lexer ==1.1.1,
any.haskoin-core ==1.1.0,
any.hexstring ==0.12.1.0,
any.hourglass ==0.2.12,
any.hsc2hs ==0.68.10,
hsc2hs -in-ghc-tree,
any.hspec ==2.11.9,
any.hspec-core ==2.11.9,
any.hspec-discover ==2.11.9,
any.hspec-expectations ==0.8.4,
any.http-client ==0.7.17,
http-client +network-uri,
any.http-client-tls ==0.3.6.3,
any.http-conduit ==2.3.9,
http-conduit +aeson,
any.http-types ==0.12.4,
any.indexed-traversable ==0.1.4,
any.indexed-traversable-instances ==0.1.2,
any.integer-conversion ==0.1.1,
any.integer-gmp ==1.1,
any.integer-logarithms ==1.0.3.1,
integer-logarithms -check-bounds +integer-gmp,
any.iproute ==1.7.14,
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.20.0,
any.mtl ==2.3.1,
any.murmur3 ==1.0.5,
any.network ==3.2.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.os-string ==2.0.6,
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.19.0,
any.quickcheck-io ==0.2.0,
any.quickcheck-transformer ==0.3.1.2,
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.8.0,
scientific -integer-simple,
any.secp256k1-haskell ==1.4.0,
any.semialign ==1.3.1,
semialign +semigroupoids,
any.semigroupoids ==6.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.1,
any.string-conversions ==0.4.0.1,
any.tagged ==0.8.8,
tagged +deepseq +transformers,
any.template-haskell ==2.20.0.0,
any.text ==2.0.2,
any.text-iso8601 ==0.1.1,
any.text-short ==0.1.6,
text-short -asserts,
any.tf-random ==0.5,
any.th-abstraction ==0.7.0.0,
any.th-compat ==0.1.5,
any.these ==1.2.1,
any.time ==1.12.2,
any.time-compat ==1.9.7,
any.tls ==2.1.0,
tls -devel,
any.transformers ==0.6.1.0,
any.transformers-compat ==0.7.2,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.typed-process ==0.2.12.0,
any.unix ==2.8.4.0,
any.unix-time ==0.4.15,
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.6,
any.vector ==0.13.1.0,
vector +boundschecks -internalchecks -unsafechecks -wall,
any.vector-algorithms ==0.9.0.2,
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.5,
any.zlib ==0.7.1.0,
zlib -bundled-c-zlib +non-blocking-ffi +pkg-config
index-state: hackage.haskell.org 2024-10-11T12:55:31Z

2
configure vendored
View file

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
echo -e "\n"
echo "export PKG_CONFIG_PATH=$(pwd)/librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug:\$PKG_CONFIG_PATH" | tee -a ~/.bashrc echo "export PKG_CONFIG_PATH=$(pwd)/librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug:\$PKG_CONFIG_PATH" | tee -a ~/.bashrc
echo "export LD_LIBRARY_PATH=$(pwd)/librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug:\$LD_LIBRARY_PATH" | tee -a ~/.bashrc echo "export LD_LIBRARY_PATH=$(pwd)/librustzcash-wrapper/target/x86_64-unknown-linux-gnu/debug:\$LD_LIBRARY_PATH" | tee -a ~/.bashrc
source ~/.bashrc source ~/.bashrc

File diff suppressed because it is too large Load diff

View file

@ -4,19 +4,28 @@ version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
haskell-ffi.git = "https://github.com/BeFunctional/haskell-rust-ffi.git" haskell-ffi.git = "https://github.com/BeFunctional/haskell-rust-ffi.git"
haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274" haskell-ffi.rev = "2bf292e2e56eac8e9fb0fb2e1450cf4a4bd01274"
f4jumble = "0.1" f4jumble = "0.1"
zcash_address = "0.2.0" zcash_address = "0.2.0"
borsh = "0.10" borsh = "0.10"
bech32 = "0.9.1" bech32 = "0.11"
orchard = "0.4.0" orchard = "0.10.0"
zcash_note_encryption = "0.3.0" zcash_note_encryption = "0.4.0"
zcash_primitives = "0.11.0" zcash_primitives = { version = "0.19.0", features = ["transparent-inputs"]}
zcash_client_backend = "0.9.0" zcash_client_backend = "0.14.0"
sapling-crypto = "0.3"
zip32 = "0.1.2"
proc-macro2 = "1.0.66" proc-macro2 = "1.0.66"
nonempty = "0.7.0"
incrementalmerkletree = "0.7.0"
secp256k1 = "0.27.0"
jubjub = "0.10.0"
rand_core = { version = "0.6.4", features = ["getrandom"]}
wagyu-zcash-parameters = "0.2.0"
bip0039 = "0.12.0"
[features] [features]
capi = [] capi = []

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,52 +0,0 @@
name: zcash-haskell
version: 0.2.0
git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell"
license: BOSL
author: "Rene Vergara"
maintainer: "rene@vergara.network"
copyright: "(c)2023 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 <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme>
dependencies:
- base >= 4.7 && < 5
library:
source-dirs: src
dependencies:
- bytestring
- borsh >= 0.2
- text
- foreign-rust
- generics-sop
- aeson
- http-conduit
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

View file

@ -1,3 +1,9 @@
{- Copyright 2022-2024 Vergara Technologies LLC
This file is part of Zcash-Haskell.
-}
{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveAnyClass #-}
@ -11,10 +17,11 @@ module C.Zcash where
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Codec.Borsh import Codec.Borsh
import Data.Text (Text) import qualified Data.Text as T
import Data.Word import Data.Word
import Data.Int import Data.Int
import Data.Structured import Data.Structured
import Data.HexString (HexString(..))
import Foreign.C.Types import Foreign.C.Types
import Foreign.Rust.Marshall.External import Foreign.Rust.Marshall.External
import Foreign.Rust.Marshall.Fixed import Foreign.Rust.Marshall.Fixed
@ -25,6 +32,20 @@ import qualified Generics.SOP as SOP
import qualified GHC.Generics as GHC import qualified GHC.Generics as GHC
import ZcashHaskell.Types 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 {# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
@ -40,10 +61,11 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun pure unsafe rust_wrapper_ua_decode as rustWrapperIsUA {# fun unsafe rust_wrapper_ua_decode as rustWrapperUADecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer RawUA'&
} }
-> `Bool' -> `()'
#} #}
{# fun pure unsafe rust_wrapper_shielded_decode as rustWrapperIsShielded {# fun pure unsafe rust_wrapper_shielded_decode as rustWrapperIsShielded
@ -52,13 +74,6 @@ import ZcashHaskell.Types
-> `Bool' -> `Bool'
#} #}
{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer RawData'&
}
-> `()'
#}
{# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode {# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
} }
@ -81,12 +96,23 @@ import ZcashHaskell.Types
{# fun unsafe rust_wrapper_sapling_note_decrypt_v2 as rustWrapperSaplingNoteDecode {# fun unsafe rust_wrapper_sapling_note_decrypt_v2 as rustWrapperSaplingNoteDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'& , toBorshVar* `ShieldedOutput'&
, getVarBuffer `Buffer DecodedNote'& , 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 {# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer UnifiedFullViewingKey'& , getVarBuffer `Buffer UnifiedFullViewingKey'&
@ -102,9 +128,285 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_tx_parse as rustWrapperTxParse {# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK
{ toBorshVar* `BS.ByteString'& { 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* `SaplingFrontier'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer SaplingFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_witness as rustWrapperReadSaplingWitness
{ toBorshVar* `SaplingFrontier'&
, 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_read_sapling_frontier as rustWrapperReadSaplingFrontier
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer SaplingFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_decode_sapling_address as rustWrapperDecodeSaplingAddress
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_node as rustWrapperReadSaplingNode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_combine_sapling_nodes as rustWrapperCombineSaplingNodes
{ `Int8'
, toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_get_sapling_root as rustWrapperGetSaplingRootTest
{ `Int8'
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_commitment_tree_parts as rustWrapperReadSaplingTreeParts
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer SaplingRawTree'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_tree_anchor as rustWrapperReadSaplingTreeAnchor
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_path_anchor as rustWrapperReadSaplingPathAnchor
{ toBorshVar* `MerklePath'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_node as rustWrapperReadOrchardNode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_combine_orchard_nodes as rustWrapperCombineOrchardNodes
{ `Int8'
, toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_tree_anchor as rustWrapperReadOrchardTreeAnchor
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_witness_anchor as rustWrapperReadOrchardWitnessAnchor
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_path_anchor as rustWrapperReadOrchardPathAnchor
{ toBorshVar* `MerklePath'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_get_orchard_root as rustWrapperGetOrchardRootTest
{ `Int8'
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_commitment_tree as rustWrapperReadOrchardCommitmentTree
{ toBorshVar* `OrchardFrontier'&
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer OrchardFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_commitment_tree_parts as rustWrapperReadOrchardTreeParts
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer OrchardRawTree'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_frontier as rustWrapperReadOrchardFrontier
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer OrchardFrontier'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_orchard_witness as rustWrapperReadOrchardWitness
{ toBorshVar* `OrchardFrontier'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun pure unsafe rust_wrapper_read_orchard_position as rustWrapperReadOrchardPosition
{ toBorshVar* `BS.ByteString'&
}
-> `Word64'
#}
{# fun unsafe rust_wrapper_orchard_add_node as rustWrapperOrchardAddNodeTest
{ `Int8'
, toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_update_sapling_witness as rustWrapperUpdateSaplingWitness
{ 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]'&
, `Bool'
, `Word64'
, `Bool'
, getVarBuffer `Buffer HexString'&
} }
-> `()' -> `()'
#} #}

38
src/ZcashHaskell/Keys.hs Normal file
View file

@ -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

View file

@ -1,7 +1,13 @@
{-# LANGUAGE OverloadedStrings #-}
-- Copyright 2022-2024 Vergara Technologies LLC
--
-- This file is part of Zcash-Haskell.
--
-- | -- |
-- Module : ZcashHaskell.Orchard -- Module : ZcashHaskell.Orchard
-- Copyright : Vergara Technologies 2023 -- Copyright : 2022-2024 Vergara Technologies
-- License : BOSL -- License : MIT
-- --
-- Maintainer : rene@vergara.network -- Maintainer : rene@vergara.network
-- Stability : experimental -- Stability : experimental
@ -12,18 +18,135 @@
module ZcashHaskell.Orchard where module ZcashHaskell.Orchard where
import C.Zcash import C.Zcash
( rustWrapperIsUA ( rustWrapperCombineOrchardNodes
, rustWrapperGenOrchardReceiver
, rustWrapperGenOrchardSpendKey
, rustWrapperGetOrchardRootTest
, rustWrapperOrchardAddNodeTest
, rustWrapperOrchardCheck , rustWrapperOrchardCheck
, rustWrapperOrchardNoteDecode , rustWrapperOrchardNoteDecode
, rustWrapperOrchardNoteDecodeSK
, rustWrapperReadOrchardCommitmentTree
, rustWrapperReadOrchardFrontier
, rustWrapperReadOrchardNode
, rustWrapperReadOrchardPathAnchor
, rustWrapperReadOrchardPosition
, rustWrapperReadOrchardTreeAnchor
, rustWrapperReadOrchardTreeParts
, rustWrapperReadOrchardWitness
, rustWrapperReadOrchardWitnessAnchor
, rustWrapperUADecode
, rustWrapperUfvkDecode , rustWrapperUfvkDecode
, rustWrapperUpdateOrchardWitness
) )
import qualified Data.ByteString as BS 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 Foreign.Rust.Marshall.Variable
import ZcashHaskell.Sapling (decodeSaplingAddress)
import ZcashHaskell.Transparent
( decodeExchangeAddress
, decodeTransparentAddress
)
import ZcashHaskell.Types import ZcashHaskell.Types
import ZcashHaskell.Utils (encodeBech32, encodeBech32m, f4Jumble)
-- | 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 -- | Checks if given bytestring is a valid encoded unified address
isValidUnifiedAddress :: BS.ByteString -> Bool isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress
isValidUnifiedAddress = rustWrapperIsUA isValidUnifiedAddress str =
case raw_net decodedAddress of
0 -> Nothing
_ -> Just $ makeUA decodedAddress
where
decodedAddress = (withPureBorshVarBuffer . rustWrapperUADecode) str
whichNet =
case raw_net decodedAddress of
1 -> MainNet
2 -> TestNet
3 -> RegTestNet
makeUA x =
UnifiedAddress
whichNet
(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 BS.length (raw_t x) > 1
then Just $ TransparentReceiver P2PKH (fromRawBytes $ raw_t x)
else if BS.length (raw_to x) > 1
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 -- | Attempts to decode the given bytestring into a Unified Full Viewing Key
decodeUfvk :: BS.ByteString -> Maybe UnifiedFullViewingKey decodeUfvk :: BS.ByteString -> Maybe UnifiedFullViewingKey
@ -49,3 +172,168 @@ decryptOrchardAction key encAction =
decodedAction = decodedAction =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperOrchardNoteDecode (o_key key) encAction 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)
getOrchardFrontier :: OrchardCommitmentTree -> Maybe OrchardFrontier
getOrchardFrontier tree =
if of_pos updatedTree > 1
then Just updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadOrchardFrontier $ toBytes $ orchTree tree
getOrchardTreeAnchor :: OrchardCommitmentTree -> HexString
getOrchardTreeAnchor tree =
withPureBorshVarBuffer $
rustWrapperReadOrchardTreeAnchor $ toBytes $ orchTree tree
getOrchardWitnessAnchor :: OrchardWitness -> HexString
getOrchardWitnessAnchor wit =
withPureBorshVarBuffer $
rustWrapperReadOrchardWitnessAnchor $ toBytes $ orchWit wit
getOrchardRootTest :: Int -> HexString
getOrchardRootTest level =
withPureBorshVarBuffer $ rustWrapperGetOrchardRootTest $ fromIntegral level
addOrchardNodeGetRoot :: Int -> BS.ByteString -> HexString
addOrchardNodeGetRoot l n =
withPureBorshVarBuffer $ rustWrapperOrchardAddNodeTest (fromIntegral l) n
getOrchardTreeParts :: OrchardCommitmentTree -> Maybe OrchardTree
getOrchardTreeParts h =
if isBlank (ort_left tree) && isBlank (ort_right tree)
then Nothing
else Just $
OrchardTree
(parseHex $ ort_left tree)
(parseHex $ ort_right tree)
(map parseHex (ort_parents tree))
where
isBlank h = (BS.length $ hexBytes $ h) == 1
parseHex h =
if (BS.length $ hexBytes $ h) > 1
then Just h
else Nothing
tree =
withPureBorshVarBuffer $
rustWrapperReadOrchardTreeParts $ toBytes $ orchTree h
getOrchardPathAnchor :: HexString -> MerklePath -> HexString
getOrchardPathAnchor hex p =
withPureBorshVarBuffer $ rustWrapperReadOrchardPathAnchor p (hexBytes hex)
-- | Update a Orchard commitment tree
updateOrchardCommitmentTree ::
OrchardFrontier -- ^ the base tree
-> HexString -- ^ the new note commitment
-> Maybe OrchardFrontier
updateOrchardCommitmentTree tree cmx =
if of_pos updatedTree > 1
then Just updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadOrchardCommitmentTree tree (hexBytes cmx)
-- | Get the Orchard incremental witness from a commitment tree
getOrchardWitness :: OrchardFrontier -> Maybe OrchardWitness
getOrchardWitness tree =
if BS.length (hexBytes wit) > 1
then Just $ OrchardWitness wit
else Nothing
where
wit = withPureBorshVarBuffer $ rustWrapperReadOrchardWitness tree
-- | Get the Sapling note position from a witness
getOrchardNotePosition :: OrchardWitness -> Integer
getOrchardNotePosition =
fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit
-- | Update the witness of an Orchard note
updateOrchardWitness :: OrchardWitness -> [HexString] -> OrchardWitness
updateOrchardWitness wit cmus =
if not (null cmus)
then OrchardWitness $
withPureBorshVarBuffer $
rustWrapperUpdateOrchardWitness
(toBytes $ orchWit wit)
(map toBytes cmus)
else wit
getOrchardNodeValue :: BS.ByteString -> Maybe HexString
getOrchardNodeValue cmx =
if BS.length (hexBytes n) > 1
then Just n
else Nothing
where
n = withPureBorshVarBuffer $ rustWrapperReadOrchardNode cmx
combineOrchardNodes :: Integer -> HexString -> HexString -> Maybe HexString
combineOrchardNodes level n1 n2 =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r =
withPureBorshVarBuffer $
rustWrapperCombineOrchardNodes
(fromIntegral level)
(toBytes n1)
(toBytes n2)
-- | Parse a potential Zcash address
parseAddress :: BS.ByteString -> Maybe ValidAddress
parseAddress t =
case isValidUnifiedAddress t of
Nothing ->
case decodeSaplingAddress t of
Nothing ->
case decodeTransparentAddress t of
Nothing ->
case decodeExchangeAddress t of
Nothing -> Nothing
Just x -> Just $ Exchange x
Just t -> Just $ Transparent t
Just s -> Just $ Sapling s
Just u -> Just $ Unified u
compareAddress :: ValidAddress -> UnifiedAddress -> Bool
compareAddress a u =
case a of
Unified i -> i == u
Sapling s -> s_rec u == Just (sa_receiver s) && ua_net u == net_type s
Transparent t -> t_rec u == Just (ta_receiver t) && ua_net u == ta_network t
Exchange x -> False

View file

@ -1,32 +1,74 @@
-- Copyright 2022-2024 Vergara Technologies LLC
--
-- This file is part of Zcash-Haskell.
--
-- |
-- Module : ZcashHaskell.Sapling
-- Copyright : 2022-2024 Vergara Technologies
-- License : MIT
--
-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
-- Functions to interact with the Sapling shielded pool of the Zcash blockchain.
--
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module ZcashHaskell.Sapling where module ZcashHaskell.Sapling where
import C.Zcash import C.Zcash
( rustWrapperIsShielded ( rustWrapperCombineSaplingNodes
, rustWrapperDecodeSaplingAddress
, rustWrapperGetSaplingRootTest
, rustWrapperIsShielded
, rustWrapperReadSaplingCommitmentTree
, rustWrapperReadSaplingFrontier
, rustWrapperReadSaplingNode
, rustWrapperReadSaplingPathAnchor
, rustWrapperReadSaplingPosition
, rustWrapperReadSaplingTreeAnchor
, rustWrapperReadSaplingTreeParts
, rustWrapperReadSaplingWitness
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingDecodeEsk
, rustWrapperSaplingNoteDecode , rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress
, rustWrapperSaplingSpendingkey
, rustWrapperSaplingVkDecode , rustWrapperSaplingVkDecode
, rustWrapperTxParse , rustWrapperTxParse
, rustWrapperUpdateSaplingWitness
) )
import Data.Aeson import Data.Aeson
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Foreign.Rust.Marshall.Variable (withPureBorshVarBuffer) import qualified Data.ByteString.Char8 as C
import ZcashHaskell.Types import Data.HexString (HexString(..), fromText, hexString, toBytes, toText)
( DecodedNote(..) import Data.Int (Int8)
, RawData(..) import qualified Data.Text as T
, RawTxResponse(..) import Data.Word
, ShieldedOutput(..) import Foreign.Rust.Marshall.Variable
, decodeHexText ( 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 -- | Check if given bytesting is a valid encoded shielded address
isValidShieldedAddress :: BS.ByteString -> Bool isValidShieldedAddress :: BS.ByteString -> Bool
isValidShieldedAddress = rustWrapperIsShielded isValidShieldedAddress = rustWrapperIsShielded
getShieldedOutputs :: BS.ByteString -> [BS.ByteString] getShieldedOutputs :: HexString -> [ShieldedOutput]
getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse t 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 -- | Check if given bytestring is a valid Sapling viewing key
isValidSaplingViewingKey :: BS.ByteString -> Bool isValidSaplingViewingKey :: BS.ByteString -> Bool
@ -42,7 +84,7 @@ matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool
matchSaplingAddress = rustWrapperSaplingCheck matchSaplingAddress = rustWrapperSaplingCheck
-- | Attempt to decode the given raw tx with the given Sapling viewing key -- | 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 = decodeSaplingOutput key out =
case a_value decodedAction of case a_value decodedAction of
0 -> Nothing 0 -> Nothing
@ -60,13 +102,18 @@ instance FromJSON RawTxResponse where
ht <- obj .: "height" ht <- obj .: "height"
c <- obj .: "confirmations" c <- obj .: "confirmations"
b <- obj .: "blocktime" b <- obj .: "blocktime"
sSpend <- obj .: "vShieldedSpend"
sOut <- obj .: "vShieldedOutput"
case o of case o of
Nothing -> Nothing ->
pure $ pure $
RawTxResponse RawTxResponse
i i
(decodeHexText h) h
(getShieldedOutputs (decodeHexText h)) sSpend
(if not (null (sOut :: [Object]))
then getShieldedOutputs h
else [])
[] []
ht ht
c c
@ -76,9 +123,198 @@ instance FromJSON RawTxResponse where
pure $ pure $
RawTxResponse RawTxResponse
i i
(decodeHexText h) h
(getShieldedOutputs (decodeHexText h)) sSpend
(if not (null sOut)
then getShieldedOutputs h
else [])
a a
ht ht
c c
b 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)
getSaplingNodeValue :: BS.ByteString -> Maybe HexString
getSaplingNodeValue cmu =
if BS.length (hexBytes n) > 1
then Just n
else Nothing
where
n = withPureBorshVarBuffer $ rustWrapperReadSaplingNode cmu
combineSaplingNodes :: Int8 -> HexString -> HexString -> Maybe HexString
combineSaplingNodes level n1 n2 =
if BS.length (hexBytes r) > 1
then Just r
else Nothing
where
r =
withPureBorshVarBuffer $
rustWrapperCombineSaplingNodes level (toBytes n1) (toBytes n2)
getSaplingRootTest :: Int8 -> HexString
getSaplingRootTest level =
withPureBorshVarBuffer $ rustWrapperGetSaplingRootTest level
getSaplingTreeParts :: SaplingCommitmentTree -> Maybe SaplingTree
getSaplingTreeParts h =
if isBlank (srt_left tree) && isBlank (srt_right tree)
then Nothing
else Just $
SaplingTree
(parseHex $ srt_left tree)
(parseHex $ srt_right tree)
(map parseHex (srt_parents tree))
where
isBlank h = (BS.length $ hexBytes $ h) == 1
parseHex h =
if (BS.length $ hexBytes $ h) > 1
then Just h
else Nothing
tree =
withPureBorshVarBuffer $
rustWrapperReadSaplingTreeParts $ toBytes $ sapTree h
getSaplingTreeAnchor :: SaplingCommitmentTree -> HexString
getSaplingTreeAnchor tree =
withPureBorshVarBuffer $
rustWrapperReadSaplingTreeAnchor $ toBytes $ sapTree tree
getSaplingPathAnchor :: HexString -> MerklePath -> HexString
getSaplingPathAnchor hex p =
withPureBorshVarBuffer $ rustWrapperReadSaplingPathAnchor p (hexBytes hex)
getSaplingFrontier :: SaplingCommitmentTree -> Maybe SaplingFrontier
getSaplingFrontier tree =
if sf_pos updatedTree > 1
then Just updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadSaplingFrontier $ toBytes $ sapTree tree
-- | Update a Sapling commitment tree
updateSaplingCommitmentTree ::
SaplingFrontier -- ^ the base tree
-> HexString -- ^ the new note commitment
-> Maybe SaplingFrontier
updateSaplingCommitmentTree tree cmu =
if sf_pos updatedTree > 1
then Just updatedTree
else Nothing
where
updatedTree =
withPureBorshVarBuffer $
rustWrapperReadSaplingCommitmentTree tree (hexBytes cmu)
-- | Get the Sapling incremental witness from a commitment tree
getSaplingWitness :: SaplingFrontier -> Maybe SaplingWitness
getSaplingWitness tree =
if BS.length (hexBytes wit) > 1
then Just $ SaplingWitness wit
else Nothing
where
wit = withPureBorshVarBuffer $ rustWrapperReadSaplingWitness tree
-- | Get the Sapling note position from a witness
getSaplingNotePosition :: SaplingWitness -> Integer
getSaplingNotePosition =
fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit
updateSaplingWitness :: SaplingWitness -> [HexString] -> SaplingWitness
updateSaplingWitness wit cmus =
if not (null cmus)
then SaplingWitness $
withPureBorshVarBuffer $
rustWrapperUpdateSaplingWitness
(toBytes $ sapWit wit)
(map toBytes cmus)
else wit
-- | 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

View file

@ -0,0 +1,214 @@
{-# LANGUAGE OverloadedStrings #-}
-- Copyright 2022-2024 Vergara Technologies LLC
--
-- This file is part of Zcash-Haskell.
--
-- |
-- Module : ZcashHaskell.Transparent
-- Copyright : 2022-2024 Vergara Technologies
-- License : MIT
--
-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
-- Functions to interact with the transparent addresses in the Zcash blockchain
--
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 "tex" (toBytes (tr_bytes tr))
Just vhash
TestNet -> do
let vhash = encodeBech32m "textest" (toBytes (tr_bytes tr))
Just vhash
_any -> Nothing
-- | Decode an Exchange Address into a ExchangeAddress
decodeExchangeAddress :: BS.ByteString -> Maybe ExchangeAddress
decodeExchangeAddress ex = do
if BS.length ex > 1
then do
let rawd = decodeBech32 ex
let tMain = "tex"
let tTest = "textest"
let tFail = "fail"
let hr = 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

View file

@ -1,51 +1,222 @@
{-# LANGUAGE DeriveGeneric #-} -- Copyright 2022-2024 Vergara Technologies LLC
{-# LANGUAGE DuplicateRecordFields #-} --
{-# LANGUAGE DeriveAnyClass #-} -- This file is part of Zcash-Haskell.
{-# LANGUAGE DerivingVia #-} --
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverloadedStrings #-}
-- | -- |
-- Module : ZcashHaskell.Types -- Module : ZcashHaskell.Types
-- Copyright : Vergara Technologies 2023 -- Copyright : 2022-2024 Vergara Technologies
-- License : BOSL -- License : MIT
-- --
-- Maintainer : rene@vergara.network -- Maintainer : pitmutt@vergara.tech
-- Stability : experimental -- Stability : experimental
-- Portability : unknown -- Portability : unknown
-- --
-- The types used by the ZcashHaskell library to interact with the Zcash blockchain -- 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 module ZcashHaskell.Types where
import Codec.Borsh import Codec.Borsh
import Crypto.Hash
import Data.Aeson import Data.Aeson
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy.UTF8 as US
import qualified Data.ByteString.Short as BS (ShortByteString, toShort)
import Data.HexString
import Data.Int import Data.Int
import Data.Maybe (fromJust, fromMaybe)
import Data.Structured import Data.Structured
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified Data.Vector as V
import Data.Word import Data.Word
import qualified GHC.Generics as GHC import qualified GHC.Generics as GHC
import qualified Generics.SOP as SOP import qualified Generics.SOP as SOP
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 -- * 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 -- | Type to represent data after Bech32 decoding
data RawData = RawData data RawData = RawData
{ hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding { hrp :: !BS.ByteString -- ^ Human-readable part of the Bech32 encoding
, bytes :: BS.ByteString -- ^ Decoded bytes , bytes :: !BS.ByteString -- ^ Decoded bytes
} deriving stock (Prelude.Show, GHC.Generic) } deriving stock (Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawData 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, GHC.Generic, ToJSON, FromJSON)
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)
instance ToJSON Transaction where
toJSON (Transaction t h c e tb sb ob) =
object
[ "txid" .= t
, "height" .= h
, "confirmations" .= c
, "expiry" .= e
, "transparent" .= tb
, "sapling" .= sb
, "orchard" .= ob
]
-- | The transparent portion of a Zcash transaction
data TransparentBundle = TransparentBundle
{ tb_vin :: ![H.TxIn]
, tb_vout :: ![H.TxOut]
, tb_coinbase :: !Bool
} deriving (Eq, Prelude.Show, Read)
instance ToJSON TransparentBundle where
toJSON (TransparentBundle vin vout c) =
object ["vin" .= vin, "vout" .= vout, "coinbase" .= c]
-- | Read a raw transparent bundle into the Haskell type
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 -- | A type to model Zcash RPC calls
data RpcCall = RpcCall data RpcCall = RpcCall
{ jsonrpc :: T.Text { jsonrpc :: !T.Text
, callId :: T.Text , callId :: !T.Text
, method :: T.Text , method :: !T.Text
, parameters :: [Data.Aeson.Value] , parameters :: ![Data.Aeson.Value]
} deriving stock (Prelude.Show, GHC.Generic) } deriving stock (Prelude.Show, GHC.Generic)
instance ToJSON RpcCall where instance ToJSON RpcCall where
@ -54,24 +225,24 @@ instance ToJSON RpcCall where
-- | A type to model the response of the Zcash RPC -- | A type to model the response of the Zcash RPC
data RpcResponse r = MakeRpcResponse data RpcResponse r = MakeRpcResponse
{ err :: Maybe RpcError { err :: !(Maybe RpcError)
, respId :: T.Text , respId :: !T.Text
, result :: Maybe r , result :: !(Maybe r)
} deriving stock (Prelude.Show, GHC.Generic) } deriving stock (Prelude.Show, GHC.Generic)
deriving anyclass (ToJSON) deriving anyclass (ToJSON)
instance (FromJSON r) => FromJSON (RpcResponse r) where instance (FromJSON r) => FromJSON (RpcResponse r) where
parseJSON = parseJSON =
withObject "RpcResponse" $ \obj -> do withObject "RpcResponse" $ \obj -> do
e <- obj .: "error" e <- obj .:? "error"
i <- obj .: "id" i <- obj .: "id"
r <- obj .: "result" r <- obj .:? "result"
pure $ MakeRpcResponse e i r pure $ MakeRpcResponse e i r
-- | A type to model the errors from the Zcash RPC -- | A type to model the errors from the Zcash RPC
data RpcError = RpcError data RpcError = RpcError
{ ecode :: Double { ecode :: !Double
, emessage :: T.Text , emessage :: !T.Text
} deriving stock (Prelude.Show, GHC.Generic) } deriving stock (Prelude.Show, GHC.Generic)
deriving anyclass (ToJSON) deriving anyclass (ToJSON)
@ -82,12 +253,14 @@ instance FromJSON RpcError where
m <- obj .: "message" m <- obj .: "message"
pure $ RpcError c m pure $ RpcError c m
-- ** `zcashd`
-- | Type to represent response from the `zcashd` RPC `getblock` method -- | Type to represent response from the `zcashd` RPC `getblock` method
data BlockResponse = BlockResponse data BlockResponse = BlockResponse
{ bl_confirmations :: Integer -- ^ Block confirmations { bl_hash :: !HexString
, bl_height :: Integer -- ^ Block height , bl_confirmations :: !Integer -- ^ Block confirmations
, bl_time :: Integer -- ^ Block time , bl_height :: !Integer -- ^ Block height
, bl_txs :: [T.Text] -- ^ List of transaction IDs in the block , bl_time :: !Integer -- ^ Block time
, bl_txs :: ![HexString] -- ^ List of transaction IDs in the block
} deriving (Prelude.Show, Eq) } deriving (Prelude.Show, Eq)
instance FromJSON BlockResponse where instance FromJSON BlockResponse where
@ -95,35 +268,333 @@ instance FromJSON BlockResponse where
withObject "BlockResponse" $ \obj -> do withObject "BlockResponse" $ \obj -> do
c <- obj .: "confirmations" c <- obj .: "confirmations"
h <- obj .: "height" h <- obj .: "height"
t <- obj .: "time" t <- obj .:? "time"
txs <- obj .: "tx" txs <- obj .: "tx"
pure $ BlockResponse c h t txs hash <- obj .: "hash"
pure $ BlockResponse hash c h (fromMaybe 0 t) txs
instance ToJSON BlockResponse where
toJSON (BlockResponse h c ht t txs) =
object
[ "hash" .= h
, "confirmations" .= c
, "height" .= ht
, "time" .= t
, "tx" .= txs
]
-- | Type to represent response from the `zcashd` RPC `getrawtransaction` -- | Type to represent response from the `zcashd` RPC `getrawtransaction`
data RawTxResponse = RawTxResponse data RawTxResponse = RawTxResponse
{ rt_id :: T.Text { rt_id :: !HexString
, rt_hex :: BS.ByteString , rt_hex :: !HexString
, rt_shieldedOutputs :: [BS.ByteString] , rt_shieldedSpends :: ![ShieldedSpend]
, rt_orchardActions :: [OrchardAction] , rt_shieldedOutputs :: ![ShieldedOutput]
, rt_blockheight :: Integer , rt_orchardActions :: ![OrchardAction]
, rt_confirmations :: Integer , rt_blockheight :: !Integer
, rt_blocktime :: Integer , rt_confirmations :: !Integer
} deriving (Prelude.Show, Eq) , rt_blocktime :: !Integer
} deriving (Prelude.Show, Eq, Read)
-- * Sapling -- ** `zebrad`
-- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@. data ZebraTxResponse = ZebraTxResponse
data ShieldedOutput = ShieldedOutput { ztr_blockheight :: !Int
{ s_cv :: BS.ByteString -- ^ Value commitment to the input note , ztr_conf :: !Int
, s_cmu :: BS.ByteString -- ^ The u-coordinate of the note commitment for the output note , ztr_hex :: !HexString
, s_ephKey :: BS.ByteString -- ^ Ephemeral Jubjub public key } deriving (Prelude.Show, Eq, Read)
, 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 instance FromJSON ZebraTxResponse where
, s_proof :: BS.ByteString -- ^ Zero-knowledge proof using the Sapling Output circuit 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 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)
instance ToJSON SaplingBundle where
toJSON (SaplingBundle s o v sig) =
object ["spends" .= s, "outputs" .= o, "value" .= v, "sig" .= sig]
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)
instance ToJSON OrchardBundle where
toJSON (OrchardBundle a f v an p s) =
object
[ "actions" .= a
, "flags" .= f
, "value" .= v
, "anchor" .= an
, "proof" .= p
, "sig" .= s
]
fromRawOBundle :: RawOBundle -> Maybe OrchardBundle
fromRawOBundle 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
instance ToJSON OrchardFlags where
toJSON (OrchardFlags s o) =
Data.Aeson.Array $ V.fromList [Data.Aeson.Bool s, Data.Aeson.Bool o]
-- | Type for the response from the `zebrad` RPC method `getinfo`
data ZebraGetInfo = ZebraGetInfo
{ zgi_build :: !T.Text
, zgi_subversion :: !T.Text
} deriving (Prelude.Show, Eq)
instance FromJSON ZebraGetInfo where
parseJSON =
withObject "ZebraGetInfo" $ \obj -> do
b <- obj .: "build"
s <- obj .: "subversion"
pure $ ZebraGetInfo b s
-- | Type for the response from the `zebrad` RPC method `getblockchaininfo`
data ZebraGetBlockChainInfo = ZebraGetBlockChainInfo
{ zgb_best :: !HexString
, zgb_blocks :: !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, 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_network :: !ZcashNet
, ta_receiver :: !TransparentReceiver
} deriving (Eq, Prelude.Show, Read)
-- | Type to represent a TEX Zcash addresses
data ExchangeAddress = ExchangeAddress
{ ex_network :: !ZcashNet
, ex_address :: !TransparentReceiver
} deriving (Eq, Prelude.Show, Read)
-- | Wrapper types for transparent elements
data RawTxIn = RawTxIn
{ rti_outpoint :: !RawOutPoint
, 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 ToJSON ShieldedSpend where
toJSON (ShieldedSpend cv a n rk p au) =
object
[ "cv" .= cv
, "anchor" .= a
, "nullifier" .= n
, "rk" .= rk
, "proof" .= p
, "spendAuthSig" .= au
]
instance FromJSON ShieldedSpend where
parseJSON =
withObject "ShieldedSpend" $ \obj -> do
cv <- obj .: "cv"
anchor <- obj .: "anchor"
nullifier <- obj .: "nullifier"
rk <- obj .: "rk"
p <- obj .: "proof"
sig <- obj .: "spendAuthSig"
pure $ ShieldedSpend cv anchor nullifier rk p sig
-- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@.
data ShieldedOutput = ShieldedOutput
{ s_cv :: !HexString -- ^ Value commitment to the input note
, 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 (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput
instance ToJSON ShieldedOutput where
toJSON (ShieldedOutput c cm e enc o p) =
object
[ "cv" .= c
, "cmu" .= cm
, "ephemeralKey" .= e
, "encCiphertext" .= enc
, "outCiphertext" .= o
, "proof" .= p
]
instance FromJSON ShieldedOutput where instance FromJSON ShieldedOutput where
parseJSON = parseJSON =
withObject "ShieldedOutput" $ \obj -> do withObject "ShieldedOutput" $ \obj -> do
@ -133,22 +604,93 @@ instance FromJSON ShieldedOutput where
encText <- obj .: "encCiphertext" encText <- obj .: "encCiphertext"
outText <- obj .: "outCiphertext" outText <- obj .: "outCiphertext"
p <- obj .: "proof" p <- obj .: "proof"
pure $ pure $ ShieldedOutput cv cmu ephKey encText outText p
ShieldedOutput
(decodeHexText cv) -- | Type for a Sapling note commitment tree
(decodeHexText cmu) newtype SaplingCommitmentTree = SaplingCommitmentTree
(decodeHexText ephKey) { sapTree :: HexString
(decodeHexText encText) } deriving (Eq, Prelude.Show, Read)
(decodeHexText outText)
(decodeHexText p) data SaplingRawTree = SaplingRawTree
{ srt_left :: !HexString
, srt_right :: !HexString
, srt_parents :: ![HexString]
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingRawTree
data SaplingTree = SaplingTree
{ st_left :: !(Maybe HexString)
, st_right :: !(Maybe HexString)
, st_parents :: ![Maybe HexString]
} deriving (Eq, Prelude.Show, Read)
data SaplingFrontier = SaplingFrontier
{ sf_pos :: !Int64
, sf_leaf :: !HexString
, sf_ommers :: ![HexString]
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingFrontier
-- | Type for a Sapling incremental witness
newtype SaplingWitness = SaplingWitness
{ sapWit :: HexString
} deriving (Eq, Prelude.Show, Read)
-- * Orchard -- * 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 :: !(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
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawUA
-- | A type to handle user-entered addresses
data ValidAddress
= Unified !UnifiedAddress
| Sapling !SaplingAddress
| Transparent !TransparentAddress
| Exchange !ExchangeAddress
deriving stock (Eq, Prelude.Show)
-- | Type to represent a Unified Full Viewing Key -- | Type to represent a Unified Full Viewing Key
data UnifiedFullViewingKey = UnifiedFullViewingKey data UnifiedFullViewingKey = UnifiedFullViewingKey
{ net :: Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@. { net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@.
, o_key :: BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, s_key :: BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, t_key :: BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
@ -156,19 +698,32 @@ 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) -- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf)
data OrchardAction = OrchardAction data OrchardAction = OrchardAction
{ nf :: BS.ByteString -- ^ The nullifier of the input note { nf :: !HexString -- ^ The nullifier of the input note
, rk :: BS.ByteString -- ^ The randomized validating key for @auth@ , rk :: !HexString -- ^ The randomized validating key for @auth@
, cmx :: BS.ByteString -- ^ The x-coordinate of the note commitment for the output note , cmx :: !HexString -- ^ The x-coordinate of the note commitment for the output note
, eph_key :: BS.ByteString -- ^ An encoding of an ephemeral Pallas public key , eph_key :: !HexString -- ^ An encoding of an ephemeral Pallas public key
, enc_ciphertext :: BS.ByteString -- ^ The output note encrypted to the recipient , enc_ciphertext :: !HexString -- ^ The output note encrypted to the recipient
, out_ciphertext :: BS.ByteString -- ^ A ciphertext enabling the sender to recover the output note , out_ciphertext :: !HexString -- ^ 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 , cv :: !HexString -- ^ 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 , auth :: !HexString -- ^ A signature authorizing the spend in this Action
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardAction deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardAction
instance ToJSON OrchardAction where
toJSON (OrchardAction n r c e en o cv a) =
object
[ "nullifier" .= n
, "rk" .= r
, "cmx" .= c
, "ephemeralKey" .= e
, "encCiphertext" .= en
, "outCiphertext" .= o
, "cv" .= cv
, "spendAuthSig" .= a
]
instance FromJSON OrchardAction where instance FromJSON OrchardAction where
parseJSON = parseJSON =
withObject "OrchardAction" $ \obj -> do withObject "OrchardAction" $ \obj -> do
@ -180,27 +735,139 @@ instance FromJSON OrchardAction where
outText <- obj .: "outCiphertext" outText <- obj .: "outCiphertext"
cval <- obj .: "cv" cval <- obj .: "cv"
a <- obj .: "spendAuthSig" a <- obj .: "spendAuthSig"
pure $ pure $ OrchardAction n r c ephKey encText outText cval a
OrchardAction
(decodeHexText n) data MerklePath = MerklePath
(decodeHexText r) { mp_position :: !Int32
(decodeHexText c) , mp_path :: ![HexString]
(decodeHexText ephKey) } deriving stock (Eq, Prelude.Show, GHC.Generic)
(decodeHexText encText) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
(decodeHexText outText) deriving anyclass (Data.Structured.Show)
(decodeHexText cval) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct MerklePath
(decodeHexText a)
-- | Type for a Orchard note commitment tree
newtype OrchardCommitmentTree = OrchardCommitmentTree
{ orchTree :: HexString
} deriving (Eq, Prelude.Show, Read)
data OrchardRawTree = OrchardRawTree
{ ort_left :: !HexString
, ort_right :: !HexString
, ort_parents :: ![HexString]
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardRawTree
data OrchardTree = OrchardTree
{ ot_left :: !(Maybe HexString)
, ot_right :: !(Maybe HexString)
, ot_parents :: ![Maybe HexString]
} deriving (Eq, Prelude.Show, Read)
data OrchardFrontier = OrchardFrontier
{ of_pos :: !Int64
, of_leaf :: !HexString
, of_ommers :: ![HexString]
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardFrontier
-- | Type for a Sapling incremental witness
newtype OrchardWitness = OrchardWitness
{ orchWit :: HexString
} deriving (Eq, Prelude.Show, Read)
-- | Type to represent a decoded note -- | Type to represent a decoded note
data DecodedNote = DecodedNote data DecodedNote = DecodedNote
{ a_value :: Int64 -- ^ The amount of the transaction in _zatoshis_. { a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_.
, a_recipient :: BS.ByteString -- ^ The recipient Orchard receiver. , a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver.
, a_memo :: BS.ByteString -- ^ The decoded shielded memo field. , 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 stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote 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 :: !MerklePath
} 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 :: !MerklePath
} 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
| PrivacyPolicyError !T.Text
| ZHError
deriving (Eq, Prelude.Show, Read)
-- * Classes
-- | Class to represent types with a bytestring representation
class ToBytes a where
getBytes :: a -> BS.ByteString
-- * Helpers -- * Helpers
-- | Helper function to turn a hex-encoded string to bytestring -- | Helper function to turn a hex-encoded string to bytestring
decodeHexText :: String -> BS.ByteString decodeHexText :: String -> BS.ByteString

View file

@ -1,9 +1,13 @@
-- Copyright 2022-2024 Vergara Technologies LLC
--
-- This file is part of Zcash-Haskell.
--
-- | -- |
-- Module : ZcashHaskell.Utils -- Module : ZcashHaskell.Utils
-- Copyright : Vergara Technologies (c)2023 -- Copyright : 2022-2024 Vergara Technologies LLC
-- License : BOSL -- License : MIT
-- --
-- Maintainer : rene@vergara.network -- Maintainer : pitmutt@vergara.tech
-- Stability : experimental -- Stability : experimental
-- Portability : unknown -- Portability : unknown
-- --
@ -15,21 +19,40 @@ module ZcashHaskell.Utils where
import C.Zcash import C.Zcash
( rustWrapperBech32Decode ( rustWrapperBech32Decode
, rustWrapperBech32Encode
, rustWrapperBech32mEncode
, rustWrapperCreateTx
, rustWrapperF4Jumble , rustWrapperF4Jumble
, rustWrapperF4UnJumble , rustWrapperF4UnJumble
, rustWrapperTxRead
) )
import Control.Exception (SomeException(..), try)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Data.Aeson import Data.Aeson
import Data.Binary.Get
import qualified Data.ByteString as BS 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 as T
import qualified Data.Text.Encoding as E
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
import Network.HTTP.Client (HttpException(..))
import Network.HTTP.Simple import Network.HTTP.Simple
import ZcashHaskell.Types import ZcashHaskell.Types
-- * Utility functions
-- | Decode the given bytestring using Bech32 -- | Decode the given bytestring using Bech32
decodeBech32 :: BS.ByteString -> RawData decodeBech32 :: BS.ByteString -> RawData
decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode 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 -- | Apply the F4Jumble transformation to the given bytestring
f4Jumble :: BS.ByteString -> BS.ByteString f4Jumble :: BS.ByteString -> BS.ByteString
f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble
@ -38,6 +61,7 @@ f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble
f4UnJumble :: BS.ByteString -> BS.ByteString f4UnJumble :: BS.ByteString -> BS.ByteString
f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble
-- * Node interaction
-- | Make a Zcash RPC call -- | Make a Zcash RPC call
makeZcashCall :: makeZcashCall ::
(MonadIO m, FromJSON a) (MonadIO m, FromJSON a)
@ -54,3 +78,86 @@ makeZcashCall username password m p = do
setRequestBasicAuth username password $ setRequestBasicAuth username password $
setRequestMethod "POST" defaultRequest setRequestMethod "POST" defaultRequest
httpJSON myRequest 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 ::
HexString -- ^ to obtain the Sapling anchor
-> HexString -- ^ 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
-> ZcashNet -- ^ the network to be used
-> Int -- ^ target block height
-> Bool -- ^ True to build, False to estimate fee
-> IO (Either TxError HexString)
createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do
txResult <-
withBorshBufferOfInitSize 51200 $
rustWrapperCreateTx
(hexBytes sapAnchor)
(hexBytes orchAnchor)
tSpend
sSpend
oSpend
outgoing
(znet == MainNet)
(fromIntegral bh)
build
if BS.length (hexBytes txResult) > 1
then pure $ Right txResult
else case head (BS.unpack $ hexBytes txResult) of
0 -> pure $ Left InsufficientFunds
1 -> pure $ Left ChangeRequired
2 -> pure $ Left Fee
3 -> pure $ Left Balance
4 -> pure $ Left TransparentBuild
5 -> pure $ Left SaplingBuild
6 -> pure $ Left OrchardBuild
7 -> pure $ Left OrchardSpend
8 -> pure $ Left OrchardRecipient
9 -> pure $ Left SaplingBuilderNotAvailable
10 -> pure $ Left OrchardBuilderNotAvailable
_ -> pure $ Left ZHError

View file

@ -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.6
# 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

File diff suppressed because one or more lines are too long

View file

@ -1,58 +1,73 @@
cabal-version: 1.12 cabal-version: 3.0
-- This file has been generated from package.yaml by hpack version 0.35.1. -- This file has been generated from package.yaml by hpack version 0.36.0.
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
name: zcash-haskell name: zcash-haskell
version: 0.2.0 version: 0.7.5.0
synopsis: Utilities to interact with the Zcash blockchain synopsis: Utilities to interact with the Zcash blockchain
description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme> description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme>
category: Blockchain category: Blockchain
author: Rene Vergara author: Rene Vergara
maintainer: rene@vergara.network maintainer: pitmutt@vergara.tech
copyright: (c)2023 Vergara Technologies LLC copyright: (c)2022-2024 Vergara Technologies LLC
license: BOSL license: MIT
license-file: LICENSE license-file: LICENSE
build-type: Simple build-type: Custom
extra-source-files: extra-source-files:
Setup.hs
extra-doc-files:
README.md README.md
CHANGELOG.md CHANGELOG.md
configure
source-repository head custom-setup
type: git setup-depends:
location: https://git.vergara.tech/Vergara_Tech/zcash-haskell 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 library
exposed-modules: exposed-modules:
C.Zcash C.Zcash
ZcashHaskell.Keys
ZcashHaskell.Orchard ZcashHaskell.Orchard
ZcashHaskell.Sapling ZcashHaskell.Sapling
ZcashHaskell.Transparent
ZcashHaskell.Types ZcashHaskell.Types
ZcashHaskell.Utils ZcashHaskell.Utils
other-modules:
Paths_zcash_haskell
hs-source-dirs: hs-source-dirs:
src src
pkgconfig-depends:
rustzcash_wrapper-uninstalled
build-depends: build-depends:
aeson aeson
, base >=4.7 && <5 , base >=4.7 && <5
, base58-bytestring
, borsh >=0.2 , borsh >=0.2
, bytestring , bytestring
, cryptonite
, foreign-rust , foreign-rust
, generics-sop , generics-sop
, hexstring >=0.12.1
, http-conduit , http-conduit
, http-client
, memory
, binary
, text , text
, haskoin-core
, secp256k1-haskell >= 1.1
, vector
, utf8-string
build-tool-depends:
c2hs:c2hs
default-language: Haskell2010 default-language: Haskell2010
test-suite zcash-haskell-test test-suite zcash-haskell-test
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
main-is: Spec.hs main-is: Spec.hs
other-modules:
Paths_zcash_haskell
hs-source-dirs: hs-source-dirs:
test test
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N
@ -60,7 +75,17 @@ test-suite zcash-haskell-test
aeson aeson
, base >=4.7 && <5 , base >=4.7 && <5
, bytestring , bytestring
, haskoin-core
, hexstring >= 0.12.1
, hspec , hspec
, HUnit
, QuickCheck
, quickcheck-transformer
, text , text
, zcash-haskell , zcash-haskell
, binary
, cryptonite
, secp256k1-haskell
, http-conduit
pkgconfig-depends: rustzcash_wrapper
default-language: Haskell2010 default-language: Haskell2010

20
zebrablock.json Normal file
View file

@ -0,0 +1,20 @@
{
"result": {
"hash": "0041ee9cb0e256a73c92bb72d830143c402ea350152f56f19f74d23cf51418fb",
"confirmations": 3583,
"height": 2767099,
"tx": [
"d169ec3eda57dc750edfc1aa6b8ffb4ed2065780bfd5964de34b529503ec372f",
"987fcdb9bd37cbb5b205a8336de60d043f7028bebaa372828d81f3da296c7ef9"
],
"trees": {
"sapling": {
"size": 129349
},
"orchard": {
"size": 39382
}
}
},
"id": 123
}

4
zebrahexblock.json Normal file

File diff suppressed because one or more lines are too long

8
zebratx.json Normal file
View file

@ -0,0 +1,8 @@
{
"result": {
"hex": "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02",
"height": 2767099,
"confirmations": 3582
},
"id": 123
}