diff --git a/INSTALL.md b/INSTALL.md index c47c6ddfe..bb667b7be 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -39,7 +39,7 @@ sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib \ git clone https://github.com/MyHush/hush3.git cd hush3 # Build -./zcutil/build.sh -j$(nproc) +./build.sh -j$(nproc) ``` ## Run a HUSH Node @@ -63,10 +63,8 @@ Downloading Git source repo, building and running Hush: # pull git clone https://github.com/MyHush/hush3.git cd hush -# fetch key -./zcutil/fetch-params.sh # Build -./zcutil/build-win.sh -j$(nproc) +./build-win.sh -j$(nproc) # Run a HUSH node ./src/hushd ``` diff --git a/README.md b/README.md index 1ec5dd7b1..95c8999fe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HUSH 3 +# HUSH ## What is HUSH? diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..2ae8514c3 --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Copyright (c) 2019-2020 The Hush developers + +set -eu -o pipefail +./zcutil/build.sh $@ diff --git a/configure.ac b/configure.ac index cf712cc67..e3be0c9dd 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 3) -define(_CLIENT_VERSION_MINOR, 3) -define(_CLIENT_VERSION_REVISION, 2) +define(_CLIENT_VERSION_MINOR, 4) +define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 50) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) diff --git a/contrib/block_time.pl b/contrib/block_time.pl index dff43ea4e..17da3c461 100755 --- a/contrib/block_time.pl +++ b/contrib/block_time.pl @@ -1,5 +1,5 @@ #!/usr/bin/perl -# Copyright 2019 The Hush developers +# Copyright 2019-2020 The Hush developers # Released under the GPLv3 use warnings; use strict; @@ -9,6 +9,11 @@ my $block = shift || die "Usage: $0 123"; my $hush = "./src/hush-cli"; my $blockcount = qx{$hush getblockcount}; +unless ($blockcount = int($blockcount)) { + print "Invalid response from hush-cli\n"; + exit 1; +} + if ($block <= $blockcount) { die "That block has already happened!"; } else { diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 48c60710d..3112ca7be 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,7 +1,7 @@ package=boost $(package)_version=1_72_0 -$(package)_download_path=https://dl.bintray.com/boostorg/release/1.72.0/source/ +$(package)_download_path=https://github.com/MyHush/boost/releases/download/v1.72.0/ $(package)_sha256_hash=59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722 $(package)_file_name=$(package)_$($(package)_version).tar.bz2 diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk index b3693cc6f..64fee77ba 100644 --- a/depends/packages/native_ccache.mk +++ b/depends/packages/native_ccache.mk @@ -1,8 +1,8 @@ package=native_ccache -$(package)_version=3.7.7 +$(package)_version=3.7.9 $(package)_download_path=https://github.com/ccache/ccache/releases/download/v$($(package)_version) -$(package)_file_name=ccache-$($(package)_version).tar.xz -$(package)_sha256_hash=b7c1d6d6fe42f18e424de92746af863e0bc85794da3d69e44300840c478c98cd +$(package)_file_name=ccache-$($(package)_version).tar.gz +$(package)_sha256_hash=92838e2133c9e704fdab9ee2608dad86c99021278b9ac47d065aa8ff2ea8ce36 define $(package)_set_vars $(package)_config_opts= diff --git a/doc/man/hush-cli.1 b/doc/man/hush-cli.1 index 0c9080b59..5d87d8205 100644 --- a/doc/man/hush-cli.1 +++ b/doc/man/hush-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.10. -.TH HUSH-CLI "1" "March 2020" "hush-cli v3.3.2" "User Commands" +.TH HUSH-CLI "1" "June 2020" "hush-cli v3.4.0" "User Commands" .SH NAME -hush-cli \- manual page for hush-cli v3.3.2 +hush-cli \- manual page for hush-cli v3.4.0 .SH DESCRIPTION -Komodo RPC client version v3.3.2\-699b59037 +Komodo RPC client version v3.4.0\-2fbcca416\-dirty .PP In order to ensure you are adequately protecting your privacy when using Hush, please see . @@ -71,7 +71,7 @@ Timeout in seconds during HTTP requests, or 0 for no timeout. (default: Read extra arguments from standard input, one per line until EOF/Ctrl\-D (recommended for sensitive information such as passphrases) .SH COPYRIGHT -Hush Daemon version v3.3.2-699b59037 +Hush Daemon version v3.4.0-2fbcca416-dirty In order to ensure you are adequately protecting your privacy when using Hush, please see . diff --git a/doc/man/hush-tx.1 b/doc/man/hush-tx.1 index 9004dcade..a6d3be7ef 100644 --- a/doc/man/hush-tx.1 +++ b/doc/man/hush-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.10. -.TH HUSH-TX "1" "March 2020" "hush-tx v3.3.2" "User Commands" +.TH HUSH-TX "1" "June 2020" "hush-tx v3.4.0" "User Commands" .SH NAME -hush-tx \- manual page for hush-tx v3.3.2 +hush-tx \- manual page for hush-tx v3.4.0 .SH DESCRIPTION -Hush komodo\-tx utility version v3.3.2\-699b59037 +Hush komodo\-tx utility version v3.4.0\-2fbcca416\-dirty .SS "Usage:" .TP komodo\-tx [options] [commands] @@ -84,7 +84,7 @@ set=NAME:JSON\-STRING .IP Set register NAME to given JSON\-STRING .SH COPYRIGHT -Hush Daemon version v3.3.2-699b59037 +Hush Daemon version v3.4.0-2fbcca416-dirty In order to ensure you are adequately protecting your privacy when using Hush, please see . diff --git a/doc/man/hushd.1 b/doc/man/hushd.1 index 0cde080df..6e57386b7 100644 --- a/doc/man/hushd.1 +++ b/doc/man/hushd.1 @@ -1,10 +1,10 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.10. -.TH HUSHD "1" "March 2020" "hushd v3.3.2" "User Commands" +.TH HUSHD "1" "June 2020" "hushd v3.4.0" "User Commands" .SH NAME -hushd \- manual page for hushd v3.3.2 +hushd \- manual page for hushd v3.4.0 .SH DESCRIPTION Found binary: ./komodod -Hush Daemon version v3.3.2\-699b59037 +Hush Daemon version v3.4.0\-2fbcca416\-dirty .PP In order to ensure you are adequately protecting your privacy when using Hush, please see . @@ -87,6 +87,11 @@ leave that many cores free, default: 0) .IP Specify pid file (default: komodod.pid) .HP +\fB\-txexpirynotify=\fR +.IP +Execute command when transaction expires (%s in cmd is replaced by +transaction id) +.HP \fB\-prune=\fR .IP Reduce storage requirements by pruning (deleting) old blocks. This mode @@ -295,6 +300,11 @@ Keep the last transactions (default: 200) .IP Keep transactions for at least blocks (default: 10000) .HP +\fB\-opretmintxfee=\fR +.IP +Minimum fee (in KMD/kB) to allow for OP_RETURN transactions (default: +400000) +.HP \fB\-paytxfee=\fR .IP Fee (in KMD/kB) to add to transactions you send (default: 0.00) @@ -629,7 +639,7 @@ Starting supply, default is 0 .IP Enforce transaction\-rate limit, default 0 .SH COPYRIGHT -Hush Daemon version v3.3.2-699b59037 +Hush Daemon version v3.4.0-2fbcca416-dirty In order to ensure you are adequately protecting your privacy when using Hush, please see . diff --git a/doc/payment-disclosure.md b/doc/payment-disclosure.md deleted file mode 100644 index 02b4167da..000000000 --- a/doc/payment-disclosure.md +++ /dev/null @@ -1,107 +0,0 @@ -# Payment Disclosure (Experimental Feature) - -**Summary** - -Use RPC calls `z_getpaymentdisclosure` and `z_validatepaymentdisclosure` to reveal details of a shielded payment. - -**Who should read this document** - -Frequent users of shielded transactions, payment processors, exchanges, block explorer - -### Experimental Feature - -This is an experimental feature. Enable it by launching `zcashd` with flags: - - zcashd -experimentalfeatures -paymentdisclosure -debug=paymentdisclosure -txindex=1 - -These flags can also be set as options in `zcash.conf`. - -All nodes that generate or validate payment disclosures must run with `txindex=1` enabled. - -### Background - -Payment Disclosure is an implementation of the work-in-progress Payment Disclosure ZIP [1]. - -The ZIP describes a method of proving that a payment was sent to a shielded address. In the typical case, this means enabling a sender to present a proof that they transferred funds to a recipient's shielded address. - -[1] https://github.com/zcash/zips/pull/119 - -### Example Use Case - -Alice the customer sends 10 HUSH to Bob the merchant at the shielded address shown on their website. However, Bob is not sure if he received the funds. - -Alice's node is running with payment disclosure enabled, so Alice generates a payment disclosure and provides it to Bob, who verifies the payment was made. - -If Bob is a bad merchant, Alice can present the payment disclosure to a third party to validate that payment was indeed made. - -### Solution - -A payment disclosure can be generated for any output of a JoinSplit using the RPC call: - - z_getpaymentdisclosure txid js_index output_index (message) - -An optional message can be supplied. This could be used for a refund address or some other reference, as currently it is not common practice to (ahead of time) include a refund address in the memo field when making a payment. - -To validate a payment disclosure, the following RPC call can be used: - - z_validatepaymentdisclosure hexdata - -### Example - -Generate a payment disclosure for the first joinsplit, second output (index starts from zero): - - hush-cli z_getpaymentdisclosure 79189528d611e811a1c7bb0358dd31343033d14b4c1e998d7c4799c40f8b652b 0 1 "Hello" - -This returns a payment disclosure in the form of a hex string: - - 706462ff000a3722aafa8190cdf9710bfad6da2af6d3a74262c1fc96ad47df814b0cd5641c2b658b0fc499477c8d991e4c4bd133303431dd5803bbc7a111e811d6289518790000000000000000017e861adb829d8cb1cbcf6330b8c2e25fb0d08041a67a857815a136f0227f8a5342bce5b3c0d894e2983000eb594702d3c1580817d0374e15078528e56bb6f80c0548656c6c6f59a7085395c9e706d82afe3157c54ad4ae5bf144fcc774a8d9c921c58471402019c156ec5641e2173c4fb6467df5f28530dc4636fa71f4d0e48fc5c560fac500 - -To validate the payment disclosure: - - hush-cli z_validatepaymentdisclosure HEXDATA - -This returns data related to the payment and the payment disclosure: - - { - "txid": "79189528d611e811a1c7bb0358dd31343033d14b4c1e998d7c4799c40f8b652b", - "jsIndex": 0, - "outputIndex": 1, - "version": 0, - "onetimePrivKey": "1c64d50c4b81df47ad96fcc16242a7d3f62adad6fa0b71f9cd9081faaa22370a", - "message": "Hello", - "joinSplitPubKey": "d1c465d16166b602992479acfac18e87dc18065f6cefde6a002e70bc371b9faf", - "signatureVerified": true, - "paymentAddress": "ztaZJXy8iX8nrk2ytXKDBoTWqPkhQcj6E2ifARnD3wfkFwsxXs5SoX7NGmrjkzSiSKn8VtLHTJae48vX5NakvmDhtGNY5eb", - "memo": "f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "value": 12.49900000, - "commitmentMatch": true, - "valid": true - } - -The `signatureVerified` field confirms that the payment disclosure was generated and signed with the joinSplitPrivKey, which should only be known by the node generating and sending the transaction 7918...652b in question. - -### Where is the data stored? - -For all nodes, payment disclosure does not touch `wallet.dat` in any way. - -For nodes that only validate payment disclosures, no data is stored locally. - -For nodes that generate payment disclosures, a LevelDB database is created in the node's datadir. For most users, this would be in the folder: - - $HOME/.zcash/paymentdisclosure - -If you decide you don't want to use payment disclosure, it is safe to shut down your node and delete the database folder. - -### Security Properties - -Please consult the work-in-progress ZIP for details about the protocol, security properties and caveats. - -### Reminder - -Feedback is most welcome! - -This is an experimental feature so there are no guarantees that the protocol, database format, RPC interface etc. will remain the same in the future. - -### Notes - -Currently there is no user friendly way to help senders identify which joinsplit output index maps to a given payment they made. It is possible to construct this from `debug.log`. Ideas and feedback are most welcome on how to improve the user experience. diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index a23f2908d..35bf5a7e9 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -17,14 +17,12 @@ testScripts=( 'dpow.py' 'dpowconfs.py' 'ac_private.py' - 'paymentdisclosure.py' 'prioritisetransaction.py' 'wallet_treestate.py' 'wallet_anchorfork.py' 'wallet_changeindicator.py' 'wallet_import_export.py' 'wallet_protectcoinbase.py' - 'wallet_shieldcoinbase_sprout.py' 'wallet_shieldcoinbase_sapling.py' 'wallet_listreceived.py' 'wallet_mergetoaddress.py' @@ -65,14 +63,11 @@ testScripts=( 'decodescript.py' 'blockchain.py' 'disablewallet.py' - 'zcjoinsplit.py' - 'zcjoinsplitdoublespend.py' 'ivk_import_export.py' 'zkey_import_export.py' 'getblocktemplate.py' 'bip65-cltv-p2p.py' 'bipdersig-p2p.py' - 'p2p_nu_peer_management.py' 'rewind_index.py' 'p2p_txexpiry_dos.py' 'p2p_node_bloom.py' diff --git a/qa/rpc-tests/p2p_nu_peer_management.py b/qa/rpc-tests/p2p_nu_peer_management.py deleted file mode 100755 index 6cedf66bb..000000000 --- a/qa/rpc-tests/p2p_nu_peer_management.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env python2 -# Copyright (c) 2018 The Zcash developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from test_framework.mininode import ( - NodeConn, - NodeConnCB, - NetworkThread, - msg_ping, - SPROUT_PROTO_VERSION, - OVERWINTER_PROTO_VERSION, - SAPLING_PROTO_VERSION, -) -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import initialize_chain_clean, start_nodes, \ - p2p_port, assert_equal - -import time - -# -# In this test we connect Sprout, Overwinter, and Sapling mininodes to a Zcashd -# node which will activate Overwinter at block 10 and Sapling at block 15. -# -# We test: -# 1. the mininodes stay connected to Zcash with Sprout consensus rules -# 2. when Overwinter activates, the Sprout mininodes are dropped -# 3. new Overwinter and Sapling nodes can connect to Zcash -# 4. new Sprout nodes cannot connect to Zcash -# 5. when Sapling activates, the Overwinter mininodes are dropped -# 6. new Sapling nodes can connect to Zcash -# 7. new Sprout and Overwinter nodes cannot connect to Zcash -# -# This test *does not* verify that prior to each activation, the Zcashd -# node will prefer connections with NU-aware nodes, with an eviction process -# that prioritizes non-NU-aware connections. -# - - -class TestManager(NodeConnCB): - def __init__(self): - NodeConnCB.__init__(self) - self.create_callback_map() - - def on_close(self, conn): - pass - - def on_reject(self, conn, message): - conn.rejectMessage = message - - -class NUPeerManagementTest(BitcoinTestFramework): - - def setup_chain(self): - print "Initializing test directory "+self.options.tmpdir - initialize_chain_clean(self.options.tmpdir, 1) - - def setup_network(self): - self.nodes = start_nodes(1, self.options.tmpdir, extra_args=[[ - '-nuparams=5ba81b19:10', # Overwinter - '-nuparams=76b809bb:15', # Sapling - '-debug', - '-whitelist=127.0.0.1', - ]]) - - def run_test(self): - test = TestManager() - - # Launch Sprout, Overwinter, and Sapling mininodes - nodes = [] - for x in xrange(10): - nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], - test, "regtest", SPROUT_PROTO_VERSION)) - nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], - test, "regtest", OVERWINTER_PROTO_VERSION)) - nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], - test, "regtest", SAPLING_PROTO_VERSION)) - - # Start up network handling in another thread - NetworkThread().start() - - # Sprout consensus rules apply at block height 9 - self.nodes[0].generate(9) - assert_equal(9, self.nodes[0].getblockcount()) - - # Verify mininodes are still connected to zcashd node - peerinfo = self.nodes[0].getpeerinfo() - versions = [x["version"] for x in peerinfo] - assert_equal(10, versions.count(SPROUT_PROTO_VERSION)) - assert_equal(10, versions.count(OVERWINTER_PROTO_VERSION)) - assert_equal(10, versions.count(SAPLING_PROTO_VERSION)) - - # Overwinter consensus rules activate at block height 10 - self.nodes[0].generate(1) - assert_equal(10, self.nodes[0].getblockcount()) - print('Overwinter active') - - # Mininodes send ping message to zcashd node. - pingCounter = 1 - for node in nodes: - node.send_message(msg_ping(pingCounter)) - pingCounter = pingCounter + 1 - - time.sleep(3) - - # Verify Sprout mininodes have been dropped, while Overwinter and - # Sapling mininodes are still connected. - peerinfo = self.nodes[0].getpeerinfo() - versions = [x["version"] for x in peerinfo] - assert_equal(0, versions.count(SPROUT_PROTO_VERSION)) - assert_equal(10, versions.count(OVERWINTER_PROTO_VERSION)) - assert_equal(10, versions.count(SAPLING_PROTO_VERSION)) - - # Extend the Overwinter chain with another block. - self.nodes[0].generate(1) - - # Connect a new Overwinter mininode to the zcashd node, which is accepted. - nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", OVERWINTER_PROTO_VERSION)) - time.sleep(3) - assert_equal(21, len(self.nodes[0].getpeerinfo())) - - # Connect a new Sapling mininode to the zcashd node, which is accepted. - nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", SAPLING_PROTO_VERSION)) - time.sleep(3) - assert_equal(22, len(self.nodes[0].getpeerinfo())) - - # Try to connect a new Sprout mininode to the zcashd node, which is rejected. - sprout = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", SPROUT_PROTO_VERSION) - nodes.append(sprout) - time.sleep(3) - assert("Version must be 170003 or greater" in str(sprout.rejectMessage)) - - # Verify that only Overwinter and Sapling mininodes are connected. - peerinfo = self.nodes[0].getpeerinfo() - versions = [x["version"] for x in peerinfo] - assert_equal(0, versions.count(SPROUT_PROTO_VERSION)) - assert_equal(11, versions.count(OVERWINTER_PROTO_VERSION)) - assert_equal(11, versions.count(SAPLING_PROTO_VERSION)) - - # Sapling consensus rules activate at block height 15 - self.nodes[0].generate(4) - assert_equal(15, self.nodes[0].getblockcount()) - print('Sapling active') - - # Mininodes send ping message to zcashd node. - pingCounter = 1 - for node in nodes: - node.send_message(msg_ping(pingCounter)) - pingCounter = pingCounter + 1 - - time.sleep(3) - - # Verify Sprout and Overwinter mininodes have been dropped, while - # Sapling mininodes are still connected. - peerinfo = self.nodes[0].getpeerinfo() - versions = [x["version"] for x in peerinfo] - assert_equal(0, versions.count(SPROUT_PROTO_VERSION)) - assert_equal(0, versions.count(OVERWINTER_PROTO_VERSION)) - assert_equal(11, versions.count(SAPLING_PROTO_VERSION)) - - # Extend the Sapling chain with another block. - self.nodes[0].generate(1) - - # Connect a new Sapling mininode to the zcashd node, which is accepted. - nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", SAPLING_PROTO_VERSION)) - time.sleep(3) - assert_equal(12, len(self.nodes[0].getpeerinfo())) - - # Try to connect a new Sprout mininode to the zcashd node, which is rejected. - sprout = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", SPROUT_PROTO_VERSION) - nodes.append(sprout) - time.sleep(3) - assert("Version must be 170006 or greater" in str(sprout.rejectMessage)) - - # Try to connect a new Overwinter mininode to the zcashd node, which is rejected. - sprout = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", OVERWINTER_PROTO_VERSION) - nodes.append(sprout) - time.sleep(3) - assert("Version must be 170006 or greater" in str(sprout.rejectMessage)) - - # Verify that only Sapling mininodes are connected. - peerinfo = self.nodes[0].getpeerinfo() - versions = [x["version"] for x in peerinfo] - assert_equal(0, versions.count(SPROUT_PROTO_VERSION)) - assert_equal(0, versions.count(OVERWINTER_PROTO_VERSION)) - assert_equal(12, versions.count(SAPLING_PROTO_VERSION)) - - for node in nodes: - node.disconnect_node() - -if __name__ == '__main__': - NUPeerManagementTest().main() diff --git a/qa/rpc-tests/paymentdisclosure.py b/qa/rpc-tests/paymentdisclosure.py deleted file mode 100755 index 48d4712a9..000000000 --- a/qa/rpc-tests/paymentdisclosure.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python2 -# Copyright (c) 2017 The Zcash developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ - start_node, connect_nodes_bi, wait_and_assert_operationid_status - -from decimal import Decimal - -class PaymentDisclosureTest (BitcoinTestFramework): - - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) - - def setup_network(self, split=False): - args = ['-debug=zrpcunsafe,paymentdisclosure', '-experimentalfeatures', '-paymentdisclosure', '-txindex=1'] - self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, args)) - self.nodes.append(start_node(1, self.options.tmpdir, args)) - # node 2 does not enable payment disclosure - args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-txindex=1'] - self.nodes.append(start_node(2, self.options.tmpdir, args2)) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) - self.is_network_split=False - self.sync_all() - - def run_test (self): - print "Mining blocks..." - - self.nodes[0].generate(4) - walletinfo = self.nodes[0].getwalletinfo() - assert_equal(walletinfo['immature_balance'], 40) - assert_equal(walletinfo['balance'], 0) - self.sync_all() - self.nodes[2].generate(3) - self.sync_all() - self.nodes[1].generate(101) - self.sync_all() - assert_equal(self.nodes[0].getbalance(), 40) - assert_equal(self.nodes[1].getbalance(), 10) - assert_equal(self.nodes[2].getbalance(), 30) - - mytaddr = self.nodes[0].getnewaddress() - myzaddr = self.nodes[0].z_getnewaddress() - - # Check that Node 2 has payment disclosure disabled. - try: - self.nodes[2].z_getpaymentdisclosure("invalidtxid", 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("payment disclosure is disabled" in errorString) - - # Check that Node 0 returns an error for an unknown txid - try: - self.nodes[0].z_getpaymentdisclosure("invalidtxid", 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("No information available about transaction" in errorString) - - # Shield coinbase utxos from node 0 of value 40, standard fee of 0.00010000 - recipients = [{"address":myzaddr, "amount":Decimal('40.0')-Decimal('0.0001')}] - myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - txid = wait_and_assert_operationid_status(self.nodes[0], myopid) - - # Check the tx has joinsplits - assert( len(self.nodes[0].getrawtransaction("" + txid, 1)["vjoinsplit"]) > 0 ) - - # Sync mempools - self.sync_all() - - # Confirm that you can't create a payment disclosure for an unconfirmed tx - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction has not been confirmed yet" in errorString) - - try: - self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction has not been confirmed yet" in errorString) - - # Mine tx - self.nodes[0].generate(1) - self.sync_all() - - # Confirm that Node 1 cannot create a payment disclosure for a transaction which does not impact its wallet - try: - self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction does not belong to the wallet" in errorString) - - # Check that an invalid joinsplit index is rejected - try: - self.nodes[0].z_getpaymentdisclosure(txid, 1, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid js_index" in errorString) - - try: - self.nodes[0].z_getpaymentdisclosure(txid, -1, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid js_index" in errorString) - - # Check that an invalid output index is rejected - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 2) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid output_index" in errorString) - - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, -1) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid output_index" in errorString) - - # Ask Node 0 to create and validate a payment disclosure for output 0 - message = "Here is proof of my payment!" - pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, message) - result = self.nodes[0].z_validatepaymentdisclosure(pd) - assert(result["valid"]) - output_value_sum = Decimal(result["value"]) - - # Ask Node 1 to confirm the payment disclosure is valid - result = self.nodes[1].z_validatepaymentdisclosure(pd) - assert(result["valid"]) - assert_equal(result["message"], message) - assert_equal(result["value"], output_value_sum) - - # Confirm that payment disclosure begins with prefix zpd: - assert(pd.startswith("zpd:")) - - # Confirm that payment disclosure without prefix zpd: fails validation - try: - self.nodes[1].z_validatepaymentdisclosure(pd[4:]) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("payment disclosure prefix not found" in errorString) - - # Check that total value of output index 0 and index 1 should equal shielding amount of 40 less standard fee. - pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 1) - result = self.nodes[0].z_validatepaymentdisclosure(pd) - output_value_sum += Decimal(result["value"]) - assert_equal(output_value_sum, Decimal('39.99990000')) - - # Create a z->z transaction, sending shielded funds from node 0 to node 1 - node1zaddr = self.nodes[1].z_getnewaddress() - recipients = [{"address":node1zaddr, "amount":Decimal('1')}] - myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - txid = wait_and_assert_operationid_status(self.nodes[0], myopid) - self.sync_all() - self.nodes[0].generate(1) - self.sync_all() - - # Confirm that Node 0 can create a valid payment disclosure - pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, "a message of your choice") - result = self.nodes[0].z_validatepaymentdisclosure(pd) - assert(result["valid"]) - - # Confirm that Node 1, even as recipient of shielded funds, cannot create a payment disclosure - # as the transaction was created by Node 0 and Node 1's payment disclosure database does not - # contain the necessary data to do so, where the data would only have been available on Node 0 - # when executing z_shieldcoinbase. - try: - self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Could not find payment disclosure info for the given joinsplit output" in errorString) - - # Payment disclosures cannot be created for transparent transactions. - txid = self.nodes[2].sendtoaddress(mytaddr, 1.0) - self.sync_all() - - # No matter the type of transaction, if it has not been confirmed, it is ignored. - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction has not been confirmed yet" in errorString) - - self.nodes[0].generate(1) - self.sync_all() - - # Confirm that a payment disclosure can only be generated for a shielded transaction. - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction is not a shielded transaction" in errorString) - -if __name__ == '__main__': - PaymentDisclosureTest().main() diff --git a/qa/rpc-tests/regtest_signrawtransaction.py b/qa/rpc-tests/regtest_signrawtransaction.py index 2e0273677..78ec1fbc8 100755 --- a/qa/rpc-tests/regtest_signrawtransaction.py +++ b/qa/rpc-tests/regtest_signrawtransaction.py @@ -1,4 +1,5 @@ #!/usr/bin/env python2 +# Copyright (c) 2019-2020 The Hush developers # Copyright (c) 2018 The Zcash developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,7 +19,7 @@ class RegtestSignrawtransactionTest (BitcoinTestFramework): self.nodes[0].generate(1) self.sync_all() taddr = self.nodes[1].getnewaddress() - zaddr1 = self.nodes[1].z_getnewaddress('sprout') + zaddr1 = self.nodes[1].z_getnewaddress('sapling') self.nodes[0].sendtoaddress(taddr, 2.0) self.nodes[0].generate(1) diff --git a/qa/rpc-tests/wallet_listnotes.py b/qa/rpc-tests/wallet_listnotes.py index 5cd89c661..90fbcced1 100755 --- a/qa/rpc-tests/wallet_listnotes.py +++ b/qa/rpc-tests/wallet_listnotes.py @@ -1,4 +1,5 @@ #!/usr/bin/env python2 +# Copyright (c) 2019-2020 The Hush developers # Copyright (c) 2018 The Zcash developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -20,89 +21,9 @@ class WalletListNotes(BitcoinTestFramework): def run_test(self): # Current height = 200 -> Sprout assert_equal(200, self.nodes[0].getblockcount()) - sproutzaddr = self.nodes[0].z_getnewaddress('sprout') # test that we can create a sapling zaddr before sapling activates saplingzaddr = self.nodes[0].z_getnewaddress('sapling') - - # we've got lots of coinbase (taddr) but no shielded funds yet - assert_equal(0, Decimal(self.nodes[0].z_gettotalbalance()['private'])) - - # Set current height to 201 -> Sprout - self.nodes[0].generate(1) - self.sync_all() - assert_equal(201, self.nodes[0].getblockcount()) - - mining_addr = self.nodes[0].listunspent()[0]['address'] - - # Shield coinbase funds (must be a multiple of 10, no change allowed pre-sapling) - receive_amount_10 = Decimal('10.0') - Decimal('0.0001') - recipients = [{"address":sproutzaddr, "amount":receive_amount_10}] - myopid = self.nodes[0].z_sendmany(mining_addr, recipients) - txid_1 = wait_and_assert_operationid_status(self.nodes[0], myopid) - self.sync_all() - - # No funds (with (default) one or more confirmations) in sproutzaddr yet - assert_equal(0, len(self.nodes[0].z_listunspent())) - assert_equal(0, len(self.nodes[0].z_listunspent(1))) - - # no private balance because no confirmations yet - assert_equal(0, Decimal(self.nodes[0].z_gettotalbalance()['private'])) - - # list private unspent, this time allowing 0 confirmations - unspent_cb = self.nodes[0].z_listunspent(0) - assert_equal(1, len(unspent_cb)) - assert_equal(False, unspent_cb[0]['change']) - assert_equal(txid_1, unspent_cb[0]['txid']) - assert_equal(True, unspent_cb[0]['spendable']) - assert_equal(sproutzaddr, unspent_cb[0]['address']) - assert_equal(receive_amount_10, unspent_cb[0]['amount']) - - # list unspent, filtering by address, should produce same result - unspent_cb_filter = self.nodes[0].z_listunspent(0, 9999, False, [sproutzaddr]) - assert_equal(unspent_cb, unspent_cb_filter) - - # Generate a block to confirm shield coinbase tx - self.nodes[0].generate(1) - self.sync_all() - - # Current height = 202 -> Overwinter. Default address type remains Sprout - assert_equal(202, self.nodes[0].getblockcount()) - - # Send 1.0 (actually 0.9999) from sproutzaddr to a new zaddr - sproutzaddr2 = self.nodes[0].z_getnewaddress() - receive_amount_1 = Decimal('1.0') - Decimal('0.0001') - change_amount_9 = receive_amount_10 - Decimal('1.0') - assert_equal('sprout', self.nodes[0].z_validateaddress(sproutzaddr2)['type']) - recipients = [{"address": sproutzaddr2, "amount":receive_amount_1}] - myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients) - txid_2 = wait_and_assert_operationid_status(self.nodes[0], myopid) - self.sync_all() - - # list unspent, allowing 0conf txs - unspent_tx = self.nodes[0].z_listunspent(0) - assert_equal(len(unspent_tx), 2) - # sort low-to-high by amount (order of returned entries is not guaranteed) - unspent_tx = sorted(unspent_tx, key=lambda k: k['amount']) - assert_equal(False, unspent_tx[0]['change']) - assert_equal(txid_2, unspent_tx[0]['txid']) - assert_equal(True, unspent_tx[0]['spendable']) - assert_equal(sproutzaddr2, unspent_tx[0]['address']) - assert_equal(receive_amount_1, unspent_tx[0]['amount']) - - assert_equal(True, unspent_tx[1]['change']) - assert_equal(txid_2, unspent_tx[1]['txid']) - assert_equal(True, unspent_tx[1]['spendable']) - assert_equal(sproutzaddr, unspent_tx[1]['address']) - assert_equal(change_amount_9, unspent_tx[1]['amount']) - - unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, [sproutzaddr2]) - assert_equal(1, len(unspent_tx_filter)) - assert_equal(unspent_tx[0], unspent_tx_filter[0]) - - unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, [sproutzaddr]) - assert_equal(1, len(unspent_tx_filter)) - assert_equal(unspent_tx[1], unspent_tx_filter[0]) # Set current height to 204 -> Sapling self.nodes[0].generate(2) diff --git a/qa/rpc-tests/zcjoinsplit.py b/qa/rpc-tests/zcjoinsplit.py deleted file mode 100755 index b3ca745f8..000000000 --- a/qa/rpc-tests/zcjoinsplit.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python2 - -# -# Test joinsplit semantics -# - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, start_node, \ - gather_inputs - - -class JoinSplitTest(BitcoinTestFramework): - def setup_network(self): - self.nodes = [] - self.is_network_split = False - self.nodes.append(start_node(0, self.options.tmpdir)) - - def run_test(self): - zckeypair = self.nodes[0].zcrawkeygen() - zcsecretkey = zckeypair["zcsecretkey"] - zcaddress = zckeypair["zcaddress"] - - (total_in, inputs) = gather_inputs(self.nodes[0], 40) - protect_tx = self.nodes[0].createrawtransaction(inputs, {}) - joinsplit_result = self.nodes[0].zcrawjoinsplit(protect_tx, {}, {zcaddress:39.99}, 39.99, 0) - - receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"]) - assert_equal(receive_result["exists"], False) - - protect_tx = self.nodes[0].signrawtransaction(joinsplit_result["rawtxn"]) - self.nodes[0].sendrawtransaction(protect_tx["hex"]) - self.nodes[0].generate(1) - self.sync_all() - - receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"]) - assert_equal(receive_result["exists"], True) - - # The pure joinsplit we create should be mined in the next block - # despite other transactions being in the mempool. - addrtest = self.nodes[0].getnewaddress() - for xx in range(0,10): - self.nodes[0].generate(1) - self.sync_all() - for x in range(0,50): - self.nodes[0].sendtoaddress(addrtest, 0.01); - - joinsplit_tx = self.nodes[0].createrawtransaction([], {}) - joinsplit_result = self.nodes[0].zcrawjoinsplit(joinsplit_tx, {receive_result["note"] : zcsecretkey}, {zcaddress: 39.98}, 0, 0.01) - - self.nodes[0].sendrawtransaction(joinsplit_result["rawtxn"]) - self.nodes[0].generate(1) - self.sync_all() - - print "Done!" - receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"]) - assert_equal(receive_result["exists"], True) - -if __name__ == '__main__': - JoinSplitTest().main() diff --git a/qa/rpc-tests/zcjoinsplitdoublespend.py b/qa/rpc-tests/zcjoinsplitdoublespend.py deleted file mode 100755 index b56e7475a..000000000 --- a/qa/rpc-tests/zcjoinsplitdoublespend.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python2 - -# -# Tests a joinsplit double-spend and a subsequent reorg. -# - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, connect_nodes, \ - gather_inputs, sync_blocks - -import time - -class JoinSplitTest(BitcoinTestFramework): - def setup_network(self): - # Start with split network: - return super(JoinSplitTest, self).setup_network(True) - - def txid_in_mempool(self, node, txid): - exception_triggered = False - - try: - node.getrawtransaction(txid) - except JSONRPCException: - exception_triggered = True - - return not exception_triggered - - def cannot_joinsplit(self, node, txn): - exception_triggered = False - - try: - node.sendrawtransaction(txn) - except JSONRPCException: - exception_triggered = True - - return exception_triggered - - def expect_cannot_joinsplit(self, node, txn): - assert_equal(self.cannot_joinsplit(node, txn), True) - - def run_test(self): - # All nodes should start with 250 HUSH: - starting_balance = 250 - for i in range(4): - assert_equal(self.nodes[i].getbalance(), starting_balance) - self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! - - # Generate zcaddress keypairs - zckeypair = self.nodes[0].zcrawkeygen() - zcsecretkey = zckeypair["zcsecretkey"] - zcaddress = zckeypair["zcaddress"] - - pool = [0, 1, 2, 3] - for i in range(4): - (total_in, inputs) = gather_inputs(self.nodes[i], 40) - pool[i] = self.nodes[i].createrawtransaction(inputs, {}) - pool[i] = self.nodes[i].zcrawjoinsplit(pool[i], {}, {zcaddress:39.99}, 39.99, 0) - signed = self.nodes[i].signrawtransaction(pool[i]["rawtxn"]) - - # send the tx to both halves of the network - self.nodes[0].sendrawtransaction(signed["hex"]) - self.nodes[0].generate(1) - self.nodes[2].sendrawtransaction(signed["hex"]) - self.nodes[2].generate(1) - pool[i] = pool[i]["encryptednote1"] - - sync_blocks(self.nodes[0:2]) - sync_blocks(self.nodes[2:4]) - - # Confirm that the protects have taken place - for i in range(4): - enc_note = pool[i] - receive_result = self.nodes[0].zcrawreceive(zcsecretkey, enc_note) - assert_equal(receive_result["exists"], True) - pool[i] = receive_result["note"] - - # Extra confirmations - receive_result = self.nodes[1].zcrawreceive(zcsecretkey, enc_note) - assert_equal(receive_result["exists"], True) - - receive_result = self.nodes[2].zcrawreceive(zcsecretkey, enc_note) - assert_equal(receive_result["exists"], True) - - receive_result = self.nodes[3].zcrawreceive(zcsecretkey, enc_note) - assert_equal(receive_result["exists"], True) - - blank_tx = self.nodes[0].createrawtransaction([], {}) - # Create joinsplit {A, B}->{*} - joinsplit_AB = self.nodes[0].zcrawjoinsplit(blank_tx, - {pool[0] : zcsecretkey, pool[1] : zcsecretkey}, - {zcaddress:(39.99*2)-0.01}, - 0, 0.01) - - # Create joinsplit {B, C}->{*} - joinsplit_BC = self.nodes[0].zcrawjoinsplit(blank_tx, - {pool[1] : zcsecretkey, pool[2] : zcsecretkey}, - {zcaddress:(39.99*2)-0.01}, - 0, 0.01) - - # Create joinsplit {C, D}->{*} - joinsplit_CD = self.nodes[0].zcrawjoinsplit(blank_tx, - {pool[2] : zcsecretkey, pool[3] : zcsecretkey}, - {zcaddress:(39.99*2)-0.01}, - 0, 0.01) - - # Create joinsplit {A, D}->{*} - joinsplit_AD = self.nodes[0].zcrawjoinsplit(blank_tx, - {pool[0] : zcsecretkey, pool[3] : zcsecretkey}, - {zcaddress:(39.99*2)-0.01}, - 0, 0.01) - - # (a) Node 0 will spend joinsplit AB, then attempt to - # double-spend it with BC. It should fail before and - # after Node 0 mines blocks. - # - # (b) Then, Node 2 will spend BC, and mine 5 blocks. - # Node 1 connects, and AB will be reorg'd from the chain. - # Any attempts to spend AB or CD should fail for - # both nodes. - # - # (c) Then, Node 0 will spend AD, which should work - # because the previous spend for A (AB) is considered - # invalid due to the reorg. - - # (a) - - AB_txid = self.nodes[0].sendrawtransaction(joinsplit_AB["rawtxn"]) - - self.expect_cannot_joinsplit(self.nodes[0], joinsplit_BC["rawtxn"]) - - # Wait until node[1] receives AB before we attempt to double-spend - # with BC. - print "Waiting for AB_txid...\n" - while True: - if self.txid_in_mempool(self.nodes[1], AB_txid): - break - time.sleep(0.2) - print "Done!\n" - - self.expect_cannot_joinsplit(self.nodes[1], joinsplit_BC["rawtxn"]) - - # Generate a block - self.nodes[0].generate(1) - sync_blocks(self.nodes[0:2]) - - self.expect_cannot_joinsplit(self.nodes[0], joinsplit_BC["rawtxn"]) - self.expect_cannot_joinsplit(self.nodes[1], joinsplit_BC["rawtxn"]) - - # (b) - self.nodes[2].sendrawtransaction(joinsplit_BC["rawtxn"]) - self.nodes[2].generate(5) - - # Connect the two nodes - - connect_nodes(self.nodes[1], 2) - sync_blocks(self.nodes) - - # AB and CD should all be impossible to spend for each node. - self.expect_cannot_joinsplit(self.nodes[0], joinsplit_AB["rawtxn"]) - self.expect_cannot_joinsplit(self.nodes[0], joinsplit_CD["rawtxn"]) - - self.expect_cannot_joinsplit(self.nodes[1], joinsplit_AB["rawtxn"]) - self.expect_cannot_joinsplit(self.nodes[1], joinsplit_CD["rawtxn"]) - - self.expect_cannot_joinsplit(self.nodes[2], joinsplit_AB["rawtxn"]) - self.expect_cannot_joinsplit(self.nodes[2], joinsplit_CD["rawtxn"]) - - self.expect_cannot_joinsplit(self.nodes[3], joinsplit_AB["rawtxn"]) - self.expect_cannot_joinsplit(self.nodes[3], joinsplit_CD["rawtxn"]) - - # (c) - # AD should be possible to send due to the reorg that - # tossed out AB. - - self.nodes[0].sendrawtransaction(joinsplit_AD["rawtxn"]) - self.nodes[0].generate(1) - - sync_blocks(self.nodes) - -if __name__ == '__main__': - JoinSplitTest().main() diff --git a/src/Makefile.am b/src/Makefile.am index 6277e0f98..b44dd13c5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,5 @@ +# Copyright 2019-2020 The Hush developers + DIST_SUBDIRS = secp256k1 univalue cryptoconditions AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) @@ -181,14 +183,11 @@ BITCOIN_CORE_H = \ netbase.h \ notaries_staked.h \ noui.h \ - paymentdisclosure.h \ - paymentdisclosuredb.h \ policy/fees.h \ pow.h \ prevector.h \ primitives/block.h \ primitives/transaction.h \ - primitives/nonce.h \ protocol.h \ pubkey.h \ random.h \ @@ -303,8 +302,6 @@ libbitcoin_server_a_SOURCES = \ notaries_staked.cpp \ noui.cpp \ notarisationdb.cpp \ - paymentdisclosure.cpp \ - paymentdisclosuredb.cpp \ policy/fees.cpp \ pow.cpp \ rest.cpp \ @@ -343,12 +340,10 @@ libbitcoin_proton_a_SOURCES = \ amqp/amqppublishnotifier.cpp endif -# wallet: zcashd, but only linked when wallet enabled +# wallet: komodod, but only linked when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ - utiltest.cpp \ - utiltest.h \ zcbenchmarks.cpp \ zcbenchmarks.h \ wallet/asyncrpcoperation_mergetoaddress.cpp \ @@ -357,10 +352,8 @@ libbitcoin_wallet_a_SOURCES = \ wallet/asyncrpcoperation_shieldcoinbase.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ - paymentdisclosure.cpp \ - paymentdisclosuredb.cpp \ + zcash/Note.cpp \ transaction_builder.cpp \ - wallet/rpcdisclosure.cpp \ wallet/rpcdump.cpp \ cc/CCtokens.cpp \ cc/CCassetsCore.cpp \ @@ -436,7 +429,6 @@ libbitcoin_common_a_SOURCES = \ metrics.cpp \ primitives/block.cpp \ primitives/transaction.cpp \ - primitives/nonce.cpp \ protocol.cpp \ pubkey.cpp \ scheduler.cpp \ @@ -496,7 +488,7 @@ libbitcoin_cli_a_SOURCES = \ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # -# bitcoind binary # +# komodod binary # komodod_SOURCES = bitcoind.cpp komodod_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) komodod_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -555,7 +547,7 @@ if TARGET_DARWIN komodod_LDFLAGS += -static-libgcc endif -# bitcoin-cli binary # +# komodo-cli binary # komodo_cli_SOURCES = bitcoin-cli.cpp komodo_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) komodo_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -603,7 +595,7 @@ wallet_utility_LDADD = \ $(LIBCRYPTOCONDITIONS) endif -# zcash-tx binary # +# komodo-tx binary # komodo_tx_SOURCES = komodo-tx.cpp komodo_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) komodo_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -613,7 +605,6 @@ if TARGET_WINDOWS komodo_tx_SOURCES += bitcoin-tx-res.rc endif -# FIXME: Is libzcash needed for hush-tx ? komodo_tx_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_COMMON) \ @@ -625,7 +616,6 @@ komodo_tx_LDADD = \ $(LIBCRYPTOCONDITIONS) komodo_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) -# # zcash protocol primitives # libzcash_a_SOURCES = \ @@ -661,7 +651,6 @@ libzcashconsensus_la_SOURCES = \ crypto/sha512.cpp \ hash.cpp \ primitives/transaction.cpp \ - primitives/nonce.cpp \ pubkey.cpp \ script/zcashconsensus.cpp \ script/interpreter.cpp \ diff --git a/src/amount.h b/src/amount.h index be1c39a6e..c5f49593c 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index e9c0dd056..d7a5c0afe 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -193,9 +193,10 @@ unsigned int base_uint::bits() const { for (int pos = WIDTH - 1; pos >= 0; pos--) { if (pn[pos]) { - for (int bits = 31; bits > 0; bits--) { - if (pn[pos] & 1 << bits) + for (size_t bits = 31; bits > 0; bits--) { + if (pn[pos] & (1U << bits)) { return 32 * pos + bits + 1; + } } return 32 * pos + 1; } diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 364953148..67440f3da 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -1,3 +1,4 @@ +// Copyright © 2019-2020 The Hush Developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * diff --git a/src/cc/dapps/Makefile b/src/cc/dapps/Makefile index 6e7874788..1fea27084 100644 --- a/src/cc/dapps/Makefile +++ b/src/cc/dapps/Makefile @@ -1,6 +1,10 @@ +# Copyright 2020 The Hush Developers # just type make to compile all dapps all: zmigrate oraclefeed +subatomic: + $(CC) subatomic.c -o subatomic -lm + zmigrate: $(CC) zmigrate.c -o zmigrate -lm @@ -9,3 +13,4 @@ oraclefeed: clean: rm zmigrate oraclefeed + diff --git a/src/cc/dapps/dappinc.h b/src/cc/dapps/dappinc.h new file mode 100644 index 000000000..3c71dfdec --- /dev/null +++ b/src/cc/dapps/dappinc.h @@ -0,0 +1,1599 @@ +/****************************************************************************** + * Copyright © 2014-2020 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include +#include +#include +#include +#include "cJSON.c" + +bits256 zeroid; + +int32_t unstringbits(char *buf,uint64_t bits) +{ + int32_t i; + for (i=0; i<8; i++,bits>>=8) + if ( (buf[i]= (char)(bits & 0xff)) == 0 ) + break; + buf[i] = 0; + return(i); +} + +uint64_t stringbits(char *str) +{ + uint64_t bits = 0; + if ( str == 0 ) + return(0); + int32_t i,n = (int32_t)strlen(str); + if ( n > 8 ) + n = 8; + for (i=n-1; i>=0; i--) + bits = (bits << 8) | (str[i] & 0xff); + //printf("(%s) -> %llx %llu\n",str,(long long)bits,(long long)bits); + return(bits); +} + +char hexbyte(int32_t c) +{ + c &= 0xf; + if ( c < 10 ) + return('0'+c); + else if ( c < 16 ) + return('a'+c-10); + else return(0); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(unsigned char *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *clonestr(char *str) +{ + char *clone; + if ( str == 0 || str[0]==0) + { + printf("warning cloning nullstr.%p\n",str); + //#ifdef __APPLE__ + // while ( 1 ) sleep(1); + //#endif + str = (char *)""; + } + clone = (char *)malloc(strlen(str)+16); + strcpy(clone,str); + return(clone); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i0; i--) + str[i] = str[i-1]; + str[0] = '/'; + str[n+1] = 0; + }*/ +#endif + return(str); +#endif +} + +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) +{ + FILE *fp; + long filesize,buflen = *allocsizep; + uint8_t *buf = *bufp; + *lenp = 0; + if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) + { + fseek(fp,0,SEEK_END); + filesize = ftell(fp); + if ( filesize == 0 ) + { + fclose(fp); + *lenp = 0; + //printf("loadfile null size.(%s)\n",fname); + return(0); + } + if ( filesize > buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +void *filestr(long *allocsizep,char *_fname) +{ + long filesize = 0; char *fname,*buf = 0; void *retptr; + *allocsizep = 0; + fname = malloc(strlen(_fname)+1); + strcpy(fname,_fname); + retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); + free(fname); + return(retptr); +} + +char *send_curl(char *url,char *fname) +{ + long fsize; char curlstr[1024]; + sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); + system(curlstr); + return(filestr(&fsize,fname)); +} + +cJSON *get_urljson(char *url,char *fname) +{ + char *jsonstr; cJSON *json = 0; + if ( (jsonstr= send_curl(url,fname)) != 0 ) + { + //printf("(%s) -> (%s)\n",url,jsonstr); + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + return(json); +} + +////////////////////////////////////////////// +// start of dapp +////////////////////////////////////////////// +int md_unlink(char *file) +{ +#ifdef _WIN32 + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +char *REFCOIN_CLI,DPOW_pubkeystr[67],DPOW_secpkeystr[67],DPOW_handle[67],DPOW_recvaddr[64],DPOW_recvZaddr[128]; + +cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6) +{ + long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[32768]; + sprintf(fname,"/tmp/notarizer_%s_%d",method,(rand() >> 17) % 10000); + //if ( (acname == 0 || acname[0] == 0) && strcmp(refcoin,"KMD") != 0 ) + // acname = refcoin; + if ( acname[0] != 0 ) + { + if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 && strcmp(refcoin,acname) != 0 ) + printf("unexpected: refcoin.(%s) acname.(%s)\n",refcoin,acname); + sprintf(cmdstr,"komodo-cli -ac_name=%s %s %s %s %s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); + } + else if ( strcmp(refcoin,"KMD") == 0 ) + sprintf(cmdstr,"komodo-cli %s %s %s %s %s %s %s %s > %s\n",method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); + else if ( REFCOIN_CLI != 0 && REFCOIN_CLI[0] != 0 ) + { + sprintf(cmdstr,"%s %s %s %s %s %s %s %s %s > %s\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); + //printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); + } +//fprintf(stderr,"system(%s)\n",cmdstr); + system(cmdstr); + *retstrp = 0; + if ( (jsonstr= filestr(&fsize,fname)) != 0 ) + { + jsonstr[strlen(jsonstr)-1]='\0'; + //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr); + if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) + *retstrp = jsonstr; + else free(jsonstr); + md_unlink(fname); + } //else fprintf(stderr,"system(%s) -> NULL\n",cmdstr); + return(retjson); +} + +cJSON *subatomic_cli(char *clistr,char **retstrp,char *method,char *arg0,char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6) +{ + long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[32768]; + sprintf(fname,"/tmp/subatomic_%s_%d",method,(rand() >> 17) % 10000); + sprintf(cmdstr,"%s %s %s %s %s %s %s %s %s > %s\n",clistr,method,arg0,arg1,arg2,arg3,arg4,arg5,arg6,fname); +//fprintf(stderr,"system(%s)\n",cmdstr); + system(cmdstr); + *retstrp = 0; + if ( (jsonstr= filestr(&fsize,fname)) != 0 ) + { + jsonstr[strlen(jsonstr)-1]='\0'; + //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr); + if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) + *retstrp = jsonstr; + else free(jsonstr); + md_unlink(fname); + } //else fprintf(stderr,"system(%s) -> NULL\n",cmdstr); + return(retjson); +} + +bits256 komodobroadcast(char *refcoin,char *acname,cJSON *hexjson) +{ + char *hexstr,*retstr,str[65]; cJSON *retjson; bits256 txid; + memset(txid.bytes,0,sizeof(txid)); + if ( (hexstr= jstr(hexjson,"hex")) != 0 ) + { + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendrawtransaction",hexstr,"","","","","","")) != 0 ) + { + //fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(txid.bytes,32,retstr); + } + fprintf(stderr,"broadcast %s txid.(%s)\n",strlen(acname)>0?acname:refcoin,bits256_str(str,txid)); + free(retstr); + } + } + return(txid); +} + +bits256 sendtoaddress(char *refcoin,char *acname,char *destaddr,int64_t satoshis,char *oprethexstr) +{ + char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; + memset(txid.bytes,0,sizeof(txid)); + sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendtoaddress",destaddr,numstr,"false","","",oprethexstr,"")) != 0 ) + { + fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(txid.bytes,32,retstr); + } + fprintf(stderr,"sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); + free(retstr); + } + return(txid); +} + +bits256 tokentransfer(char *refcoin,char *acname,char *tokenid,char *destpub,int64_t units) +{ + char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; + memset(txid.bytes,0,sizeof(txid)); + sprintf(numstr,"%llu",(long long)units); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"tokentransfer",tokenid,destpub,numstr,"","","","")) != 0 ) + { + txid = komodobroadcast(refcoin,acname,retjson); + fprintf(stderr,"tokentransfer returned (%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"tokentransfer.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(txid); +} + +char *get_tokenaddress(char *refcoin,char *acname,char *tokenaddr) +{ + char *retstr,*str; cJSON *retjson; + tokenaddr[0] = 0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"tokenaddress","","","","","","","")) != 0 ) + { + if ( (str= jstr(retjson,"myCCAddress(Tokens)")) != 0 ) + { + strcpy(tokenaddr,str); + fprintf(stderr,"tokenaddress returned (%s)\n",tokenaddr); + free_json(retjson); + return(tokenaddr); + } + free_json(retjson); + } + else if ( retstr != 0 ) + { + //fprintf(stderr,"tokentransfer.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(0); +} + +int64_t get_tokenbalance(char *refcoin,char *acname,char *tokenid) +{ + cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"tokenbalance",tokenid,"","","","","","")) != 0 ) + { + amount = j64bits(retjson,"balance"); + fprintf(stderr,"tokenbalance %llu\n",(long long)amount); + free_json(retjson); + } + else if ( retstr != 0 ) + { + //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); + free(retstr); + } + return (amount); +} + +cJSON *get_decodescript(char *refcoin,char *acname,char *script) +{ + cJSON *retjson; char *retstr; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"decodescript",script,"","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_decodescript.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(0); +} + +char *get_createmultisig2(char *refcoin,char *acname,char *msigaddr,char *redeemscript,char *pubkeyA,char *pubkeyB) +{ + //char para 2 '["02c3af47b51a506b08b4ededb156cb4c3f9db9e0ac7ad27b8623c08a056fdcc220", "038e61fbface549a850862f12ed99b7cbeef5c2bd2d8f1daddb34809416f0259e1"]' + cJSON *retjson; char *retstr,*str,params[256]; int32_t height=0; + msigaddr[0] = 0; + redeemscript[0] = 0; + sprintf(params,"'[\"%s\", \"%s\"]'",pubkeyA,pubkeyB); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"createmultisig","2",params,"","","","","")) != 0 ) + { + if ( (str= jstr(retjson,"address")) != 0 ) + strcpy(msigaddr,str); + if ( (str= jstr(retjson,"redeemScript")) != 0 ) + strcpy(redeemscript,str); + free_json(retjson); + if ( msigaddr[0] != 0 && redeemscript[0] != 0 ) + return(msigaddr); + else return(0); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"%s get_createmultisig2.(%s) error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +int32_t get_coinheight(char *refcoin,char *acname,bits256 *blockhashp) +{ + cJSON *retjson; char *retstr; int32_t height=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockchaininfo","","","","","","","")) != 0 ) + { + height = jint(retjson,"blocks"); + *blockhashp = jbits256(retjson,"bestblockhash"); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(height); +} + +bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height) +{ + cJSON *retjson; char *retstr,heightstr[32]; bits256 hash; + memset(hash.bytes,0,sizeof(hash)); + sprintf(heightstr,"%d",height); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","","","","")) != 0 ) + { + fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(hash.bytes,32,retstr); + } + free(retstr); + } + return(hash); +} + +bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash,uint32_t *blocktimep) +{ + cJSON *retjson; char *retstr,str[65]; bits256 merkleroot; + memset(merkleroot.bytes,0,sizeof(merkleroot)); + *blocktimep = 0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","","","","")) != 0 ) + { + merkleroot = jbits256(retjson,"merkleroot"); + *blocktimep = juint(retjson,"time"); + //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(merkleroot); +} + +uint32_t get_heighttime(char *refcoin,char *acname,int32_t height) +{ + bits256 blockhash; uint32_t blocktime; + blockhash = get_coinblockhash(refcoin,acname,height); + get_coinmerkleroot(refcoin,acname,blockhash,&blocktime); + return(blocktime); +} + +int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) +{ + int32_t height = 0; char str[65]; bits256 bhash; uint32_t blocktime; + if ( prevheight == 0 ) + height = get_coinheight(refcoin,acname,&bhash) - 20; + else height = prevheight + 1; + if ( height > 0 ) + { + *blockhashp = get_coinblockhash(refcoin,acname,height); + if ( bits256_nonz(*blockhashp) != 0 ) + { + *merklerootp = get_coinmerkleroot(refcoin,acname,*blockhashp,&blocktime); + if ( bits256_nonz(*merklerootp) != 0 ) + return(height); + } + } + memset(blockhashp,0,sizeof(*blockhashp)); + memset(merklerootp,0,sizeof(*merklerootp)); + return(0); +} + +cJSON *get_rawmempool(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawmempool","","","","","","","")) != 0 ) + { + //printf("mempool.(%s)\n",jprint(retjson,0)); + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_rawmempool.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) +{ + cJSON *retjson; char *retstr,jsonbuf[256]; + if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) + printf("warning: assumes %s has addressindex enabled\n",refcoin); + sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","","","","")) != 0 ) + { + //printf("addressutxos.(%s)\n",jprint(retjson,0)); + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_z_viewtransaction(char *refcoin,char *acname,bits256 txid) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_viewtransaction",bits256_str(str,txid),"","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_z_viewtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_listunspent(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"listunspent","","","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_listunspent.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_getinfo(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getinfo","","","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_getinfo.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *z_listunspent(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_listunspent","","","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_listunspent.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *z_listoperationids(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_listoperationids","","","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_listoperationids.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *z_getoperationstatus(char *refcoin,char *acname,char *opid) +{ + cJSON *retjson; char *retstr,str[65],params[512]; + sprintf(params,"'[\"%s\"]'",opid); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getoperationstatus",params,"","","","","","")) != 0 ) + { + //printf("got status (%s)\n",jprint(retjson,0)); + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_getoperationstatus.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *z_getoperationresult(char *refcoin,char *acname,char *opid) +{ + cJSON *retjson; char *retstr,str[65],params[512]; + sprintf(params,"'[\"%s\"]'",opid); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getoperationresult",params,"","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_getoperationresult.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* compare) +{ + cJSON *retjson; char *retstr; int32_t res=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","","","","")) != 0 ) + { + if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return (res); +} + +int32_t z_validateaddress(char *refcoin,char *acname,char *depositaddr, char *compare) +{ + cJSON *retjson; char *retstr; int32_t res=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_validateaddress",depositaddr,"","","","","","")) != 0 ) + { + if (is_cJSON_True(jobj(retjson,compare)) != 0 ) + res=1; + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return (res); +} + +int64_t get_getbalance(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getbalance","","","","","","","")) != 0 ) + { + fprintf(stderr,"get_getbalance.(%s) %s returned json!\n",refcoin,acname); + free_json(retjson); + } + else if ( retstr != 0 ) + { + amount = atof(retstr) * SATOSHIDEN; + sprintf(cmpstr,"%.8f",dstr(amount)); + if ( strcmp(retstr,cmpstr) != 0 ) + amount++; + //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); + free(retstr); + } + return (amount); +} + +int64_t z_getbalance(char *refcoin,char *acname,char *coinaddr) +{ + cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getbalance",coinaddr,"","","","","","")) != 0 ) + { + fprintf(stderr,"z_getbalance.(%s) %s returned json!\n",refcoin,acname); + free_json(retjson); + } + else if ( retstr != 0 ) + { + amount = atof(retstr) * SATOSHIDEN; + sprintf(cmpstr,"%.8f",dstr(amount)); + if ( strcmp(retstr,cmpstr) != 0 ) + amount++; + //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); + free(retstr); + } + return (amount); +} + +int32_t z_exportkey(char *privkey,char *refcoin,char *acname,char *zaddr) +{ + cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; + privkey[0] = 0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_exportkey",zaddr,"","","","","","")) != 0 ) + { + fprintf(stderr,"z_exportkey.(%s) %s returned json!\n",refcoin,acname); + free_json(retjson); + return(-1); + } + else if ( retstr != 0 ) + { + //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); + strcpy(privkey,retstr); + free(retstr); + return(0); + } + return(-1); +} + +int32_t getnewaddress(char *coinaddr,char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr; int64_t amount=0; int32_t retval = -1; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getnewaddress","","","","","","","")) != 0 ) + { + fprintf(stderr,"getnewaddress.(%s) %s returned json!\n",refcoin,acname); + free_json(retjson); + } + else if ( retstr != 0 ) + { + strcpy(coinaddr,retstr); + free(retstr); + retval = 0; + } + return(retval); +} + +int32_t z_getnewaddress(char *coinaddr,char *refcoin,char *acname,char *typestr) +{ + cJSON *retjson; char *retstr; int64_t amount=0; int32_t retval = -1; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getnewaddress",typestr,"","","","","","")) != 0 ) + { + fprintf(stderr,"z_getnewaddress.(%s) %s returned json!\n",refcoin,acname); + free_json(retjson); + } + else if ( retstr != 0 ) + { + strcpy(coinaddr,retstr); + free(retstr); + retval = 0; + } + return(retval); +} + +int64_t find_onetime_amount(char *coinstr,char *coinaddr) +{ + cJSON *array,*item; int32_t i,n; char *addr; int64_t amount = 0; + coinaddr[0] = 0; + if ( (array= get_listunspent(coinstr,"")) != 0 ) + { + //printf("got listunspent.(%s)\n",jprint(array,0)); + if ( (n= cJSON_GetArraySize(array)) > 0 ) + { + for (i=0; i 0 ) + { + for (i=0; i %s\n",coinstr,acname,srcaddr,params); + if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_sendmany",addr,params,"","","","","")) != 0 ) + { + printf("unexpected json z_sendmany.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_sendmany.(%s) -> opid.(%s)\n",coinstr,retstr); + strcpy(opidstr,retstr); + free(retstr); + retval = 0; + } + return(retval); +} + +int32_t z_mergetoaddress(char *opidstr,char *coinstr,char *acname,char *destaddr) +{ + cJSON *retjson; char *retstr,addr[128],*opstr; int32_t retval = -1; + sprintf(addr,"[\\\"ANY_SPROUT\\\"]"); + if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_mergetoaddress",addr,destaddr,"","","","","")) != 0 ) + { + if ( (opstr= jstr(retjson,"opid")) != 0 ) + strcpy(opidstr,opstr); + retval = jint(retjson,"remainingNotes"); + fprintf(stderr,"%s\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_mergetoaddress.(%s) -> opid.(%s)\n",coinstr,retstr); + strcpy(opidstr,retstr); + free(retstr); + } + return(retval); +} + +int32_t empty_mempool(char *coinstr,char *acname) +{ + cJSON *array; int32_t n; + if ( (array= get_rawmempool(coinstr,acname)) != 0 ) + { + if ( (n= cJSON_GetArraySize(array)) > 0 ) + return(0); + free_json(array); + return(1); + } + return(-1); +} + +cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required) +{ + cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,n,v; int64_t satoshis; bits256 txid; + *totalp = 0; + if ( (n= cJSON_GetArraySize(unspents)) > 0 ) + { + for (i=0; i= required ) + break; + } + } + } + return(vins); +} + +int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinaddr) +{ + cJSON *txobj,*vouts,*vout,*vins,*vin,*sobj,*addresses; char *addr,str[65]; int32_t i,j,n,numarray,retval = 0, hasvout=0; + if ( (txobj= get_rawtransaction(refcoin,acname,txid)) != 0 ) + { + if ( (vouts= jarray(&numarray,txobj,"vout")) != 0 ) + { + for (i=0; i 0 ) + { + for (i=0; i 0 ) + { + for (j=0; j 0 && strcmp(vinaddr,cmpaddr) == 0 ) + return(0); + printf("mismatched vinaddr.(%s) vs %s\n",vinaddr,cmpaddr); + } + } + return(-1); +} + +int32_t txid_in_vins(char *refcoin,bits256 txid,bits256 cmptxid) +{ + cJSON *txjson,*vins,*vin; int32_t numvins,v,vinvout; bits256 vintxid; char str[65]; + if ( (txjson= get_rawtransaction(refcoin,"",txid)) != 0 ) + { + if ( (vins= jarray(&numvins,txjson,"vin")) != 0 ) + { + for (v=0; v n.%d retval.%d\n",tagA,tagB,pubkeystr,n,retval); + } + free_json(retjson); + } + return(retval); +} + +int32_t dpow_hasmessage(char *payload,char *tagA,char *tagB,char *pubkeystr) +{ + cJSON *retjson,*item,*array; char *retstr,*pstr; int32_t i,n,retval = 0; + if ( (retjson= get_komodocli((char *)"",&retstr,DEXP2P_CHAIN,"DEX_list","0","0",tagA,tagB,pubkeystr,"","")) != 0 ) + { + if ( (array= jarray(&n,retjson,"matches")) != 0 ) + { + for (i=0; i 0 ) + { + ptrs = calloc(n,sizeof(*ptrs)); + for (i=0; ishorthash = juint(item,"id"); + ptrs[m]->jsonstr = ptr; + strcpy(ptrs[m]->senderpub,senderpub); + m++; + } + } + } + *nump = m; + } + free_json(retjson); + } + return(ptrs); +} + + diff --git a/src/cc/dapps/subatomic.c b/src/cc/dapps/subatomic.c new file mode 100644 index 000000000..2f6ebf824 --- /dev/null +++ b/src/cc/dapps/subatomic.c @@ -0,0 +1,1429 @@ +/****************************************************************************** + * Copyright © 2014-2020 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +// build subatomic and put in path: git pull; gcc cc/dapps/subatomic.c -lm -o subatomic; cp subatomic /usr/bin +// alice sends relcoin and gets basecoin + +#define DEXP2P_CHAIN ((char *)"DEX") +#define DEXP2P_PUBKEYS ((char *)"subatomic") +#include "dappinc.h" + +// for OTC mode, the following 4 functions are the only ones that should be needed to support a new "coin" +//int64_t subatomic_getbalance(char *coin); +//bits256 subatomic_coinpayment(int32_t OTCmode,char *coin,char *destaddr,uint64_t paytoshis,char *memostr); +//cJSON *subatomic_txidwait(char *coin,bits256 txid,char *hexstr,int32_t numseconds); +//int64_t subatomic_verifypayment(char *coin,cJSON *rawtx,uint64_t destsatoshis,char *destaddr); + +// TODO: +// address conversion +// new inventory types: +// anonsend + +// bob nodes: +// mutex for bob instances +// "deposits" messages and approved bobs +// volume caps per coin and non-notarized exposure + +// later: +// sharded storage + +#define SUBATOMIC_OTCDEFAULT 1 +#define SUBATOMIC_TIMEOUT 60 +#define SUBATOMIC_LOCKTIME 3600 +#define SUBATOMIC_TXFEE 10000 + +#define SUBATOMIC_PRIORITY 5 + +#define SUBATOMIC_OPENREQUEST 1 +#define SUBATOMIC_APPROVED 2 +#define SUBATOMIC_OPENED 3 +#define SUBATOMIC_PAYMENT 4 +#define SUBATOMIC_PAIDINFULL 5 +#define SUBATOMIC_CLOSED 6 + +cJSON *SUBATOMIC_json; +int32_t SUBATOMIC_retval = -1; + +struct abinfo +{ + char pubkey[67],recvaddr[64],recvZaddr[128],secp[67]; +}; + +struct coininfo +{ + uint64_t satoshis,txfee,maxamount; + char istoken,iszaddr,isfile,isexternal,tokenid[65],coin[16],name[16],cli[256],acname[16],coinstr[16]; +}; + +struct msginfo +{ + UT_hash_handle hh; + bits256 bobpayment,alicepayment; + double price; + uint64_t gotpayment; + uint32_t origid,openrequestid,approvalid,openedid,paymentids[100],paidid,closedid,locktime; + int32_t bobflag,status,OTCmode; + char payload[128],approval[128],senderpub[67],msigaddr[64],redeemscript[256]; + struct coininfo base,rel; + struct abinfo alice,bob; +} *Messages; + +uint64_t subatomic_txfee(char *coin) +{ + return(SUBATOMIC_TXFEE); +} + +char *subatomic_checkname(char *tmpstr,struct msginfo *mp,int32_t baserel,char *coin) +{ + int32_t i,n; cJSON *external,*item; char *coinstr,*clistr; struct coininfo *ptr; + ptr = (baserel == 0) ? &mp->base : &mp->rel; + if ( coin[0] == 0 ) + return(coin); + if ( (external= jarray(&n,SUBATOMIC_json,"externalcoins")) != 0 && n > 0 ) + { + for (i=0; icli) ) + { + ptr->isexternal = 1; + strcpy(ptr->cli,clistr); + //fprintf(stderr,"found external coin %s %s\n",coin,clistr); + } + } + } + if ( coin[0] == '#' ) + { + strcpy(ptr->coinstr,coin); + strcpy(ptr->acname,""); + ptr->isfile = 1; + return(coin); + } + else if ( coin[0] != 'z' ) + { + for (i=1; coin[i]!=0; i++) + if ( coin[i] == '.' ) + { + dpow_tokenregister(ptr->tokenid,0,coin,0); + if ( ptr->tokenid[0] != 0 ) + { + strcpy(tmpstr,coin); + tmpstr[i] = 0; + //fprintf(stderr,"found a tokenmap %s -> %s %s\n",coin,tmpstr,ptr->tokenid); + ptr->istoken = 1; + strcpy(ptr->acname,coin); + strcpy(ptr->coinstr,""); + return(tmpstr); + } + } + if ( ptr->isexternal == 0 ) + { + if ( strcmp(coin,"KMD") != 0 ) + { + strcpy(ptr->acname,coin); + strcpy(ptr->coinstr,""); + } + else + { + strcpy(ptr->coinstr,coin); + strcpy(ptr->acname,""); + } + } + else + { + strcpy(ptr->coinstr,coin); + strcpy(ptr->acname,""); + } + return(coin); + } + else + { + for (i=1; coin[i]!=0; i++) + if ( isupper(coin[i]) == 0 ) + return(coin); + if ( strcmp(coin+1,"KMD") != 0 ) + ptr->iszaddr = 1; + return(coin+1); + } +} + +int32_t subatomic_zonly(struct coininfo *coin) +{ + if ( strcmp(coin->coin,"PIRATE") == 0 ) + return(1); + else return(coin->iszaddr); +} + +// //////////////////////////////// the four key functions needed to support a new item for subatomics + +int64_t _subatomic_getbalance(struct coininfo *coin) +{ + cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; + if ( (retjson= subatomic_cli(coin->cli,&retstr,"getbalance","","","","","","","")) != 0 ) + { + fprintf(stderr,"_subatomic_getbalance.(%s) %s returned json!\n",coin->coinstr,coin->cli); + free_json(retjson); + } + else if ( retstr != 0 ) + { + amount = atof(retstr) * SATOSHIDEN; + sprintf(cmpstr,"%.8f",dstr(amount)); + if ( strcmp(retstr,cmpstr) != 0 ) + amount++; + //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); + free(retstr); + } + return (amount); +} + +bits256 _subatomic_sendtoaddress(struct coininfo *coin,char *destaddr,int64_t satoshis) +{ + char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; + memset(txid.bytes,0,sizeof(txid)); + sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); + if ( (retjson= subatomic_cli(coin->cli,&retstr,"sendtoaddress",destaddr,numstr,"false","","","","")) != 0 ) + { + fprintf(stderr,"unexpected _subatomic_sendtoaddress json.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(txid.bytes,32,retstr); + } + fprintf(stderr,"_subatomic_sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); + free(retstr); + } + return(txid); +} + +cJSON *_subatomic_rawtransaction(struct coininfo *coin,bits256 txid) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= subatomic_cli(coin->cli,&retstr,"getrawtransaction",bits256_str(str,txid),"1","","","","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"_subatomic_rawtransaction.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr); + free(retstr); + } + return(0); +} + +int64_t subatomic_getbalance(struct coininfo *coin) +{ + char *coinstr,*acname=""; FILE *fp; int64_t retval = 0; + if ( strcmp(coin->coin,"KMD") != 0 ) + { + acname = coin->coin; + coinstr = ""; + } else coinstr = coin->coin; + if ( coin->isfile != 0 ) + { + if ( (fp= fopen(coin->name+1,"rb")) != 0 ) // if alice, add bob pubkey to fname + { + fclose(fp); + retval = SATOSHIDEN; + } + return(retval); + } + else if ( subatomic_zonly(coin) != 0 ) + return(z_getbalance(coinstr,acname,DPOW_recvZaddr)); + else + { + if ( coin->istoken != 0 ) + { + if ( get_getbalance(coinstr,acname) < SUBATOMIC_TXFEE ) + { + fprintf(stderr,"not enough balance to send token\n"); + return(0); + } + //fprintf(stderr,"token balance %s\n",coin->tokenid); + return(get_tokenbalance(coinstr,acname,coin->tokenid) * SATOSHIDEN); + } + else if ( coin->isexternal == 0 ) + return(get_getbalance(coinstr,acname)); + else return(_subatomic_getbalance(coin)); + } +} + +bits256 subatomic_coinpayment(uint32_t origid,int32_t OTCmode,struct coininfo *coin,char *destaddr,uint64_t paytoshis,char *memostr,char *destpub,char *senderpub) +{ + bits256 txid; char opidstr[128],opretstr[32],str[65],*status,*coinstr,*acname=""; cJSON *retjson,*retjson2,*item,*res; int32_t i,pending=0; + memset(&txid,0,sizeof(txid)); + if ( OTCmode == 0 ) + { + fprintf(stderr,"micropayment channels are not supported yet\n"); + return(txid); + } + if ( coin->isfile != 0 ) + { + fprintf(stderr,"start broadcast of (%s)\n",coin->coin+1); + if ( (retjson= dpow_publish(SUBATOMIC_PRIORITY,coin->coin+1)) != 0 ) // spawn thread + { + sprintf(opretstr,"%08x",juint(retjson,"id")); + sprintf(opidstr,"%u",origid); + if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,opretstr,"inbox",opidstr,senderpub,"","")) != 0 ) + free_json(retjson2); + fprintf(stderr,"broadcast file.(%s) and send id.%u to alice (%s)\n",coin->coin+1,juint(retjson,"id"),jprint(retjson,0)); + txid = jbits256(retjson,"filehash"); + free_json(retjson); + } + fprintf(stderr,"end broadcast of (%s) to %s\n",coin->coin+1,senderpub); + return(txid); + } + else if ( subatomic_zonly(coin) != 0 ) + { + if ( memostr[0] == 0 ) + memostr = "beef"; + z_sendmany(opidstr,"",coin->coin,DPOW_recvZaddr,destaddr,paytoshis,memostr); + for (i=0; icoin,opidstr)) != 0 ) + { + item = jitem(retjson,0); + if ( (status= jstr(item,"status")) != 0 ) + { + if ( strcmp(status,"executing") == 0 ) + pending++; + else + { + res = jobj(item,"result"); + txid = jbits256(res,"txid"); + //fprintf(stderr,"got Ztx txid.%s\n",bits256_str(str,txid)); + free_json(retjson); + break; + } + /*else if ( clearresults != 0 ) + { + if ( (result= z_getoperationresult(coinstr,"",jstri(array,i))) != 0 ) + { + free_json(result); + } + }*/ + } + free_json(retjson); + } + sleep(1); + } + if ( i == 60 ) + printf("%u timed out waiting for opid to finish\n",origid); + } + else + { + if ( strcmp(coin->coin,"KMD") != 0 ) + { + acname = coin->coin; + coinstr = ""; + } else coinstr = coin->coin; + if ( coin->istoken != 0 ) + txid = tokentransfer(coinstr,acname,coin->tokenid,destpub,paytoshis/SATOSHIDEN); + else if ( coin->isexternal == 0 ) + { + sprintf(opretstr,"%08x",origid); + txid = sendtoaddress(coinstr,acname,destaddr,paytoshis,opretstr); + } else txid = _subatomic_sendtoaddress(coin,destaddr,paytoshis); + printf("%u got txid.%s\n",origid,bits256_str(str,txid)); + } + return(txid); +} + +cJSON *subatomic_txidwait(struct coininfo *coin,bits256 txid,char *hexstr,int32_t numseconds,char *senderpub) +{ + int32_t i,zflag; char *coinstr,str[65],*acname=""; cJSON *rawtx; bits256 z; bits256 filehash; + memset(&z,0,sizeof(z)); + if ( memcmp(&z,&txid,sizeof(txid)) == 0 ) + return(0); + if ( hexstr != 0 && hexstr[0] != 0 ) // probably not worth doing and zaddr is a problem to decode + { + // compare against txid + // if matches, sendrawtransaction if OTC mode, decoode and return if channels mode + } + zflag = (subatomic_zonly(coin) != 0); + if ( strcmp(coin->coin,"KMD") != 0 ) + { + acname = coin->coin; + coinstr = ""; + } else coinstr = coin->coin; + for (i=0; iisfile != 0 ) + { + if ( (rawtx= dpow_subscribe(SUBATOMIC_PRIORITY,coin->coin+1,senderpub)) != 0 ) + { + filehash = jbits256(rawtx,"filehash"); + if ( memcmp(&filehash,&txid,sizeof(filehash)) != 0 ) + { + fprintf(stderr,"waiting (%s) (%s)\n",coin->coin+1,jprint(rawtx,0)); + free_json(rawtx); + rawtx = 0; + } else return(rawtx); + } + } + else if ( zflag != 0 ) + rawtx = get_z_viewtransaction(coinstr,acname,txid); + else if ( coin->isexternal == 0 ) + rawtx = get_rawtransaction(coinstr,acname,txid); + else rawtx = _subatomic_rawtransaction(coin,txid); + if ( rawtx != 0 ) + return(rawtx); + sleep(1); + } + printf("%s/%s timeout waiting for %s\n",coin->name,coin->coin,bits256_str(str,txid)); + return(0); +} + +int64_t subatomic_verifypayment(struct coininfo *coin,cJSON *rawtx,uint64_t destsatoshis,char *destaddr,bits256 txid) +{ + int32_t i,n,m,valid=0; bits256 tokenid,filehash,checkhash; cJSON *array,*item,*sobj,*a; char *addr,*acname,*coinstr,tokenaddr[64],*hex; uint8_t hexbuf[512],pub33[33]; uint64_t netval,recvsatoshis = 0; + if ( coin->isfile != 0 ) + { + filehash = jbits256(rawtx,"filehash"); + checkhash = jbits256(rawtx,"checkhash"); + if ( memcmp(&txid,&filehash,sizeof(txid)) == 0 && memcmp(&txid,&checkhash,sizeof(txid)) == 0 ) + { + fprintf(stderr,"verified file is matching the filehash (%s)\n",jprint(rawtx,0)); + return(SATOSHIDEN); + } else return(0); + } + else if ( subatomic_zonly(coin) != 0 ) + { + if ( (array= jarray(&n,rawtx,"outputs")) != 0 && n > 0 ) + { + for (i=0; iistoken != 0 ) + { + if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 ) + { + item = jitem(array,0); + if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (a= jarray(&m,sobj,"addresses")) != 0 && m == 1 ) + { + if ( strcmp(coin->coin,"KMD") != 0 ) + { + acname = coin->coin; + coinstr = ""; + } else coinstr = coin->coin; + if ( get_tokenaddress(coinstr,acname,tokenaddr) != 0 ) + { + //fprintf(stderr,"tokenaddr.%s\n",tokenaddr); + if ( (addr= jstri(a,0)) != 0 && strcmp(addr,tokenaddr) == 0 ) + recvsatoshis += SATOSHIDEN * (uint64_t)(jdouble(item,"value")*SATOSHIDEN + 0.000000004999); + else fprintf(stderr,"miscompare (%s) vs %s\n",jprint(sobj,0),addr); + } + } + item = jitem(array,n-1); + if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (hex= jstr(sobj,"hex")) != 0 && (m= is_hexstr(hex,0)) > 1 && m/2 < sizeof(hexbuf) ) + { + m >>= 1; + decode_hex(hexbuf,m,hex); + decode_hex(tokenid.bytes,32,coin->tokenid); + decode_hex(pub33,33,DPOW_secpkeystr); + // opret 69len EVAL_TOKENS 't' tokenid 1 33 pub33 + if ( hexbuf[0] == 0x6a && hexbuf[1] == 0x45 && hexbuf[2] == 0xf2 && hexbuf[3] == 't' && memcmp(&hexbuf[4],&tokenid,sizeof(tokenid)) == 0 && hexbuf[4+32] == 1 && hexbuf[4+32+1] == 33 && memcmp(&hexbuf[4+32+2],pub33,33) == 0 ) + { + valid = 1; + //fprintf(stderr,"validated it is a token transfer!\n"); + } else fprintf(stderr,"need to validate tokentransfer.(%s) %s %d\n",hex,DPOW_secpkeystr,memcmp(&hexbuf[4+32+2],pub33,33) == 0); + //6a 45 f2 74 2b1feef719ecb526b07416dd432bce603ac6dc8bfe794cddf105cb52f6aae3cd 01 21 02b27de3ee5335518b06f69f4fbabb029cfc737613b100996841d5532b324a5a61 + + } + recvsatoshis *= valid; + } + } + else + { + if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 ) + { + for (i=0; iorigid = origid; + HASH_ADD(hh,Messages,origid,sizeof(origid),mp); + return(mp); +} + +int32_t subatomic_status(struct msginfo *mp,int32_t status) +{ + static FILE *fp; + if ( fp == 0 ) + { + int32_t i,oid,s,n,num,count; struct msginfo *m; long fsize; + if ( (fp= fopen("SUBATOMIC.DB","rb+")) == 0 ) + { + if ( (fp= fopen("SUBATOMIC.DB","wb")) == 0 ) + { + fprintf(stderr,"cant create SUBATOMIC.DB\n"); + exit(-1); + } + } + else + { + fseek(fp,0,SEEK_END); + fsize = ftell(fp); + if ( (fsize % (sizeof(uint32_t)*2)) != 0 ) + { + fprintf(stderr,"SUBATOMIC.DB illegal filesize.%ld\n",fsize); + exit(-1); + } + n = (int32_t)(fsize / (sizeof(uint32_t)*2)); + rewind(fp); + for (i=num=count=0; i SUBATOMIC_CLOSED ) + { + fprintf(stderr,"SUBATOMIC.DB corrupted at filepos.%ld: illegal status.%d\n",ftell(fp),s); + exit(-1); + } + //fprintf(stderr,"%u <- %d\n",oid,s); + if ( (m= subatomic_find(oid)) == 0 ) + { + m = subatomic_add(oid); + count++; + } + if ( s > m->status ) + { + m->status = s; + num++; + } + } + fprintf(stderr,"initialized %d messages, updated %d out of total.%d\n",count,num,n); + } + } + if ( mp->status >= status ) + return(-1); + if ( fwrite(&mp->origid,1,sizeof(mp->origid),fp) != sizeof(mp->origid) || fwrite(&status,1,sizeof(status),fp) != sizeof(status) ) + fprintf(stderr,"error updating SUBATOMIC.DB, risk of double spends\n"); + fflush(fp); + mp->status = status; + return(0); +} + +struct msginfo *subatomic_tracker(uint32_t origid) +{ + struct msginfo *mp; + if ( (mp= subatomic_find(origid)) == 0 ) + { + mp = subatomic_add(origid); + subatomic_status(mp,0); + } + return(mp); +} + +char *subatomic_hexstr(char *jsonstr) +{ + char *hexstr; int32_t i,c,n = (int32_t)strlen(jsonstr); + hexstr = malloc(2*n + 3); + strcpy(hexstr,jsonstr); + for (i=0; iorigid); + jaddnum(item,"price",mp->price); + jaddnum(item,"openrequest",mp->openrequestid); + jaddstr(item,"base",mp->base.name); + jaddstr(item,"basecoin",mp->base.coin); + jadd64bits(item,"basesatoshis",mp->base.satoshis); + jadd64bits(item,"basetxfee",mp->base.txfee); + jadd64bits(item,"maxbaseamount",mp->base.maxamount); + jaddstr(item,"rel",mp->rel.name); + jaddstr(item,"relcoin",mp->rel.coin); + jadd64bits(item,"relsatoshis",mp->rel.satoshis); + jadd64bits(item,"reltxfee",mp->rel.txfee); + jadd64bits(item,"maxrelamount",mp->rel.maxamount); + jaddstr(item,"alice",mp->alice.pubkey); + jaddstr(item,"alicesecp",mp->alice.secp); + jaddstr(item,"bob",mp->bob.pubkey); + jaddstr(item,"bobsecp",mp->bob.secp); + if ( subatomic_zonly(&mp->rel) != 0 ) + jaddstr(item,"bobZaddr",mp->bob.recvZaddr); + else jaddstr(item,"bobaddr",mp->bob.recvaddr); + if ( mp->rel.istoken != 0 ) + jaddstr(item,"bobtoken",mp->rel.tokenid); + if ( subatomic_zonly(&mp->base) != 0 ) + jaddstr(item,"aliceZaddr",mp->alice.recvZaddr); + else jaddstr(item,"aliceaddr",mp->alice.recvaddr); + if ( mp->base.istoken != 0 ) + jaddstr(item,"alicetoken",mp->base.tokenid); + return(item); +} + +uint64_t subatomic_orderbook_mpset(struct msginfo *mp,char *basecheck) +{ + cJSON *retjson; char *tagA,*tagB,*senderpub,*str,tmpstr[32]; int32_t matches=0; double volA,volB; int64_t txfee=0; + strcpy(mp->base.name,basecheck); + strcpy(mp->base.coin,subatomic_checkname(tmpstr,mp,0,basecheck)); + mp->rel.txfee = subatomic_txfee(mp->rel.coin); + if ( (retjson= dpow_get(mp->origid)) != 0 ) + { + //fprintf(stderr,"dpow_get.(%s) (%s/%s)\n",jprint(retjson,0),mp->base.coin,mp->rel.coin); + if ( (senderpub= jstr(retjson,"senderpub")) != 0 && is_hexstr(senderpub,0) == 66 && (tagA= jstr(retjson,"tagA")) != 0 && (tagB= jstr(retjson,"tagB")) != 0 && strncmp(tagB,mp->rel.name,strlen(mp->rel.name)) == 0 && strlen(tagA) < sizeof(mp->base.name) ) + { + strcpy(mp->base.name,tagA); + strcpy(mp->base.coin,subatomic_checkname(tmpstr,mp,0,tagA)); + if ( basecheck[0] == 0 || strncmp(basecheck,tagA,strlen(basecheck)) == 0 ) + matches = 1; + else if ( strcmp(tagA,mp->base.name) == 0 ) + matches = 1; + else if ( mp->bobflag != 0 && tagA[0] == '#' && strcmp(mp->base.name,"#allfiles") == 0 ) + matches = 1; + if ( matches != 0 ) + { + if ( (str= jstr(retjson,"decrypted")) != 0 && strlen(str) < 128 ) + strcpy(mp->payload,str); + mp->locktime = juint(retjson,"timestamp") + SUBATOMIC_LOCKTIME; + mp->base.txfee = subatomic_txfee(mp->base.coin); + strcpy(mp->senderpub,senderpub); + volB = jdouble(retjson,"amountB"); + volA = jdouble(retjson,"amountA"); + mp->base.maxamount = volA*SATOSHIDEN + 0.0000000049999; + mp->rel.maxamount = volB*SATOSHIDEN + 0.0000000049999; + if ( 0 && mp->rel.istoken == 0 ) + txfee = mp->rel.txfee; + if ( mp->base.maxamount != 0 && mp->rel.maxamount != 0 && volA > SMALLVAL && volB > SMALLVAL && mp->rel.satoshis <= mp->rel.maxamount ) + { + mp->price = volA / volB; + mp->base.satoshis = (mp->rel.satoshis - txfee) * mp->price; + //fprintf(stderr,"base satoshis.%llu\n",(long long)mp->base.satoshis); + } else fprintf(stderr,"%u rel %llu vs (%llu %llu)\n",mp->origid,(long long)mp->rel.satoshis,(long long)mp->base.maxamount,(long long)mp->rel.maxamount); + } else printf("%u didnt match (%s) tagA.%s %s, tagB.%s %s %d %d\n",mp->origid,basecheck,tagA,mp->base.name,tagB,mp->rel.name,tagA[0] == '#', strcmp(mp->base.name,"#allfiles") == 0); + } else printf("%u didnt compare tagA.%s %s, tagB.%s %s\n",mp->origid,tagA,mp->base.name,tagB,mp->rel.name); + free_json(retjson); + } + return(mp->base.satoshis); +} + +char *randhashstr(char *str) +{ + bits256 rands; int32_t i; + for (i=0; i<32; i++) + rands.bytes[i] = rand() >> 17; + bits256_str(str,rands); + return(str); +} + +void subatomic_extrafields(cJSON *dest,cJSON *src) +{ + char *str; + if ( (str= jstr(src,"approval")) != 0 ) + jaddstr(dest,"approval",str); + if ( (str= jstr(src,"opened")) != 0 ) + jaddstr(dest,"opened",str); + if ( (str= jstr(src,"payamount")) != 0 ) + jaddstr(dest,"payamount",str); + if ( (str= jstr(src,"destaddr")) != 0 ) + jaddstr(dest,"destaddr",str); + if ( (str= jstr(src,"bobpayment")) != 0 ) + jaddstr(dest,"bobpayment",str); + if ( (str= jstr(src,"alicepayment")) != 0 ) + jaddstr(dest,"alicepayment",str); + if ( (str= jstr(src,"bobaddr")) != 0 ) + jaddstr(dest,"bobaddr",str); + if ( (str= jstr(src,"bobZaddr")) != 0 ) + jaddstr(dest,"bobZaddr",str); + if ( (str= jstr(src,"aliceaddr")) != 0 ) + jaddstr(dest,"aliceaddr",str); + if ( (str= jstr(src,"aliceZaddr")) != 0 ) + jaddstr(dest,"aliceZaddr",str); + if ( (str= jstr(src,"alicetoken")) != 0 ) + jaddstr(dest,"alicetoken",str); + if ( (str= jstr(src,"bobtoken")) != 0 ) + jaddstr(dest,"bobtoken",str); +} + +char *subatomic_submit(cJSON *argjson,int32_t tobob) +{ + char *jsonstr,*hexstr; + jaddnum(argjson,"tobob",tobob != 0); + jsonstr = jprint(argjson,1); + hexstr = subatomic_hexstr(jsonstr); + free(jsonstr); + return(hexstr); +} + +#define SCRIPT_OP_IF 0x63 +#define SCRIPT_OP_ELSE 0x67 +#define SCRIPT_OP_DUP 0x76 +#define SCRIPT_OP_ENDIF 0x68 +#define SCRIPT_OP_TRUE 0x51 +#define SCRIPT_OP_2 0x52 +#define SCRIPT_OP_3 0x53 +#define SCRIPT_OP_DROP 0x75 +#define SCRIPT_OP_EQUALVERIFY 0x88 +#define SCRIPT_OP_HASH160 0xa9 +#define SCRIPT_OP_EQUAL 0x87 +#define SCRIPT_OP_CHECKSIG 0xac +#define SCRIPT_OP_CHECKMULTISIG 0xae +#define SCRIPT_OP_CHECKMULTISIGVERIFY 0xaf +#define SCRIPT_OP_CHECKLOCKTIMEVERIFY 0xb1 + +int32_t subatomic_redeemscript(char *redeemscript,uint32_t locktime,char *pubkeyA,char *pubkeyB) // not needed +{ + // if ( refund ) OP_HASH160 <2of2 multisig hash> OP_EQUAL // standard multisig + // else CLTV OP_DROP OP_CHECKSIG // standard spend + uint8_t pubkeyAbytes[33],pubkeyBbytes[33],hex[4096]; int32_t i,n = 0; + decode_hex(pubkeyAbytes,33,pubkeyA); + decode_hex(pubkeyBbytes,33,pubkeyB); + hex[n++] = SCRIPT_OP_IF; + hex[n++] = SCRIPT_OP_2; + hex[n++] = 33, memcpy(&hex[n],pubkeyAbytes,33), n += 33; + hex[n++] = 33, memcpy(&hex[n],pubkeyBbytes,33), n += 33; + hex[n++] = SCRIPT_OP_2; + hex[n++] = SCRIPT_OP_CHECKMULTISIG; + hex[n++] = SCRIPT_OP_ELSE; + hex[n++] = 4; + hex[n++] = locktime & 0xff, locktime >>= 8; + hex[n++] = locktime & 0xff, locktime >>= 8; + hex[n++] = locktime & 0xff, locktime >>= 8; + hex[n++] = locktime & 0xff; + hex[n++] = SCRIPT_OP_CHECKLOCKTIMEVERIFY; + hex[n++] = SCRIPT_OP_DROP; + hex[n++] = 33; memcpy(&hex[n],pubkeyAbytes,33); n += 33; + hex[n++] = SCRIPT_OP_CHECKSIG; + hex[n++] = SCRIPT_OP_ENDIF; + for (i=0; i>4) & 0xf); + redeemscript[i*2 + 1] = hexbyte(hex[i] & 0xf); + } + redeemscript[n*2] = 0; + /*tmpbuf[0] = SCRIPT_OP_HASH160; + tmpbuf[1] = 20; + calc_OP_HASH160(scriptPubKey,tmpbuf+2,redeemscript); + tmpbuf[22] = SCRIPT_OP_EQUAL; + init_hexbytes_noT(scriptPubKey,tmpbuf,23); + if ( p2shaddr != 0 ) + { + p2shaddr[0] = 0; + if ( (btc_addr= base58_encode_check(addrtype,true,tmpbuf+2,20)) != 0 ) + { + if ( strlen(btc_addr->str) < 36 ) + strcpy(p2shaddr,btc_addr->str); + cstr_free(btc_addr,true); + } + }*/ + return(n); +} + +int32_t subatomic_approved(struct msginfo *mp,cJSON *approval,cJSON *msgjson,char *senderpub) +{ + char *hexstr,numstr[32],redeemscript[1024],*coin,*acname=""; cJSON *retjson,*decodejson; int32_t i,retval = 0; + subatomic_extrafields(approval,msgjson); + if ( mp->OTCmode == 0 ) + { + coin = (mp->bobflag != 0) ? mp->base.coin : mp->rel.coin; // the other side gets this coin + if ( strcmp(coin,"KMD") != 0 ) + { + acname = coin; + coin = ""; + } + if ( get_createmultisig2(coin,acname,mp->msigaddr,mp->redeemscript,mp->alice.secp,mp->bob.secp) != 0 ) + { + subatomic_redeemscript(redeemscript,mp->locktime,mp->alice.secp,mp->bob.secp); + if ( (decodejson= get_decodescript(coin,acname,redeemscript)) != 0 ) + { + fprintf(stderr,"%s %s msigaddr.%s %s -> %s %s\n",mp->bobflag!=0?"bob":"alice",(mp->bobflag != 0) ? mp->base.coin : mp->rel.coin,mp->msigaddr,mp->redeemscript,redeemscript,jprint(decodejson,0)); + free(decodejson); + } + } + } + sprintf(numstr,"%u",mp->origid); + for (i=0; numstr[i]!=0; i++) + sprintf(&mp->approval[i<<1],"%02x",numstr[i]); + sprintf(&mp->approval[i<<1],"%02x",' '); + i++; + mp->approval[i<<1] = 0; + jaddstr(approval,"approval",mp->approval); + hexstr = subatomic_submit(approval,!mp->bobflag); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"approved",senderpub,"","")) != 0 ) + { + if ( (mp->approvalid= juint(retjson,"id")) != 0 ) + retval = 1; + printf("%u approvalid.%u (%s)\n",mp->origid,mp->approvalid,senderpub); + subatomic_status(mp,SUBATOMIC_APPROVED); + free_json(retjson); + } + free(hexstr); + return(retval); +} + +int32_t subatomic_opened(struct msginfo *mp,cJSON *opened,cJSON *msgjson,char *senderpub) +{ + char *hexstr,channelstr[65]; cJSON *retjson; int32_t retval = 0; + subatomic_extrafields(opened,msgjson); + jaddstr(opened,"opened",randhashstr(channelstr)); + hexstr = subatomic_submit(opened,!mp->bobflag); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"opened",senderpub,"","")) != 0 ) + { + if ( (mp->openedid= juint(retjson,"id")) != 0 ) + retval = 1; + printf("%u openedid.%u\n",mp->origid,mp->openedid); + subatomic_status(mp,SUBATOMIC_OPENED); + free_json(retjson); + } + free(hexstr); + return(retval); +} + +int32_t subatomic_payment(struct msginfo *mp,cJSON *payment,cJSON *msgjson,char *senderpub) +{ + bits256 txid; uint64_t paytoshis; cJSON *retjson; char numstr[32],*coin,*dest,*hexstr; int32_t retval = 0; + if ( mp->bobflag == 0 ) + { + coin = mp->rel.name; + paytoshis = mp->rel.satoshis; + if ( subatomic_zonly(&mp->rel) != 0 ) + dest = mp->bob.recvZaddr; + else dest = mp->bob.recvaddr; + sprintf(numstr,"%llu",(long long)paytoshis); + jaddstr(payment,"alicepays",numstr); + jaddstr(payment,"bobdestaddr",dest); + txid = subatomic_coinpayment(mp->origid,mp->OTCmode,&mp->rel,dest,paytoshis,mp->approval,mp->bob.secp,senderpub); + jaddbits256(payment,"alicepayment",txid); + mp->alicepayment = txid; + hexstr = 0; // get it from rawtransaction of txid + jaddstr(payment,"alicetx",hexstr); + } + else + { + coin = mp->base.name; + paytoshis = mp->base.satoshis; + if ( subatomic_zonly(&mp->base) != 0 ) + dest = mp->alice.recvZaddr; + else dest = mp->alice.recvaddr; + sprintf(numstr,"%llu",(long long)paytoshis); + jaddstr(payment,"bobpays",numstr); + jaddstr(payment,"alicedestaddr",dest); + txid = subatomic_coinpayment(mp->origid,mp->OTCmode,&mp->base,dest,paytoshis,mp->approval,mp->alice.secp,senderpub); + jaddbits256(payment,"bobpayment",txid); + mp->bobpayment = txid; + hexstr = 0; // get it from rawtransaction of txid + jaddstr(payment,"bobtx",hexstr); + } + hexstr = subatomic_submit(payment,!mp->bobflag); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"payment",senderpub,"","")) != 0 ) + { + if ( (mp->paymentids[0]= juint(retjson,"id")) != 0 ) + retval = 1; + printf("%u: %.8f %s -> %s, paymentid[0] %u\n",mp->origid,dstr(paytoshis),coin,dest,mp->paymentids[0]); + subatomic_status(mp,SUBATOMIC_PAYMENT); + free_json(retjson); + } + free(hexstr); + return(retval); +} + +int32_t subatomic_paidinfull(struct msginfo *mp,cJSON *paid,cJSON *msgjson,char *senderpub) +{ + char *hexstr; cJSON *retjson; int32_t retval = 0; + jaddstr(paid,"paid","in full"); + subatomic_extrafields(paid,msgjson); + hexstr = subatomic_submit(paid,!mp->bobflag); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"paid",senderpub,"","")) != 0 ) + { + if ( (mp->paidid= juint(retjson,"id")) != 0 ) + retval = 1; + printf("%u paidid.%u\n",mp->origid,mp->paidid); + subatomic_status(mp,SUBATOMIC_PAIDINFULL); + free_json(retjson); + } + free(hexstr); + return(retval); +} + +int32_t subatomic_closed(struct msginfo *mp,cJSON *closed,cJSON *msgjson,char *senderpub) +{ + char *hexstr; cJSON *retjson; int32_t retval = 0; + jaddnum(closed,"closed",mp->origid); + subatomic_extrafields(closed,msgjson); + hexstr = subatomic_submit(closed,!mp->bobflag); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"closed",senderpub,"","")) != 0 ) + { + if ( (mp->closedid= juint(retjson,"id")) != 0 ) + retval = 1; + subatomic_status(mp,SUBATOMIC_CLOSED); + printf("%u closedid.%u\n",mp->origid,mp->closedid); + free_json(retjson); + } + free(hexstr); + return(retval); +} + +uint32_t subatomic_alice_openrequest(struct msginfo *origmp) +{ + struct msginfo *mp; cJSON *retjson,*openrequest; char *hexstr,*str,tmpstr[32]; + mp = subatomic_tracker(origmp->origid); + mp->origid = origmp->origid; + mp->rel.satoshis = origmp->rel.satoshis; + mp->rel.istoken = origmp->rel.istoken; + strcpy(mp->rel.tokenid,origmp->rel.tokenid); + strcpy(mp->rel.name,origmp->rel.name); + strcpy(mp->rel.coin,subatomic_checkname(tmpstr,mp,1,origmp->rel.name)); + strcpy(mp->alice.pubkey,DPOW_pubkeystr); + strcpy(mp->alice.secp,DPOW_secpkeystr); + strcpy(mp->alice.recvZaddr,DPOW_recvZaddr); + strcpy(mp->alice.recvaddr,DPOW_recvaddr); + printf("rel.%s/%s %s openrequest %u status.%d (%s/%s)\n",mp->rel.name,mp->rel.coin,mp->rel.tokenid,mp->origid,mp->status,mp->alice.recvaddr,mp->alice.recvZaddr); + if ( mp->status == 0 && subatomic_orderbook_mpset(mp,"") != 0 ) + { + strcpy(mp->bob.pubkey,mp->senderpub); + if ( subatomic_zonly(&mp->base) != 0 || subatomic_zonly(&mp->rel) != 0 ) + mp->OTCmode = 1; + else mp->OTCmode = SUBATOMIC_OTCDEFAULT; + strcpy(origmp->base.name,mp->base.name); + strcpy(origmp->base.coin,mp->base.coin); + origmp->base.istoken = mp->base.istoken; + strcpy(origmp->base.tokenid,mp->base.tokenid); + origmp->OTCmode = mp->OTCmode; + if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) ) + { + printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis)); + return(0); + } + else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) ) + { + printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis)); + return(0); + } + else if ( (openrequest= subatomic_mpjson(mp)) != 0 ) + { + hexstr = subatomic_submit(openrequest,!mp->bobflag); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"openrequest",mp->bob.pubkey,"","")) != 0 ) + { + mp->openrequestid = juint(retjson,"id"); + printf("%u openrequest.%u -> (%s)\n",mp->origid,mp->openrequestid,mp->bob.pubkey); + subatomic_status(mp,SUBATOMIC_OPENREQUEST); + free_json(retjson); + } + free(hexstr); + } + } + return(mp->openrequestid); +} + +void subatomic_bob_gotopenrequest(uint32_t inboxid,char *senderpub,cJSON *msgjson,char *basename,char *relname) +{ + struct msginfo *mp; cJSON *approval; int32_t origid; char *addr,tmpstr[32],*coin,*acname=""; + origid = juint(msgjson,"origid"); + mp = subatomic_tracker(origid); + strcpy(mp->base.name,basename); + strcpy(mp->base.coin,subatomic_checkname(tmpstr,mp,0,basename)); + strcpy(mp->rel.name,relname); + strcpy(mp->rel.coin,subatomic_checkname(tmpstr,mp,1,relname)); + mp->origid = origid; + mp->rel.satoshis = j64bits(msgjson,"relsatoshis"); + mp->bobflag = 1; + strcpy(mp->bob.pubkey,DPOW_pubkeystr); + strcpy(mp->bob.secp,DPOW_secpkeystr); + strcpy(mp->bob.recvZaddr,DPOW_recvZaddr); + strcpy(mp->bob.recvaddr,DPOW_recvaddr); + if ( (addr= jstr(msgjson,"aliceaddr")) != 0 ) + strcpy(mp->alice.recvaddr,addr); + if ( (addr= jstr(msgjson,"aliceZaddr")) != 0 ) + strcpy(mp->alice.recvZaddr,addr); + if ( (addr= jstr(msgjson,"alicesecp")) != 0 ) + strcpy(mp->alice.secp,addr); + if ( subatomic_zonly(&mp->base) != 0 || subatomic_zonly(&mp->rel) != 0 ) + mp->OTCmode = 1; + else mp->OTCmode = SUBATOMIC_OTCDEFAULT; + printf("%u got open request\n",mp->origid); + if ( mp->status == 0 && subatomic_orderbook_mpset(mp,basename) != 0 && (approval= subatomic_mpjson(mp)) != 0 ) + { + if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) ) + { + printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis)); + subatomic_closed(mp,approval,msgjson,senderpub); + return; + } + else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) ) + { + printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis)); + subatomic_closed(mp,approval,msgjson,senderpub); + return; + } + else if ( subatomic_getbalance(&mp->base) < mp->base.satoshis ) + { + printf("%u bob node low on %s funds! %.8f not enough for %.8f\n",mp->origid,mp->base.coin,dstr(subatomic_getbalance(&mp->base)),dstr(mp->base.satoshis)); + subatomic_closed(mp,approval,msgjson,senderpub); + } + else + { + printf("%u bob (%s/%s) gotopenrequest origid.%u status.%d (%s/%s) SENDERPUB.(%s)\n",mp->origid,mp->base.name,mp->rel.name,mp->origid,mp->status,mp->bob.recvaddr,mp->bob.recvZaddr,senderpub); + subatomic_approved(mp,approval,msgjson,senderpub); + } + } +} + +int32_t subatomic_channelapproved(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) +{ + struct msginfo *mp; cJSON *approval; char *addr,*coin,*acname; int32_t retval = 0; + mp = subatomic_tracker(juint(msgjson,"origid")); + if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (approval= subatomic_mpjson(mp)) != 0 ) + { + printf("%u iambob.%d (%s/%s) channelapproved origid.%u status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->origid,mp->status); + if ( mp->bobflag == 0 && mp->status == SUBATOMIC_OPENREQUEST ) + { + if ( (addr= jstr(msgjson,"bobaddr")) != 0 ) + strcpy(mp->bob.recvaddr,addr); + if ( (addr= jstr(msgjson,"bobZaddr")) != 0 ) + strcpy(mp->bob.recvZaddr,addr); + if ( (addr= jstr(msgjson,"bobsecp")) != 0 ) + strcpy(mp->bob.secp,addr); + retval = subatomic_approved(mp,approval,msgjson,senderpub); + } + else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_APPROVED ) + retval = subatomic_opened(mp,approval,msgjson,senderpub); + } + return(retval); +} + +int32_t subatomic_incomingopened(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) +{ + struct msginfo *mp; cJSON *payment; int32_t retval = 0; + mp = subatomic_tracker(juint(msgjson,"origid")); + if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (payment= subatomic_mpjson(mp)) != 0 ) + { + printf("%u iambob.%d (%s/%s) incomingchannel status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); + if ( mp->bobflag == 0 && mp->status == SUBATOMIC_APPROVED ) + retval = subatomic_payment(mp,payment,msgjson,senderpub); + else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_OPENED ) + retval = 1; // nothing to do + } + return(retval); +} + +int32_t subatomic_incomingpayment(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) +{ + static FILE *fp; + struct msginfo *mp; cJSON *pay,*rawtx,*retjson; bits256 txid; char str[65],*hexstr; int32_t retval = 0; + mp = subatomic_tracker(juint(msgjson,"origid")); + if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (pay= subatomic_mpjson(mp)) != 0 ) + { + printf("%u iambob.%d (%s/%s) incomingpayment status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); + if ( mp->bobflag == 0 ) + { + txid = jbits256(msgjson,"bobpayment"); + jaddbits256(msgjson,"alicepayment",mp->alicepayment); + printf("%u alice waits for %s.%s to be in mempool (%.8f -> %s)\n",mp->origid,mp->base.name,bits256_str(str,txid),dstr(mp->base.satoshis),subatomic_zonly(&mp->base) == 0 ? mp->alice.recvaddr : mp->alice.recvZaddr); + hexstr = jstr(msgjson,"bobtx"); + if ( (rawtx= subatomic_txidwait(&mp->base,txid,hexstr,SUBATOMIC_TIMEOUT,senderpub)) != 0 ) + { + if ( subatomic_verifypayment(&mp->base,rawtx,mp->base.satoshis,subatomic_zonly(&mp->base) == 0 ? mp->alice.recvaddr : mp->alice.recvZaddr,txid) >= 0 ) + mp->gotpayment = 1; + free_json(rawtx); + } + if ( mp->gotpayment != 0 ) + { + printf("%u SWAP COMPLETE <<<<<<<<<<<<<<<<\n",mp->origid); + SUBATOMIC_retval = 0; + if ( mp->base.iszaddr == 0 ) + { + sprintf(str,"%u",mp->origid); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->alicepayment),(char *)"completed",str,DPOW_pubkeystr,"","")) != 0 ) + free_json(retjson); + } + } + else + { + printf("%u SWAP INCOMPLETE, waiting on %s.%s\n",mp->origid,mp->base.name,bits256_str(str,txid)); + if ( (fp= fopen("SUBATOMIC.incomplete","a+")) != 0 ) + { + char *jsonstr = jprint(msgjson,0); + fwrite(jsonstr,1,strlen(jsonstr),fp); + fputc('\n',fp); + fclose(fp); + free(jsonstr); + } + if ( mp->base.iszaddr == 0 ) + { + sprintf(str,"%u",mp->origid); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->alicepayment),(char *)"incomplete",str,DPOW_pubkeystr,"","")) != 0 ) + free_json(retjson); + } + subatomic_closed(mp,pay,msgjson,senderpub); + exit(-1); + } + } + if ( mp->gotpayment != 0 ) + retval = subatomic_paidinfull(mp,pay,msgjson,senderpub); + else + { + if ( mp->bobflag != 0 && mp->status == SUBATOMIC_OPENED ) + { + txid = jbits256(msgjson,"alicepayment"); + printf("%u bob waits for %s.%s to be in mempool (%.8f -> %s)\n",mp->origid,mp->rel.name,bits256_str(str,txid),dstr(mp->rel.satoshis),subatomic_zonly(&mp->rel) == 0 ? mp->bob.recvaddr : mp->bob.recvZaddr); + hexstr = jstr(msgjson,"alicetx"); + if ( (rawtx= subatomic_txidwait(&mp->rel,txid,hexstr,SUBATOMIC_TIMEOUT,senderpub)) != 0 ) + { + if ( subatomic_verifypayment(&mp->rel,rawtx,mp->rel.satoshis,subatomic_zonly(&mp->rel) == 0 ? mp->bob.recvaddr : mp->bob.recvZaddr,txid) >= 0 ) + mp->gotpayment = 1; + free_json(rawtx); + } + if ( mp->gotpayment != 0 ) + { + retval = subatomic_payment(mp,pay,msgjson,senderpub); + jaddbits256(msgjson,"bobpayment",mp->bobpayment); + if ( mp->rel.iszaddr == 0 ) + { + sprintf(str,"%u",mp->origid); + if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->bobpayment),(char *)"completed",str,DPOW_pubkeystr,"","")) != 0 ) + free_json(retjson); + } + printf("%u SWAP COMPLETE <<<<<<<<<<<<<<<<\n",mp->origid); + if ( (fp= fopen("SUBATOMIC.proof","rb+")) == 0 ) + fp = fopen("SUBATOMIC.proof","wb"); + if ( fp != 0 ) + { + char *jsonstr = jprint(msgjson,0); + fseek(fp,0,SEEK_END); + fwrite(jsonstr,1,strlen(jsonstr),fp); + fputc('\n',fp); + fflush(fp); + free(jsonstr); + } + } else printf("%u SWAP INCOMPLETE: %s\n",mp->origid,jprint(msgjson,0)); + } + } + } + return(retval); +} + +int32_t subatomic_incomingfullypaid(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) +{ + struct msginfo *mp; cJSON *closed; int32_t retval = 0; + mp = subatomic_tracker(juint(msgjson,"origid")); + if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (closed= subatomic_mpjson(mp)) != 0 ) + { + printf("%u iambob.%d (%s/%s) incomingfullypaid status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); + // error check msgjson vs M + if ( mp->bobflag == 0 && mp->status == SUBATOMIC_PAIDINFULL ) + retval = subatomic_closed(mp,closed,msgjson,senderpub); + else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_PAYMENT ) + retval = subatomic_paidinfull(mp,closed,msgjson,senderpub); + } + return(retval); +} + +int32_t subatomic_incomingclosed(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp) +{ + struct msginfo *mp; cJSON *closed; int32_t retval = 0; + mp = subatomic_tracker(juint(msgjson,"origid")); + if ( subatomic_orderbook_mpset(mp,mp->base.name) != 0 && (closed= subatomic_mpjson(mp)) != 0 ) + { + printf("%u iambob.%d (%s/%s) incomingclose status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status); + if ( mp->bobflag != 0 ) + dpow_cancel(mp->origid); + if ( mp->status < SUBATOMIC_CLOSED ) + { + retval = subatomic_closed(mp,closed,msgjson,senderpub); + subatomic_status(mp,SUBATOMIC_CLOSED); + } + retval = 1; + } + return(retval); +} + +int32_t subatomic_ismine(int32_t bobflag,cJSON *json,char *basename,char *relname) +{ + char *base,*rel; + if ( (base= jstr(json,"base")) != 0 && (rel= jstr(json,"rel")) != 0 ) + { + if ( strcmp(base,basename) == 0 && strcmp(rel,relname) == 0 ) + return(1); + if ( bobflag != 0 ) + { + if ( strcmp(basename,"#allfiles") == 0 && base[0] == '#' ) + return(1); + fprintf(stderr,"skip ismine (%s/%s) vs (%s/%s)\n",basename,relname,base,rel); + } + } + return(0); +} + +void subatomic_tokensregister(int32_t priority) +{ + char *token_name,*tokenid,existing[65]; cJSON *tokens,*token; int32_t i,numtokens; + if ( SUBATOMIC_json != 0 && (tokens= jarray(&numtokens,SUBATOMIC_json,"tokens")) != 0 ) + { + for (i=0; i 0 ) + { + for (j=0; j %s, %u %llu %u\n",mp->bobflag,mp->base.name,mp->rel.name,mp->origid,(long long)mp->rel.satoshis,mp->openrequestid); + while ( 1 ) + { + if ( msgs == 0 ) + { + sleep(1); + fflush(stdout); + if ( mp->bobflag != 0 ) + { + dpow_pubkeyregister(SUBATOMIC_PRIORITY); + subatomic_tokensregister(SUBATOMIC_PRIORITY); + subatomic_filesregister(SUBATOMIC_PRIORITY); + } + } + msgs = 0; + for (iter=0; iter<(int32_t)(sizeof(tagBs)/sizeof(*tagBs)); iter++) + { + tagB = tagBs[iter]; + if ( (ptrs= dpow_inboxcheck(&n,&stopats[iter],tagB)) != 0 ) + { + for (i=0; ijsonstr)) != 0 ) + { + if ( jint(inboxjson,"tobob") != mp->bobflag ) + continue; + if ( subatomic_ismine(mp->bobflag,inboxjson,mp->base.name,mp->rel.name) != 0 ) + { + if ( strcmp(tagB,"openrequest") == 0 && mp->bobflag != 0 ) + subatomic_bob_gotopenrequest(ptr->shorthash,ptr->senderpub,inboxjson,mp->base.name,mp->rel.name); + else if ( strcmp(tagB,"approved") == 0 ) + mask |= subatomic_channelapproved(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 0; + else if ( strcmp(tagB,"opened") == 0 ) + mask |= subatomic_incomingopened(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 1; + else if ( strcmp(tagB,"payment") == 0 ) + mask |= subatomic_incomingpayment(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 2; + else if ( strcmp(tagB,"paid") == 0 ) + mask |= subatomic_incomingfullypaid(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 3; + else if ( strcmp(tagB,"closed") == 0 ) + mask |= subatomic_incomingclosed(ptr->shorthash,ptr->senderpub,inboxjson,mp) * 0x1f; + else fprintf(stderr,"iambob.%d unknown unexpected tagB.(%s)\n",mp->bobflag,tagB); + } + free_json(inboxjson); + } else fprintf(stderr,"subatomic iambob.%d loop got unparseable(%s)\n",mp->bobflag,ptr->jsonstr); + free(ptr); + ptrs[i] = 0; + } + } + free(ptrs); + } + } + if ( mp->bobflag == 0 && (mask & 0x1f) == 0x1f ) + { + printf("alice %u %llu %u finished\n",mp->origid,(long long)mp->rel.satoshis,mp->openrequestid); + break; + } + } +} + +int32_t main(int32_t argc,char **argv) +{ + char *fname = "subatomic.json"; + int32_t i,height; char *coin,*kcli,*subatomic,*hashstr,*acname=(char *)""; cJSON *retjson; bits256 blockhash; char checkstr[65],str[65],str2[65],tmpstr[32]; long fsize; struct msginfo M; + memset(&M,0,sizeof(M)); + srand((int32_t)time(NULL)); + if ( (subatomic= filestr(&fsize,fname)) == 0 ) + { + fprintf(stderr,"cant load %s file\n",fname); + exit(-1); + } + if ( (SUBATOMIC_json= cJSON_Parse(subatomic)) == 0 ) + { + fprintf(stderr,"cant parse subatomic.json file (%s)\n",subatomic); + exit(-1); + } + free(subatomic); + if ( argc >= 4 ) + { + if ( dpow_pubkey() < 0 ) + { + fprintf(stderr,"couldnt set pubkey for DEX\n"); + return(-1); + } + coin = (char *)argv[1]; + if ( argv[2][0] != 0 ) + REFCOIN_CLI = (char *)argv[2]; + else + { + if ( strcmp(coin,"KMD") != 0 ) + { + acname = coin; + } + } + hashstr = (char *)argv[3]; + strcpy(M.rel.coin,subatomic_checkname(tmpstr,&M,1,coin)); + strcpy(M.rel.name,coin); + if ( argc == 4 && strlen(hashstr) == 64 ) // for blocknotify usage, seems not needed + { + height = get_coinheight(coin,acname,&blockhash); + bits256_str(checkstr,blockhash); + if ( strcmp(checkstr,hashstr) == 0 ) + { + fprintf(stderr,"%s: (%s) %s height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,height); + if ( (retjson= dpow_ntzdata(coin,SUBATOMIC_PRIORITY,height,blockhash)) != 0 ) + free_json(retjson); + } else fprintf(stderr,"coin.%s (%s) %s vs %s, height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,hashstr,height); + if ( strcmp("BTC",coin) != 0 ) + { + bits256 prevntzhash,ntzhash; int32_t prevntzheight,ntzheight; uint32_t ntztime,prevntztime; char hexstr[81]; cJSON *retjson2; + prevntzhash = dpow_ntzhash(coin,&prevntzheight,&prevntztime); + if ( (retjson= get_getinfo(coin,acname)) != 0 ) + { + ntzheight = juint(retjson,"notarized"); + ntzhash = jbits256(retjson,"notarizedhash"); + if ( ntzheight > prevntzheight ) + { + get_coinmerkleroot(coin,acname,ntzhash,&ntztime); + fprintf(stderr,"NOTARIZATION %s.%d %s t.%u\n",coin,ntzheight,bits256_str(str,ntzhash),ntztime); + bits256_str(hexstr,ntzhash); + sprintf(&hexstr[64],"%08x",ntzheight); + sprintf(&hexstr[72],"%08x",ntztime); + hexstr[80] = 0; + if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,coin,"notarizations",DPOW_pubkeystr,"","")) != 0 ) + free_json(retjson2); + } + else if ( ntzheight == prevntzheight && memcmp(&prevntzhash,&ntzhash,32) != 0 ) + fprintf(stderr,"NTZ ERROR %s.%d %s != %s\n",coin,ntzheight,bits256_str(str,ntzhash),bits256_str(str2,prevntzhash)); + free_json(retjson); + } + } + } + else if ( argc == 5 && atol(hashstr) > 10000 ) + { + char checkstr[32]; uint64_t mult = 1; + M.origid = (uint32_t)atol(hashstr); + sprintf(checkstr,"%u",M.origid); + if ( strcmp(checkstr,hashstr) == 0 ) // alice + { + M.rel.satoshis = (uint64_t)(atof(argv[4])*SATOSHIDEN+0.0000000049999); + for (i=0; M.rel.name[i]!=0; i++) + if ( M.rel.name[i] == '.' ) + { + mult = SATOSHIDEN; + break; + } + if ( subatomic_getbalance(&M.rel) < M.rel.satoshis/mult ) + { + fprintf(stderr,"not enough balance %s %.8f for %.8f\n",M.rel.coin,dstr(subatomic_getbalance(&M.rel)),dstr(M.rel.satoshis/mult)); + return(-1); + } + fprintf(stderr,"subatomic_channel_alice (%s/%s) %s %u with %.8f %llu\n",M.rel.name,M.rel.coin,hashstr,M.origid,atof(argv[4]),(long long)M.rel.satoshis); + dpow_pubkeyregister(SUBATOMIC_PRIORITY); + M.openrequestid = subatomic_alice_openrequest(&M); + if ( M.openrequestid != 0 ) + subatomic_loop(&M); + } else fprintf(stderr,"checkstr mismatch %s %s != %s\n",coin,hashstr,checkstr); + } + else + { + M.bobflag = 1; + strcpy(M.base.name,hashstr); + strcpy(M.base.coin,subatomic_checkname(tmpstr,&M,0,hashstr)); + subatomic_loop(&M); // while ( 1 ) loop for each relcoin -> basecoin + } + } + return(SUBATOMIC_retval); +} + diff --git a/src/cc/dapps/subatomic.json b/src/cc/dapps/subatomic.json new file mode 100644 index 000000000..7832fdb9e --- /dev/null +++ b/src/cc/dapps/subatomic.json @@ -0,0 +1,27 @@ +{ +"authorized": [ + {"chmex":"030754bffcf6dfcb34a20c486ff5a5be5546b9cc16fba9692165272b3f8e98c4af" }, + {"SHossain":"03c8657bd57b6ceb14514a10e99fe8a0cec5a9bc24592df7f66f050e670e4f6bac" }, + {"satinder":"03732f8ef851ff234c74d0df575c2c5b159e2bab3faca4ec52b3f217d5cda5361d" }, + {"ml777":"02453d028c74cb9551e1aaf35113383b6ecbd9f06ff23a4ab1a953429b9763e345" }, + {"tonylhub":"0218e0f435d4544404c25a7759b7f7174d821215085ef936218c5569d975af468b" }, + {"gthub":"036c7de9a5090fbad78b9eea41549ccacc07bd0e9e7f8d290c88f470f3569e1a35" }, + {"zkTrader":"026c6b0b35ec0adc2f8a5c648da1fce634f798c69d5e9fe518400447e88398b830" }, + {"nutellalicka":"03aee08860e0340f0f490a3ef3718d6676882f2d63d4f536dfebb1d348b82c79ee" }, + {"gcharang":"02d3431950c2f0f9654217b6ce3d44468d3a9ca7255741767fdeee7c5ec6b47567" }, + {"jl777":"02b27de3ee5335518b06f69f4fbabb029cfc737613b100996841d5532b324a5a61" } +], +"tokens":[ + {"RICK.demo":"2b1feef719ecb526b07416dd432bce603ac6dc8bfe794cddf105cb52f6aae3cd"} +], +"files":[ + {"filename":"hushd","prices":[{"HUSH":0.1}, {"PIRATE":1}]} +], +"externalcoins":[ + { "BTC":"bitcoin-cli" }, + { "KMD":"komodod-cli" }, + { "CHIPS":"chips-cli" }, + { "PIRATE":"pirate-cli" } +] +} + diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 7fd94e4e7..026475f88 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -74,7 +74,7 @@ namespace Checkpoints { fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; } - return fWorkBefore / (fWorkBefore + fWorkAfter); + return std::min(fWorkBefore / (fWorkBefore + fWorkAfter), 1.0); } int GetTotalBlocksEstimate(const CChainParams::CCheckpointData& data) diff --git a/src/clientversion.h b/src/clientversion.h index 71fcee16f..c62e6d3e0 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -33,8 +33,8 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it // Must be kept in sync with configure.ac ! #define CLIENT_VERSION_MAJOR 3 -#define CLIENT_VERSION_MINOR 3 -#define CLIENT_VERSION_REVISION 2 +#define CLIENT_VERSION_MINOR 4 +#define CLIENT_VERSION_REVISION 0 #define CLIENT_VERSION_BUILD 50 //! Set to true for release, false for prerelease or test build diff --git a/src/coins.cpp b/src/coins.cpp index 38fac8252..03b046a70 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2012-2014 The Bitcoin Core developers -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -65,7 +65,6 @@ bool CCoins::Spend(uint32_t nPos) Cleanup(); return true; } -bool CCoinsView::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; } bool CCoinsView::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return false; } bool CCoinsView::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; } bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } @@ -85,7 +84,6 @@ bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } -bool CCoinsViewBacked::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return base->GetSproutAnchorAt(rt, tree); } bool CCoinsViewBacked::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return base->GetSaplingAnchorAt(rt, tree); } bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return base->GetNullifier(nullifier, type); } bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); } @@ -139,30 +137,6 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const return ret; } - -bool CCoinsViewCache::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { - CAnchorsSproutMap::const_iterator it = cacheSproutAnchors.find(rt); - if (it != cacheSproutAnchors.end()) { - if (it->second.entered) { - tree = it->second.tree; - return true; - } else { - return false; - } - } - - if (!base->GetSproutAnchorAt(rt, tree)) { - return false; - } - - CAnchorsSproutMap::iterator ret = cacheSproutAnchors.insert(std::make_pair(rt, CAnchorsSproutCacheEntry())).first; - ret->second.entered = true; - ret->second.tree = tree; - cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); - - return true; -} - bool CCoinsViewCache::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { CAnchorsSaplingMap::const_iterator it = cacheSaplingAnchors.find(rt); if (it != cacheSaplingAnchors.end()) { @@ -271,7 +245,6 @@ void CCoinsViewCache::BringBestAnchorIntoCache( SproutMerkleTree &tree ) { - assert(GetSproutAnchorAt(currentRoot, tree)); } template<> @@ -550,9 +523,9 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, bool CCoinsViewCache::Flush() { bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, cacheSproutAnchors, cacheSaplingAnchors, cacheSproutNullifiers, cacheSaplingNullifiers); cacheCoins.clear(); - cacheSproutAnchors.clear(); + //cacheSproutAnchors.clear(); cacheSaplingAnchors.clear(); - cacheSproutNullifiers.clear(); + //cacheSproutNullifiers.clear(); cacheSaplingNullifiers.clear(); cachedCoinsUsage = 0; return fOk; @@ -624,37 +597,8 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr } -bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const +bool CCoinsViewCache::HaveShieldedRequirements(const CTransaction& tx) const { - boost::unordered_map intermediates; - - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) - { - BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers) - { - if (GetNullifier(nullifier, SPROUT)) { - // If the nullifier is set, this transaction - // double-spends! - return false; - } - } - - SproutMerkleTree tree; - auto it = intermediates.find(joinsplit.anchor); - if (it != intermediates.end()) { - tree = it->second; - } else if (!GetSproutAnchorAt(joinsplit.anchor, tree)) { - return false; - } - - BOOST_FOREACH(const uint256& commitment, joinsplit.commitments) - { - tree.append(commitment); - } - - intermediates.insert(std::make_pair(tree.root(), tree)); - } - for (const SpendDescription &spendDescription : tx.vShieldedSpend) { if (GetNullifier(spendDescription.nullifier, SAPLING)) { // Prevent double spends LogPrintf("%s: sapling nullifier %s exists, preventing double spend\n", __FUNCTION__, spendDescription.nullifier.GetHex().c_str()); diff --git a/src/coins.h b/src/coins.h index cc8b19f68..2d08be42c 100644 --- a/src/coins.h +++ b/src/coins.h @@ -372,9 +372,6 @@ struct CCoinsStats class CCoinsView { public: - //! Retrieve the tree (Sprout) at a particular anchored root in the chain - virtual bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; - //! Retrieve the tree (Sapling) at a particular anchored root in the chain virtual bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; @@ -421,7 +418,6 @@ protected: public: CCoinsViewBacked(CCoinsView *viewIn); - bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; @@ -493,7 +489,6 @@ public: CNullifiersMap getNullifiers(); // Standard CCoinsView methods - bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; @@ -550,7 +545,7 @@ public: size_t DynamicMemoryUsage() const; /** - * Amount of bitcoins coming in to a transaction + * Amount of HUSH coming in to a transaction * Note that lightweight clients may not know anything besides the hash of previous transactions, * so may not be able to calculate this. * @@ -562,8 +557,8 @@ public: //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx) const; - //! Check whether all joinsplit requirements (anchors/nullifiers) are satisfied - bool HaveJoinSplitRequirements(const CTransaction& tx) const; + //! Check whether all shielded requirements (anchors/nullifiers) are satisfied + bool HaveShieldedRequirements(const CTransaction& tx) const; //! Return priority of tx at height nHeight double GetPriority(const CTransaction &tx, int nHeight) const; diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp index b75e0c002..984ca377e 100644 --- a/src/consensus/upgrades.cpp +++ b/src/consensus/upgrades.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers // Copyright (c) 2018 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptoconditions/src/anon.c b/src/cryptoconditions/src/anon.c index 55d7a3b7f..a1bd6b4a3 100644 --- a/src/cryptoconditions/src/anon.c +++ b/src/cryptoconditions/src/anon.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -53,11 +54,8 @@ static void anonToJSON(const CC *cond, cJSON *params) { } -static unsigned char *anonFingerprint(const CC *cond) { - unsigned char *out = calloc(1, 32); - //fprintf(stderr,"anon fingerprint %p %p\n",out,cond->fingerprint); +static void anonFingerprint(const CC *cond, uint8_t *out) { memcpy(out, cond->fingerprint, 32); - return out; } diff --git a/src/cryptoconditions/src/cryptoconditions.c b/src/cryptoconditions/src/cryptoconditions.c index 74949affc..6642b0345 100644 --- a/src/cryptoconditions/src/cryptoconditions.c +++ b/src/cryptoconditions/src/cryptoconditions.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -65,8 +66,8 @@ void appendUriSubtypes(uint32_t mask, unsigned char *buf) { char *cc_conditionUri(const CC *cond) { - unsigned char *fp = cond->type->fingerprint(cond); - if (!fp) return NULL; + unsigned char *fp = calloc(1, 32); + cond->type->fingerprint(cond, fp); unsigned char *encoded = base64_encode(fp, 32); @@ -118,13 +119,13 @@ uint32_t fromAsnSubtypes(const ConditionTypes_t types) { size_t cc_conditionBinary(const CC *cond, unsigned char *buf) { Condition_t *asn = calloc(1, sizeof(Condition_t)); asnCondition(cond, asn); + size_t out = 0; asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Condition, asn, buf, 1000); - if (rc.encoded == -1) { - fprintf(stderr, "CONDITION NOT ENCODED\n"); - return 0; - } + if (rc.encoded == -1) goto end; + out = rc.encoded; +end: ASN_STRUCT_FREE(asn_DEF_Condition, asn); - return rc.encoded; + return out; } @@ -146,10 +147,12 @@ void asnCondition(const CC *cond, Condition_t *asn) { // This may look a little weird - we dont have a reference here to the correct // union choice for the condition type, so we just assign everything to the threshold // type. This works out nicely since the union choices have the same binary interface. + CompoundSha256Condition_t *choice = &asn->choice.thresholdSha256; choice->cost = cc_getCost(cond); - choice->fingerprint.buf = cond->type->fingerprint(cond); choice->fingerprint.size = 32; + choice->fingerprint.buf = calloc(1, 32); + cond->type->fingerprint(cond, choice->fingerprint.buf); choice->subtypes = asnSubtypes(cond->type->getSubtypes(cond)); } diff --git a/src/cryptoconditions/src/ed25519.c b/src/cryptoconditions/src/ed25519.c index 8d73d3cf1..33b332071 100644 --- a/src/cryptoconditions/src/ed25519.c +++ b/src/cryptoconditions/src/ed25519.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -25,11 +26,10 @@ struct CCType CC_Ed25519Type; -static unsigned char *ed25519Fingerprint(const CC *cond) { +static void ed25519Fingerprint(const CC *cond, uint8_t *out) { Ed25519FingerprintContents_t *fp = calloc(1, sizeof(Ed25519FingerprintContents_t)); - //fprintf(stderr,"ed25519 fingerprint %p %p\n",fp,cond->publicKey); OCTET_STRING_fromBuf(&fp->publicKey, cond->publicKey, 32); - return hashFingerprintContents(&asn_DEF_Ed25519FingerprintContents, fp); + hashFingerprintContents(&asn_DEF_Ed25519FingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/eval.c b/src/cryptoconditions/src/eval.c index 99ff1ebf5..a017d181d 100644 --- a/src/cryptoconditions/src/eval.c +++ b/src/cryptoconditions/src/eval.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -25,11 +26,8 @@ struct CCType CC_EvalType; -static unsigned char *evalFingerprint(const CC *cond) { - unsigned char *hash = calloc(1, 32); - //fprintf(stderr,"evalfingerprint %p %p\n",hash,cond->code); - sha256(cond->code, cond->codeLength, hash); - return hash; +static void evalFingerprint(const CC *cond, uint8_t *out) { + sha256(cond->code, cond->codeLength, out); } @@ -105,7 +103,7 @@ static uint32_t evalSubtypes(const CC *cond) { */ int jsonVerifyEval(CC *cond, void *context) { if (cond->codeLength == 5 && 0 == memcmp(cond->code, "TEST", 4)) { - return cond->code[5]; + return cond->code[4]; } fprintf(stderr, "Cannot verify eval; user function unknown\n"); return 0; diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h index bdb9ae43a..5a36ba40b 100644 --- a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h @@ -36,4 +36,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons #endif - +#endif diff --git a/src/cryptoconditions/src/internal.h b/src/cryptoconditions/src/internal.h index 790e28962..3d24214ce 100644 --- a/src/cryptoconditions/src/internal.h +++ b/src/cryptoconditions/src/internal.h @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -41,7 +42,7 @@ typedef struct CCType { char name[100]; Condition_PR asnType; int (*visitChildren)(CC *cond, CCVisitor visitor); - unsigned char *(*fingerprint)(const CC *cond); + void (*fingerprint)(const CC *cond, uint8_t *fp); unsigned long (*getCost)(const CC *cond); uint32_t (*getSubtypes)(const CC *cond); CC *(*fromJSON)(const cJSON *params, char *err); @@ -77,7 +78,7 @@ struct CCType *getTypeByAsnEnum(Condition_PR present); */ unsigned char *base64_encode(const unsigned char *data, size_t input_length); unsigned char *base64_decode(const unsigned char *data_, size_t *output_length); -unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp); +void hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp, uint8_t* out); void dumpStr(unsigned char *str, size_t len); int checkString(const cJSON *value, char *key, char *err); int checkDecodeBase64(const cJSON *value, char *key, char *err, unsigned char **data, size_t *size); diff --git a/src/cryptoconditions/src/json_rpc.c b/src/cryptoconditions/src/json_rpc.c index 150bcb12b..c4fde8080 100644 --- a/src/cryptoconditions/src/json_rpc.c +++ b/src/cryptoconditions/src/json_rpc.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * diff --git a/src/cryptoconditions/src/prefix.c b/src/cryptoconditions/src/prefix.c index 45c6d8033..ea339df35 100644 --- a/src/cryptoconditions/src/prefix.c +++ b/src/cryptoconditions/src/prefix.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -37,13 +38,12 @@ static int prefixVisitChildren(CC *cond, CCVisitor visitor) { } -static unsigned char *prefixFingerprint(const CC *cond) { +static void prefixFingerprint(const CC *cond, uint8_t *out) { PrefixFingerprintContents_t *fp = calloc(1, sizeof(PrefixFingerprintContents_t)); - //fprintf(stderr,"prefixfinger %p %p\n",fp,cond->prefix); - asnCondition(cond->subcondition, &fp->subcondition); // TODO: check asnCondition for safety + asnCondition(cond->subcondition, &fp->subcondition); fp->maxMessageLength = cond->maxMessageLength; OCTET_STRING_fromBuf(&fp->prefix, cond->prefix, cond->prefixLength); - return hashFingerprintContents(&asn_DEF_PrefixFingerprintContents, fp); + hashFingerprintContents(&asn_DEF_PrefixFingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/preimage.c b/src/cryptoconditions/src/preimage.c index 9e7fe12f4..2fee86b88 100644 --- a/src/cryptoconditions/src/preimage.c +++ b/src/cryptoconditions/src/preimage.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -44,11 +45,8 @@ static unsigned long preimageCost(const CC *cond) { } -static unsigned char *preimageFingerprint(const CC *cond) { - unsigned char *hash = calloc(1, 32); - //fprintf(stderr,"preimage %p %p\n",hash,cond->preimage); - sha256(cond->preimage, cond->preimageLength, hash); - return hash; +static void preimageFingerprint(const CC *cond, uint8_t *out) { + sha256(cond->preimage, cond->preimageLength, out); } diff --git a/src/cryptoconditions/src/secp256k1.c b/src/cryptoconditions/src/secp256k1.c index a16115bb8..d5319d32b 100644 --- a/src/cryptoconditions/src/secp256k1.c +++ b/src/cryptoconditions/src/secp256k1.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -88,11 +89,10 @@ void initVerify() { } -static unsigned char *secp256k1Fingerprint(const CC *cond) { +static void secp256k1Fingerprint(const CC *cond, uint8_t *out) { Secp256k1FingerprintContents_t *fp = calloc(1, sizeof(Secp256k1FingerprintContents_t)); - //fprintf(stderr,"secpfinger %p %p size %d vs %d\n",fp,cond->publicKey,(int32_t)sizeof(Secp256k1FingerprintContents_t),(int32_t)SECP256K1_PK_SIZE); OCTET_STRING_fromBuf(&fp->publicKey, cond->publicKey, SECP256K1_PK_SIZE); - return hashFingerprintContents(&asn_DEF_Secp256k1FingerprintContents, fp); + hashFingerprintContents(&asn_DEF_Secp256k1FingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/threshold.c b/src/cryptoconditions/src/threshold.c index 9547f4f8c..e8e12435e 100644 --- a/src/cryptoconditions/src/threshold.c +++ b/src/cryptoconditions/src/threshold.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -94,17 +95,15 @@ static int cmpConditionBin(const void *a, const void *b) { } -static unsigned char *thresholdFingerprint(const CC *cond) { - /* Create fingerprint */ +static void thresholdFingerprint(const CC *cond, uint8_t *out) { ThresholdFingerprintContents_t *fp = calloc(1, sizeof(ThresholdFingerprintContents_t)); - //fprintf(stderr,"thresholdfinger %p\n",fp); fp->threshold = cond->threshold; for (int i=0; isize; i++) { Condition_t *asnCond = asnConditionNew(cond->subconditions[i]); asn_set_add(&fp->subconditions2, asnCond); } qsort(fp->subconditions2.list.array, cond->size, sizeof(Condition_t*), cmpConditionBin); - return hashFingerprintContents(&asn_DEF_ThresholdFingerprintContents, fp); + hashFingerprintContents(&asn_DEF_ThresholdFingerprintContents, fp, out); } diff --git a/src/cryptoconditions/src/utils.c b/src/cryptoconditions/src/utils.c index 6a2167119..ac2256f18 100644 --- a/src/cryptoconditions/src/utils.c +++ b/src/cryptoconditions/src/utils.c @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -210,17 +211,15 @@ void jsonAddBase64(cJSON *params, char *key, unsigned char *bin, size_t size) { } -unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp) { +void hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp, uint8_t *out) { unsigned char buf[BUF_SIZE]; asn_enc_rval_t rc = der_encode_to_buffer(asnType, fp, buf, BUF_SIZE); ASN_STRUCT_FREE(*asnType, fp); if (rc.encoded < 1) { fprintf(stderr, "Encoding fingerprint failed\n"); - return 0; + return; } - unsigned char *hash = calloc(1,32); - sha256(buf, rc.encoded, hash); - return hash; + sha256(buf, rc.encoded, out); } @@ -301,5 +300,3 @@ int jsonGetHexOptional(const cJSON *params, char *key, char *err, unsigned char } return checkDecodeHex(item, key, err, data, size); } - - diff --git a/src/cryptoconditions/tests/test_failure_modes.py b/src/cryptoconditions/tests/test_failure_modes.py index 59b0b3f24..435e20c88 100644 --- a/src/cryptoconditions/tests/test_failure_modes.py +++ b/src/cryptoconditions/tests/test_failure_modes.py @@ -82,4 +82,25 @@ def test_malleability_checked(): assert not cc_rfb(b'\xa2\x13\xa0\x0f\xa0\x06\x80\x04abcd\xa0\x05\x80\x03abc\xa1\x00') +def test_large_threshold(): + conds = [{ + 'type': "secp256k1-sha-256", + "publicKey": "02D5D969305535AC29A77079C11D4F0DD40661CF96E04E974A5E8D7E374EE225AA" + }] + + for i in range(250): + conds.append({ + "type": "eval-sha-256", + "code": "VEVTVAE" + }) + + r = jsonRPC("encodeCondition", { + "type": "threshold-sha-256", + "subfulfillments": conds, + "threshold": 251 + }) + assert 'error' not in r, r + + + so.cc_conditionUri.restype = ctypes.c_char_p diff --git a/src/gtest/test_joinsplit.cpp b/src/gtest/test_joinsplit.cpp deleted file mode 100644 index 8032fc972..000000000 --- a/src/gtest/test_joinsplit.cpp +++ /dev/null @@ -1,585 +0,0 @@ -#include - -#include "utilstrencodings.h" - -#include -#include - -#include "zcash/prf.h" -#include "util.h" -#include "streams.h" -#include "version.h" -#include "serialize.h" -#include "primitives/transaction.h" -#include "zcash/JoinSplit.hpp" -#include "zcash/Note.hpp" -#include "zcash/NoteEncryption.hpp" -#include "zcash/IncrementalMerkleTree.hpp" - -#include - -using namespace libzcash; - -extern ZCJoinSplit* params; - -// Make the Groth proof for a Sprout statement, -// and store the result in a JSDescription object. -JSDescription makeSproutProof( - ZCJoinSplit& js, - const std::array& inputs, - const std::array& outputs, - const uint256& joinSplitPubKey, - uint64_t vpub_old, - uint64_t vpub_new, - const uint256& rt -){ - return JSDescription(js, joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new); -} - -bool verifySproutProof( - ZCJoinSplit& js, - const JSDescription& jsdesc, - const uint256& joinSplitPubKey -) -{ - auto verifier = libzcash::ProofVerifier::Strict(); - return jsdesc.Verify(js, verifier, joinSplitPubKey); -} - - -void test_full_api(ZCJoinSplit* js) -{ - // Create verification context. - auto verifier = libzcash::ProofVerifier::Strict(); - - // The recipient's information. - SproutSpendingKey recipient_key = SproutSpendingKey::random(); - SproutPaymentAddress recipient_addr = recipient_key.address(); - - // Create the commitment tree - SproutMerkleTree tree; - - // Set up a JoinSplit description - uint64_t vpub_old = 10; - uint64_t vpub_new = 0; - uint256 joinSplitPubKey = random_uint256(); - uint256 rt = tree.root(); - JSDescription jsdesc; - - { - std::array inputs = { - JSInput(), // dummy input - JSInput() // dummy input - }; - - std::array outputs = { - JSOutput(recipient_addr, 10), - JSOutput() // dummy output - }; - - std::array output_notes; - - // Perform the proofs - jsdesc = makeSproutProof( - *js, - inputs, - outputs, - joinSplitPubKey, - vpub_old, - vpub_new, - rt - ); - } - - // Verify both PHGR and Groth Proof: - ASSERT_TRUE(verifySproutProof(*js, jsdesc, joinSplitPubKey)); - - { - SproutMerkleTree tree; - JSDescription jsdesc2; - // Recipient should decrypt - // Now the recipient should spend the money again - auto h_sig = js->h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey); - ZCNoteDecryption decryptor(recipient_key.receiving_key()); - - auto note_pt = SproutNotePlaintext::decrypt( - decryptor, - jsdesc.ciphertexts[0], - jsdesc.ephemeralKey, - h_sig, - 0 - ); - - auto decrypted_note = note_pt.note(recipient_addr); - - ASSERT_TRUE(decrypted_note.value() == 10); - - // Insert the commitments from the last tx into the tree - tree.append(jsdesc.commitments[0]); - auto witness_recipient = tree.witness(); - tree.append(jsdesc.commitments[1]); - witness_recipient.append(jsdesc.commitments[1]); - vpub_old = 0; - vpub_new = 1; - rt = tree.root(); - auto joinSplitPubKey2 = random_uint256(); - - { - std::array inputs = { - JSInput(), // dummy input - JSInput(witness_recipient, decrypted_note, recipient_key) - }; - - SproutSpendingKey second_recipient = SproutSpendingKey::random(); - SproutPaymentAddress second_addr = second_recipient.address(); - - std::array outputs = { - JSOutput(second_addr, 9), - JSOutput() // dummy output - }; - - std::array output_notes; - - - // Perform the proofs - jsdesc2 = makeSproutProof( - *js, - inputs, - outputs, - joinSplitPubKey2, - vpub_old, - vpub_new, - rt - ); - - } - - - // Verify Groth Proof: - ASSERT_TRUE(verifySproutProof(*js, jsdesc2, joinSplitPubKey2)); - } -} - -// Invokes the API (but does not compute a proof) -// to test exceptions -void invokeAPI( - ZCJoinSplit* js, - const std::array& inputs, - const std::array& outputs, - uint64_t vpub_old, - uint64_t vpub_new, - const uint256& rt -) { - uint256 ephemeralKey; - uint256 randomSeed; - uint256 joinSplitPubKey = random_uint256(); - std::array macs; - std::array nullifiers; - std::array commitments; - std::array ciphertexts; - - std::array output_notes; - - // Groth - SproutProof proof = js->prove( - inputs, - outputs, - output_notes, - ciphertexts, - ephemeralKey, - joinSplitPubKey, - randomSeed, - macs, - nullifiers, - commitments, - vpub_old, - vpub_new, - rt, - false - ); -} - -void invokeAPIFailure( - ZCJoinSplit* js, - const std::array& inputs, - const std::array& outputs, - uint64_t vpub_old, - uint64_t vpub_new, - const uint256& rt, - std::string reason -) -{ - try { - invokeAPI(js, inputs, outputs, vpub_old, vpub_new, rt); - FAIL() << "It worked, when it shouldn't have!"; - } catch(std::invalid_argument const & err) { - EXPECT_EQ(err.what(), reason); - } catch(...) { - FAIL() << "Expected invalid_argument exception."; - } -} - -TEST(joinsplit, h_sig) -{ -/* -// by Taylor Hornby - -import pyblake2 -import binascii - -def hSig(randomSeed, nf1, nf2, joinSplitPubKey): - return pyblake2.blake2b( - data=(randomSeed + nf1 + nf2 + joinSplitPubKey), - digest_size=32, - person=b"ZcashComputehSig" - ).digest() - -INCREASING = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" - -TEST_VECTORS = [ - [b"a" * 32, b"b" * 32, b"c" * 32, b"d" * 32], - [b"\x00" * 32, b"\x00" * 32, b"\x00" * 32, b"\x00" * 32], - [b"\xFF" * 32, b"\xFF" * 32, b"\xFF" * 32, b"\xFF" * 32], - [INCREASING, INCREASING, INCREASING, INCREASING] -] - -for test_input in TEST_VECTORS: - print "---" - print "\"" + binascii.hexlify(test_input[0][::-1]) + "\"" - print "\"" + binascii.hexlify(test_input[1][::-1]) + "\"" - print "\"" + binascii.hexlify(test_input[2][::-1]) + "\"" - print "\"" + binascii.hexlify(test_input[3][::-1]) + "\"" - print "\"" + binascii.hexlify(hSig(test_input[0], test_input[1], test_input[2], test_input[3])[::-1]) + "\"" -*/ - - std::vector> tests = { - { - "6161616161616161616161616161616161616161616161616161616161616161", - "6262626262626262626262626262626262626262626262626262626262626262", - "6363636363636363636363636363636363636363636363636363636363636363", - "6464646464646464646464646464646464646464646464646464646464646464", - "a8cba69f1fa329c055756b4af900f8a00b61e44f4cb8a1824ceb58b90a5b8113" - }, - { - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "697322276b5dd93b12fb1fcbd2144b2960f24c73aac6c6a0811447be1e7f1e19" - }, - { - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "4961048919f0ca79d49c9378c36a91a8767060001f4212fe6f7d426f3ccf9f32" - }, - { - "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100", - "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100", - "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100", - "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100", - "b61110ec162693bc3d9ca7fb0eec3afd2e278e2f41394b3ff11d7cb761ad4b27" - } - }; - - BOOST_FOREACH(std::vector& v, tests) { - auto expected = ZCJoinSplit::h_sig( - uint256S(v[0]), - {uint256S(v[1]), uint256S(v[2])}, - uint256S(v[3]) - ); - - EXPECT_EQ(expected, uint256S(v[4])); - } -} - -void increment_note_witnesses( - const uint256& element, - std::vector& witnesses, - SproutMerkleTree& tree -) -{ - tree.append(element); - for (SproutWitness& w : witnesses) { - w.append(element); - } - witnesses.push_back(tree.witness()); -} - -TEST(joinsplit, full_api_test) -{ - { - std::vector witnesses; - SproutMerkleTree tree; - increment_note_witnesses(uint256(), witnesses, tree); - SproutSpendingKey sk = SproutSpendingKey::random(); - SproutPaymentAddress addr = sk.address(); - SproutNote note1(addr.a_pk, 100, random_uint256(), random_uint256()); - increment_note_witnesses(note1.cm(), witnesses, tree); - SproutNote note2(addr.a_pk, 100, random_uint256(), random_uint256()); - increment_note_witnesses(note2.cm(), witnesses, tree); - SproutNote note3(addr.a_pk, 2100000000000001, random_uint256(), random_uint256()); - increment_note_witnesses(note3.cm(), witnesses, tree); - SproutNote note4(addr.a_pk, 1900000000000000, random_uint256(), random_uint256()); - increment_note_witnesses(note4.cm(), witnesses, tree); - SproutNote note5(addr.a_pk, 1900000000000000, random_uint256(), random_uint256()); - increment_note_witnesses(note5.cm(), witnesses, tree); - - // Should work - invokeAPI(params, - { - JSInput(), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 0, - tree.root()); - - // lhs > MAX_MONEY - invokeAPIFailure(params, - { - JSInput(), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 2100000000000001, - 0, - tree.root(), - "nonsensical vpub_old value"); - - // rhs > MAX_MONEY - invokeAPIFailure(params, - { - JSInput(), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 2100000000000001, - tree.root(), - "nonsensical vpub_new value"); - - // input witness for the wrong element - invokeAPIFailure(params, - { - JSInput(witnesses[0], note1, sk), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 100, - tree.root(), - "witness of wrong element for joinsplit input"); - - // input witness doesn't match up with - // real root - invokeAPIFailure(params, - { - JSInput(witnesses[1], note1, sk), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 100, - uint256(), - "joinsplit not anchored to the correct root"); - - // input is in the tree now! this should work - invokeAPI(params, - { - JSInput(witnesses[1], note1, sk), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 100, - tree.root()); - - // Wrong secret key - invokeAPIFailure(params, - { - JSInput(witnesses[1], note1, SproutSpendingKey::random()), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 0, - tree.root(), - "input note not authorized to spend with given key"); - - // Absurd input value - invokeAPIFailure(params, - { - JSInput(witnesses[3], note3, sk), - JSInput() - }, - { - JSOutput(), - JSOutput() - }, - 0, - 0, - tree.root(), - "nonsensical input note value"); - - // Absurd total input value - invokeAPIFailure(params, - { - JSInput(witnesses[4], note4, sk), - JSInput(witnesses[5], note5, sk) - }, - { - JSOutput(), - JSOutput() - }, - 0, - 0, - tree.root(), - "nonsensical left hand size of joinsplit balance"); - - // Absurd output value - invokeAPIFailure(params, - { - JSInput(), - JSInput() - }, - { - JSOutput(addr, 2100000000000001), - JSOutput() - }, - 0, - 0, - tree.root(), - "nonsensical output value"); - - // Absurd total output value - invokeAPIFailure(params, - { - JSInput(), - JSInput() - }, - { - JSOutput(addr, 1900000000000000), - JSOutput(addr, 1900000000000000) - }, - 0, - 0, - tree.root(), - "nonsensical right hand side of joinsplit balance"); - - // Absurd total output value - invokeAPIFailure(params, - { - JSInput(), - JSInput() - }, - { - JSOutput(addr, 1900000000000000), - JSOutput() - }, - 0, - 0, - tree.root(), - "invalid joinsplit balance"); - } - - test_full_api(params); -} - -TEST(joinsplit, note_plaintexts) -{ - uint252 a_sk = uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516840e")); - uint256 a_pk = PRF_addr_a_pk(a_sk); - uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk); - uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc); - SproutPaymentAddress addr_pk(a_pk, pk_enc); - - uint256 h_sig; - - ZCNoteEncryption encryptor(h_sig); - uint256 epk = encryptor.get_epk(); - - SproutNote note(a_pk, - 1945813, - random_uint256(), - random_uint256() - ); - - std::array memo; - - SproutNotePlaintext note_pt(note, memo); - - ZCNoteEncryption::Ciphertext ct = note_pt.encrypt(encryptor, pk_enc); - - ZCNoteDecryption decryptor(sk_enc); - - auto decrypted = SproutNotePlaintext::decrypt(decryptor, ct, epk, h_sig, 0); - auto decrypted_note = decrypted.note(addr_pk); - - ASSERT_TRUE(decrypted_note.a_pk == note.a_pk); - ASSERT_TRUE(decrypted_note.rho == note.rho); - ASSERT_TRUE(decrypted_note.r == note.r); - ASSERT_TRUE(decrypted_note.value() == note.value()); - - ASSERT_TRUE(decrypted.memo() == note_pt.memo()); - - // Check memo() returns by reference, not return by value, for use cases such as: - // std::string data(plaintext.memo().begin(), plaintext.memo().end()); - ASSERT_TRUE(decrypted.memo().data() == decrypted.memo().data()); - - // Check serialization of note plaintext - CDataStream ss(SER_DISK, PROTOCOL_VERSION); - ss << note_pt; - SproutNotePlaintext note_pt2; - ss >> note_pt2; - ASSERT_EQ(note_pt.value(), note.value()); - ASSERT_EQ(note_pt.value(), note_pt2.value()); - ASSERT_EQ(note_pt.memo(), note_pt2.memo()); - ASSERT_EQ(note_pt.rho, note_pt2.rho); - ASSERT_EQ(note_pt.r, note_pt2.r); -} - -TEST(joinsplit, note_class) -{ - uint252 a_sk = uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516840e")); - uint256 a_pk = PRF_addr_a_pk(a_sk); - uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk); - uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc); - SproutPaymentAddress addr_pk(a_pk, pk_enc); - - SproutNote note(a_pk, - 1945813, - random_uint256(), - random_uint256()); - - SproutNote clone = note; - ASSERT_NE(¬e, &clone); - ASSERT_EQ(note.value(), clone.value()); - ASSERT_EQ(note.cm(), clone.cm()); - ASSERT_EQ(note.rho, clone.rho); - ASSERT_EQ(note.r, clone.r); - ASSERT_EQ(note.a_pk, clone.a_pk); -} diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index ccf9cb9ba..a8c984984 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -95,101 +95,6 @@ TEST(keystore_tests, sapling_keys) { } } -TEST(keystore_tests, store_and_retrieve_spending_key) { - CBasicKeyStore keyStore; - libzcash::SproutSpendingKey skOut; - - std::set addrs; - keyStore.GetSproutPaymentAddresses(addrs); - EXPECT_EQ(0, addrs.size()); - - auto sk = libzcash::SproutSpendingKey::random(); - auto addr = sk.address(); - - // Sanity-check: we can't get a key we haven't added - EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr)); - EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut)); - - keyStore.AddSproutSpendingKey(sk); - EXPECT_TRUE(keyStore.HaveSproutSpendingKey(addr)); - EXPECT_TRUE(keyStore.GetSproutSpendingKey(addr, skOut)); - EXPECT_EQ(sk, skOut); - - keyStore.GetSproutPaymentAddresses(addrs); - EXPECT_EQ(1, addrs.size()); - EXPECT_EQ(1, addrs.count(addr)); -} - -TEST(keystore_tests, store_and_retrieve_note_decryptor) { - CBasicKeyStore keyStore; - ZCNoteDecryption decOut; - - auto sk = libzcash::SproutSpendingKey::random(); - auto addr = sk.address(); - - EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut)); - - keyStore.AddSproutSpendingKey(sk); - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); -} - -TEST(keystore_tests, StoreAndRetrieveViewingKey) { - CBasicKeyStore keyStore; - libzcash::SproutViewingKey vkOut; - libzcash::SproutSpendingKey skOut; - ZCNoteDecryption decOut; - - auto sk = libzcash::SproutSpendingKey::random(); - auto vk = sk.viewing_key(); - auto addr = sk.address(); - - // Sanity-check: we can't get a viewing key we haven't added - EXPECT_FALSE(keyStore.HaveSproutViewingKey(addr)); - EXPECT_FALSE(keyStore.GetSproutViewingKey(addr, vkOut)); - - // and we shouldn't have a spending key or decryptor either - EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr)); - EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut)); - EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut)); - - // and we can't find it in our list of addresses - std::set addresses; - keyStore.GetSproutPaymentAddresses(addresses); - EXPECT_FALSE(addresses.count(addr)); - - keyStore.AddSproutViewingKey(vk); - EXPECT_TRUE(keyStore.HaveSproutViewingKey(addr)); - EXPECT_TRUE(keyStore.GetSproutViewingKey(addr, vkOut)); - EXPECT_EQ(vk, vkOut); - - // We should still not have the spending key... - EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr)); - EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut)); - - // ... but we should have a decryptor - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); - - // ... and we should find it in our list of addresses - addresses.clear(); - keyStore.GetSproutPaymentAddresses(addresses); - EXPECT_TRUE(addresses.count(addr)); - - keyStore.RemoveSproutViewingKey(vk); - EXPECT_FALSE(keyStore.HaveSproutViewingKey(addr)); - EXPECT_FALSE(keyStore.GetSproutViewingKey(addr, vkOut)); - EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr)); - EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut)); - addresses.clear(); - keyStore.GetSproutPaymentAddresses(addresses); - EXPECT_FALSE(addresses.count(addr)); - - // We still have a decryptor because those are cached in memory - // (and also we only remove viewing keys when adding a spending key) - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); -} // Sapling TEST(keystore_tests, StoreAndRetrieveSaplingSpendingKey) { @@ -280,9 +185,6 @@ TEST(keystore_tests, StoreAndRetrieveHDSeedInEncryptedStore) { // 3) Test adding a new seed to an already-encrypted key store TestCCryptoKeyStore keyStore2; - // Add a Sprout address so the wallet has something to test when decrypting - ASSERT_TRUE(keyStore2.AddSproutSpendingKey(libzcash::SproutSpendingKey::random())); - ASSERT_TRUE(keyStore2.EncryptKeys(vMasterKey)); ASSERT_TRUE(keyStore2.Unlock(vMasterKey)); @@ -296,78 +198,4 @@ TEST(keystore_tests, StoreAndRetrieveHDSeedInEncryptedStore) { EXPECT_EQ(seed3, seedOut); } -TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) { - TestCCryptoKeyStore keyStore; - uint256 r {GetRandHash()}; - CKeyingMaterial vMasterKey (r.begin(), r.end()); - libzcash::SproutSpendingKey keyOut; - ZCNoteDecryption decOut; - std::set addrs; - - // 1) Test adding a key to an unencrypted key store, then encrypting it - auto sk = libzcash::SproutSpendingKey::random(); - auto addr = sk.address(); - EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut)); - - keyStore.AddSproutSpendingKey(sk); - ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr)); - ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr, keyOut)); - ASSERT_EQ(sk, keyOut); - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); - - ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey)); - ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr)); - ASSERT_FALSE(keyStore.GetSproutSpendingKey(addr, keyOut)); - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); - - // Unlocking with a random key should fail - uint256 r2 {GetRandHash()}; - CKeyingMaterial vRandomKey (r2.begin(), r2.end()); - EXPECT_FALSE(keyStore.Unlock(vRandomKey)); - - // Unlocking with a slightly-modified vMasterKey should fail - CKeyingMaterial vModifiedKey (r.begin(), r.end()); - vModifiedKey[0] += 1; - EXPECT_FALSE(keyStore.Unlock(vModifiedKey)); - - // Unlocking with vMasterKey should succeed - ASSERT_TRUE(keyStore.Unlock(vMasterKey)); - ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr, keyOut)); - ASSERT_EQ(sk, keyOut); - - keyStore.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(1, addrs.size()); - ASSERT_EQ(1, addrs.count(addr)); - - // 2) Test adding a spending key to an already-encrypted key store - auto sk2 = libzcash::SproutSpendingKey::random(); - auto addr2 = sk2.address(); - EXPECT_FALSE(keyStore.GetNoteDecryptor(addr2, decOut)); - - keyStore.AddSproutSpendingKey(sk2); - ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr2)); - ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr2, keyOut)); - ASSERT_EQ(sk2, keyOut); - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut); - - ASSERT_TRUE(keyStore.Lock()); - ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr2)); - ASSERT_FALSE(keyStore.GetSproutSpendingKey(addr2, keyOut)); - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut); - - ASSERT_TRUE(keyStore.Unlock(vMasterKey)); - ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr2, keyOut)); - ASSERT_EQ(sk2, keyOut); - EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut); - - keyStore.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(2, addrs.size()); - ASSERT_EQ(1, addrs.count(addr)); - ASSERT_EQ(1, addrs.count(addr2)); -} #endif diff --git a/src/gtest/test_paymentdisclosure.cpp b/src/gtest/test_paymentdisclosure.cpp deleted file mode 100644 index c166cdbe1..000000000 --- a/src/gtest/test_paymentdisclosure.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include - -#include "main.h" -#include "utilmoneystr.h" -#include "chainparams.h" -#include "utilstrencodings.h" -#include "zcash/Address.hpp" -#include "wallet/wallet.h" -#include "amount.h" - -#include -#include -#include -#include -#include -#include -#include -#include "util.h" - -#include "paymentdisclosure.h" -#include "paymentdisclosuredb.h" - -#include "sodium.h" - -#include -#include -#include - -using namespace std; - -/* - To run tests: - ./zcash-gtest --gtest_filter="paymentdisclosure.*" - - Note: As an experimental feature, writing your own tests may require option flags to be set. - mapArgs["-experimentalfeatures"] = true; - mapArgs["-paymentdisclosure"] = true; -*/ - -#define NUM_TRIES 10000 - -#define DUMP_DATABASE_TO_STDOUT false - -static boost::uuids::random_generator uuidgen; - -static uint256 random_uint256() -{ - uint256 ret; - randombytes_buf(ret.begin(), 32); - return ret; -} - -// Subclass of PaymentDisclosureDB to add debugging methods -class PaymentDisclosureDBTest : public PaymentDisclosureDB { -public: - PaymentDisclosureDBTest(const boost::filesystem::path& dbPath) : PaymentDisclosureDB(dbPath) {} - - void DebugDumpAllStdout() { - ASSERT_NE(db, nullptr); - std::lock_guard guard(lock_); - - // Iterate over each item in the database and print them - leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); - - for (it->SeekToFirst(); it->Valid(); it->Next()) { - cout << it->key().ToString() << " : "; - // << it->value().ToString() << endl; - try { - std::string strValue = it->value().ToString(); - PaymentDisclosureInfo info; - CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); - ssValue >> info; - cout << info.ToString() << std::endl; - } catch (const std::exception& e) { - cout << e.what() << std::endl; - } - } - - if (false == it->status().ok()) { - cerr << "An error was found iterating over the database" << endl; - cerr << it->status().ToString() << endl; - } - - delete it; - } -}; - - - -// This test creates random payment disclosure blobs and checks that they can be -// 1. inserted and retrieved from a database -// 2. serialized and deserialized without corruption -// Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only -// used as convention to improve the user experience when sharing payment disclosure blobs. -TEST(paymentdisclosure, mainnet) { - SelectParams(CBaseChainParams::MAIN); - - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); - mapArgs["-datadir"] = pathTemp.string(); - - std::cout << "Test payment disclosure database created in folder: " << pathTemp.string() << std::endl; - - PaymentDisclosureDBTest mydb(pathTemp); - - for (int i=0; i vch(&buffer[0], &buffer[0] + 32); - uint256 joinSplitPrivKey = uint256(vch); - - // Create payment disclosure key and info data to store in test database - size_t js = random_uint256().GetCheapHash() % std::numeric_limits::max(); - uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits::max(); - PaymentDisclosureKey key { random_uint256(), js, n}; - PaymentDisclosureInfo info; - info.esk = random_uint256(); - info.joinSplitPrivKey = joinSplitPrivKey; - info.zaddr = libzcash::SproutSpendingKey::random().address(); - ASSERT_TRUE(mydb.Put(key, info)); - - // Retrieve info from test database into new local variable and test it matches - PaymentDisclosureInfo info2; - ASSERT_TRUE(mydb.Get(key, info2)); - ASSERT_EQ(info, info2); - - // Modify this local variable and confirm it no longer matches - info2.esk = random_uint256(); - info2.joinSplitPrivKey = random_uint256(); - info2.zaddr = libzcash::SproutSpendingKey::random().address(); - ASSERT_NE(info, info2); - - // Using the payment info object, let's create a dummy payload - PaymentDisclosurePayload payload; - payload.version = PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL; - payload.esk = info.esk; - payload.txid = key.hash; - payload.js = key.js; - payload.n = key.n; - payload.message = "random-" + boost::uuids::to_string(uuidgen()); // random message - payload.zaddr = info.zaddr; - - // Serialize and hash the payload to generate a signature - uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); - - // Compute the payload signature - unsigned char payloadSig[64]; - if (!(crypto_sign_detached(&payloadSig[0], NULL, - dataToBeSigned.begin(), 32, - &buffer[0] // buffer containing both private and public key required - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(&payloadSig[0], - dataToBeSigned.begin(), 32, - joinSplitPubKey.begin() - ) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - // Convert signature buffer to boost array - std::array arrayPayloadSig; - memcpy(arrayPayloadSig.data(), &payloadSig[0], 64); - - // Payment disclosure blob to pass around - PaymentDisclosure pd = {payload, arrayPayloadSig}; - - // Test payment disclosure constructors - PaymentDisclosure pd2(payload, arrayPayloadSig); - ASSERT_EQ(pd, pd2); - PaymentDisclosure pd3(joinSplitPubKey, key, info, payload.message); - ASSERT_EQ(pd, pd3); - - // Verify serialization and deserialization works - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << pd; - std::string ssHexString = HexStr(ss.begin(), ss.end()); - - PaymentDisclosure pdTmp; - CDataStream ssTmp(ParseHex(ssHexString), SER_NETWORK, PROTOCOL_VERSION); - ssTmp >> pdTmp; - ASSERT_EQ(pd, pdTmp); - - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << pdTmp; - std::string ss2HexString = HexStr(ss2.begin(), ss2.end()); - ASSERT_EQ(ssHexString, ss2HexString); - - // Verify marker - ASSERT_EQ(pd.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES); - ASSERT_EQ(pdTmp.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES); - ASSERT_EQ(0, ssHexString.find("706462ff")); // Little endian encoding of PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES value - - // Sanity check - PaymentDisclosure pdDummy; - ASSERT_NE(pd, pdDummy); - } - -#if DUMP_DATABASE_TO_STDOUT == true - mydb.DebugDumpAllStdout(); -#endif -} diff --git a/src/gtest/test_transaction.cpp b/src/gtest/test_transaction.cpp deleted file mode 100644 index 1350768ff..000000000 --- a/src/gtest/test_transaction.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include - -#include "primitives/transaction.h" -#include "zcash/Note.hpp" -#include "zcash/Address.hpp" - -#include - -extern ZCJoinSplit* params; -extern int GenZero(int n); -extern int GenMax(int n); - -TEST(Transaction, JSDescriptionRandomized) { - // construct a merkle tree - SproutMerkleTree merkleTree; - - libzcash::SproutSpendingKey k = libzcash::SproutSpendingKey::random(); - libzcash::SproutPaymentAddress addr = k.address(); - - libzcash::SproutNote note(addr.a_pk, 100, uint256(), uint256()); - - // commitment from coin - uint256 commitment = note.cm(); - - // insert commitment into the merkle tree - merkleTree.append(commitment); - - // compute the merkle root we will be working with - uint256 rt = merkleTree.root(); - - auto witness = merkleTree.witness(); - - // create JSDescription - uint256 joinSplitPubKey; - std::array inputs = { - libzcash::JSInput(witness, note, k), - libzcash::JSInput() // dummy input of zero value - }; - std::array outputs = { - libzcash::JSOutput(addr, 50), - libzcash::JSOutput(addr, 50) - }; - std::array inputMap; - std::array outputMap; - - { - auto jsdesc = JSDescription::Randomized( - false, - *params, joinSplitPubKey, rt, - inputs, outputs, - inputMap, outputMap, - 0, 0, false); - - std::set inputSet(inputMap.begin(), inputMap.end()); - std::set expectedInputSet {0, 1}; - EXPECT_EQ(expectedInputSet, inputSet); - - std::set outputSet(outputMap.begin(), outputMap.end()); - std::set expectedOutputSet {0, 1}; - EXPECT_EQ(expectedOutputSet, outputSet); - } - - { - auto jsdesc = JSDescription::Randomized( - false, - *params, joinSplitPubKey, rt, - inputs, outputs, - inputMap, outputMap, - 0, 0, false, nullptr, GenZero); - - std::array expectedInputMap {1, 0}; - std::array expectedOutputMap {1, 0}; - EXPECT_EQ(expectedInputMap, inputMap); - EXPECT_EQ(expectedOutputMap, outputMap); - } - - { - auto jsdesc = JSDescription::Randomized( - false, - *params, joinSplitPubKey, rt, - inputs, outputs, - inputMap, outputMap, - 0, 0, false, nullptr, GenMax); - - std::array expectedInputMap {0, 1}; - std::array expectedOutputMap {0, 1}; - EXPECT_EQ(expectedInputMap, inputMap); - EXPECT_EQ(expectedOutputMap, outputMap); - } -} diff --git a/src/httpserver.cpp b/src/httpserver.cpp index e1d235665..603e1e6ae 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -11,6 +11,7 @@ #include "rpc/protocol.h" // For HTTP status codes #include "sync.h" #include "ui_interface.h" +#include "utilstrencodings.h" #include #include @@ -251,21 +252,25 @@ static void http_request_cb(struct evhttp_request* req, void* arg) { std::unique_ptr hreq(new HTTPRequest(req)); - LogPrint("http", "Received a %s request for %s from %s\n", - RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); - // Early address-based allow check if (!ClientAllowed(hreq->GetPeer())) { + LogPrint("http", "HTTP request from %s rejected: Client network is not allowed RPC access\n", + hreq->GetPeer().ToString()); hreq->WriteReply(HTTP_FORBIDDEN); return; } // Early reject unknown HTTP methods if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { + LogPrint("http", "HTTP request from %s rejected: Unknown HTTP request method\n", + hreq->GetPeer().ToString()); hreq->WriteReply(HTTP_BADMETHOD); return; } + LogPrint("http", "Received a %s request for %s from %s\n", + RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString()); + // Find registered handler for prefix std::string strURI = hreq->GetURI(); std::string path; diff --git a/src/init.cpp b/src/init.cpp index ac15db192..c43c5b0b4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -101,7 +101,7 @@ extern int32_t KOMODO_SNAPSHOT_INTERVAL; extern void komodo_init(int32_t height); -ZCJoinSplit* pzcashParams = NULL; +//ZCJoinSplit* pzcashParams = NULL; #ifdef ENABLE_WALLET CWallet* pwalletMain = NULL; @@ -305,8 +305,8 @@ void Shutdown() delete pwalletMain; pwalletMain = NULL; #endif - delete pzcashParams; - pzcashParams = NULL; + //delete pzcashParams; + //pzcashParams = NULL; globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -399,6 +399,7 @@ std::string HelpMessage(HelpMessageMode mode) #ifndef _WIN32 strUsage += HelpMessageOpt("-pid=", strprintf(_("Specify pid file (default: %s)"), "komodod.pid")); #endif + strUsage += HelpMessageOpt("-txexpirynotify=", _("Execute command when transaction expires (%s in cmd is replaced by transaction id)")); strUsage += HelpMessageOpt("-prune=", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); @@ -581,7 +582,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-metricsui", _("Set to 1 for a persistent metrics screen, 0 for sequential metrics output (default: 1 if running in a console, 0 otherwise)")); strUsage += HelpMessageOpt("-metricsrefreshtime", strprintf(_("Number of seconds between metrics refreshes (default: %u if running in a console, %u otherwise)"), 1, 600)); } - strUsage += HelpMessageGroup(_("Komodo Asset Chain options:")); + strUsage += HelpMessageGroup(_("Hush Smart Chain options:")); strUsage += HelpMessageOpt("-ac_algo", _("Choose PoW mining algorithm, default is Equihash")); strUsage += HelpMessageOpt("-ac_blocktime", _("Block time in seconds, default is 60")); strUsage += HelpMessageOpt("-ac_cc", _("Cryptoconditions, default 0")); @@ -618,6 +619,14 @@ static void BlockNotifyCallback(const uint256& hashNewTip) boost::thread t(runCommand, strCmd); // thread runs free } +static void TxExpiryNotifyCallback(const uint256& txid) +{ + std::string strCmd = GetArg("-txexpirynotify", ""); + + boost::replace_all(strCmd, "%s", txid.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free +} + struct CImportingNow { CImportingNow() { @@ -1033,10 +1042,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (mapArgs.count("-developerencryptwallet")) { fprintf(stderr,"%s wallet encryption error\n", __FUNCTION__); return InitError(_("Wallet encryption requires -experimentalfeatures.")); - } - else if (mapArgs.count("-paymentdisclosure")) { - fprintf(stderr,"%s payment disclosure error\n", __FUNCTION__); - return InitError(_("Payment disclosure requires -experimentalfeatures.")); + //TODO: make this non experimental } else if (mapArgs.count("-zmergetoaddress")) { fprintf(stderr,"%s zmerge error\n", __FUNCTION__); return InitError(_("RPC method z_mergetoaddress requires -experimentalfeatures.")); @@ -2114,6 +2120,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (mapArgs.count("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); + if (mapArgs.count("-txexpirynotify")) + uiInterface.NotifyTxExpiration.connect(TxExpiryNotifyCallback); if ( KOMODO_REWIND >= 0 ) { uiInterface.InitMessage(_("Activating best chain...")); diff --git a/src/init.h b/src/init.h index 108339865..c68d59419 100644 --- a/src/init.h +++ b/src/init.h @@ -23,7 +23,7 @@ #include -#include "zcash/JoinSplit.hpp" +//#include "zcash/JoinSplit.hpp" class CScheduler; class CWallet; @@ -34,7 +34,7 @@ class thread_group; } // namespace boost extern CWallet* pwalletMain; -extern ZCJoinSplit* pzcashParams; +//extern ZCJoinSplit* pzcashParams; void StartShutdown(); bool ShutdownRequested(); diff --git a/src/key.h b/src/key.h index 857e8a8ae..16492b151 100644 --- a/src/key.h +++ b/src/key.h @@ -1,6 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/key_io.cpp b/src/key_io.cpp index dd4176fee..2e0dfeb9a 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Copyright (c) 2016-2018 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -84,15 +85,6 @@ private: public: PaymentAddressEncoder(const CChainParams& params) : m_params(params) {} - std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const - { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << zaddr; - std::vector data = m_params.Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); - data.insert(data.end(), ss.begin(), ss.end()); - return EncodeBase58Check(data); - } - std::string operator()(const libzcash::SaplingPaymentAddress& zaddr) const { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -107,6 +99,7 @@ public: } std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } + std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const { return {}; } }; class ViewingKeyEncoder : public boost::static_visitor @@ -117,17 +110,6 @@ private: public: ViewingKeyEncoder(const CChainParams& params) : m_params(params) {} - std::string operator()(const libzcash::SproutViewingKey& vk) const - { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << vk; - std::vector data = m_params.Base58Prefix(CChainParams::ZCVIEWING_KEY); - data.insert(data.end(), ss.begin(), ss.end()); - std::string ret = EncodeBase58Check(data); - memory_cleanse(data.data(), data.size()); - return ret; - } - std::string operator()(const libzcash::SaplingIncomingViewingKey& vk) const { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -152,17 +134,6 @@ private: public: SpendingKeyEncoder(const CChainParams& params) : m_params(params) {} - std::string operator()(const libzcash::SproutSpendingKey& zkey) const - { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << zkey; - std::vector data = m_params.Base58Prefix(CChainParams::ZCSPENDING_KEY); - data.insert(data.end(), ss.begin(), ss.end()); - std::string ret = EncodeBase58Check(data); - memory_cleanse(data.data(), data.size()); - return ret; - } - std::string operator()(const libzcash::SaplingExtendedSpendingKey& zkey) const { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -324,18 +295,6 @@ std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) libzcash::PaymentAddress DecodePaymentAddress(const std::string& str) { std::vector data; - if (DecodeBase58Check(str, data)) { - const std::vector& zaddr_prefix = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); - if ((data.size() == libzcash::SerializedSproutPaymentAddressSize + zaddr_prefix.size()) && - std::equal(zaddr_prefix.begin(), zaddr_prefix.end(), data.begin())) { - CSerializeData serialized(data.begin() + zaddr_prefix.size(), data.end()); - CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); - libzcash::SproutPaymentAddress ret; - ss >> ret; - return ret; - } - } - data.clear(); auto bech = bech32::Decode(str); if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) && bech.second.size() == ConvertedSaplingPaymentAddressSize) { @@ -363,20 +322,6 @@ std::string EncodeViewingKey(const libzcash::ViewingKey& vk) libzcash::ViewingKey DecodeViewingKey(const std::string& str) { std::vector data; - if (DecodeBase58Check(str, data)) { - const std::vector& vk_prefix = Params().Base58Prefix(CChainParams::ZCVIEWING_KEY); - if ((data.size() == libzcash::SerializedSproutViewingKeySize + vk_prefix.size()) && - std::equal(vk_prefix.begin(), vk_prefix.end(), data.begin())) { - CSerializeData serialized(data.begin() + vk_prefix.size(), data.end()); - CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); - libzcash::SproutViewingKey ret; - ss >> ret; - memory_cleanse(serialized.data(), serialized.size()); - memory_cleanse(data.data(), data.size()); - return ret; - } - } - data.clear(); auto bech = bech32::Decode(str); if(bech.first == Params().Bech32HRP(CChainParams::SAPLING_INCOMING_VIEWING_KEY) && bech.second.size() == ConvertedSaplingIncomingViewingKeySize) { @@ -400,20 +345,6 @@ std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) libzcash::SpendingKey DecodeSpendingKey(const std::string& str) { std::vector data; - if (DecodeBase58Check(str, data)) { - const std::vector& zkey_prefix = Params().Base58Prefix(CChainParams::ZCSPENDING_KEY); - if ((data.size() == libzcash::SerializedSproutSpendingKeySize + zkey_prefix.size()) && - std::equal(zkey_prefix.begin(), zkey_prefix.end(), data.begin())) { - CSerializeData serialized(data.begin() + zkey_prefix.size(), data.end()); - CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); - libzcash::SproutSpendingKey ret; - ss >> ret; - memory_cleanse(serialized.data(), serialized.size()); - memory_cleanse(data.data(), data.size()); - return ret; - } - } - data.clear(); auto bech = bech32::Decode(str); if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY) && bech.second.size() == ConvertedSaplingExtendedSpendingKeySize) { diff --git a/src/key_io.h b/src/key_io.h index 013469ab6..567c9a2f2 100644 --- a/src/key_io.h +++ b/src/key_io.h @@ -1,6 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers // Copyright (c) 2016-2018 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/keystore.cpp b/src/keystore.cpp index 34bab456c..ca4fa3712 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -128,15 +129,6 @@ bool CBasicKeyStore::HaveWatchOnly() const return (!setWatchOnly.empty()); } -bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) -{ - LOCK(cs_SpendingKeyStore); - auto address = sk.address(); - mapSproutSpendingKeys[address] = sk; - mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.receiving_key()))); - return true; -} - //! Sapling bool CBasicKeyStore::AddSaplingSpendingKey( const libzcash::SaplingExtendedSpendingKey &sk, @@ -155,14 +147,6 @@ bool CBasicKeyStore::AddSaplingSpendingKey( return true; } -bool CBasicKeyStore::AddSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - LOCK(cs_SpendingKeyStore); - auto address = vk.address(); - mapSproutViewingKeys[address] = vk; - mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk.sk_enc))); - return true; -} bool CBasicKeyStore::AddSaplingFullViewingKey( const libzcash::SaplingFullViewingKey &fvk, @@ -190,18 +174,7 @@ bool CBasicKeyStore::AddSaplingIncomingViewingKey( return true; } -bool CBasicKeyStore::RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - LOCK(cs_SpendingKeyStore); - mapSproutViewingKeys.erase(vk.address()); - return true; -} -bool CBasicKeyStore::HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const -{ - LOCK(cs_SpendingKeyStore); - return mapSproutViewingKeys.count(address) > 0; -} bool CBasicKeyStore::HaveSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk) const { @@ -215,19 +188,6 @@ bool CBasicKeyStore::HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymen return mapSaplingIncomingViewingKeys.count(addr) > 0; } -bool CBasicKeyStore::GetSproutViewingKey( - const libzcash::SproutPaymentAddress &address, - libzcash::SproutViewingKey &vkOut) const -{ - LOCK(cs_SpendingKeyStore); - SproutViewingKeyMap::const_iterator mi = mapSproutViewingKeys.find(address); - if (mi != mapSproutViewingKeys.end()) { - vkOut = mi->second; - return true; - } - return false; -} - bool CBasicKeyStore::GetSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk, libzcash::SaplingFullViewingKey &fvkOut) const { diff --git a/src/keystore.h b/src/keystore.h index c2e1f25d9..6f34d9bc4 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -70,14 +71,6 @@ public: virtual bool HaveWatchOnly(const CScript &dest) const =0; virtual bool HaveWatchOnly() const =0; - //! Add a spending key to the store. - virtual bool AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) =0; - - //! Check whether a spending key corresponding to a given payment address is present in the store. - virtual bool HaveSproutSpendingKey(const libzcash::SproutPaymentAddress &address) const =0; - virtual bool GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey& skOut) const =0; - virtual void GetSproutPaymentAddresses(std::set &setAddress) const =0; - //! Add a Sapling spending key to the store. virtual bool AddSaplingSpendingKey( const libzcash::SaplingExtendedSpendingKey &sk, @@ -106,21 +99,11 @@ public: libzcash::SaplingIncomingViewingKey& ivkOut) const =0; virtual void GetSaplingPaymentAddresses(std::set &setAddress) const =0; - //! Support for Sprout viewing keys - virtual bool AddSproutViewingKey(const libzcash::SproutViewingKey &vk) =0; - virtual bool RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk) =0; - virtual bool HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const =0; - virtual bool GetSproutViewingKey( - const libzcash::SproutPaymentAddress &address, - libzcash::SproutViewingKey& vkOut) const =0; }; typedef std::map KeyMap; typedef std::map ScriptMap; typedef std::set WatchOnlySet; -typedef std::map SproutSpendingKeyMap; -typedef std::map SproutViewingKeyMap; -typedef std::map NoteDecryptorMap; // Full viewing key has equivalent functionality to a transparent address // When encrypting wallet, encrypt SaplingSpendingKeyMap, while leaving SaplingFullViewingKeyMap unencrypted @@ -137,10 +120,7 @@ protected: KeyMap mapKeys; ScriptMap mapScripts; WatchOnlySet setWatchOnly; - SproutSpendingKeyMap mapSproutSpendingKeys; - SproutViewingKeyMap mapSproutViewingKeys; - NoteDecryptorMap mapNoteDecryptors; - + SaplingSpendingKeyMap mapSaplingSpendingKeys; SaplingFullViewingKeyMap mapSaplingFullViewingKeys; SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys; @@ -195,62 +175,6 @@ public: virtual bool HaveWatchOnly(const CScript &dest) const; virtual bool HaveWatchOnly() const; - bool AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk); - bool HaveSproutSpendingKey(const libzcash::SproutPaymentAddress &address) const - { - bool result; - { - LOCK(cs_SpendingKeyStore); - result = (mapSproutSpendingKeys.count(address) > 0); - } - return result; - } - bool GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey &skOut) const - { - { - LOCK(cs_SpendingKeyStore); - SproutSpendingKeyMap::const_iterator mi = mapSproutSpendingKeys.find(address); - if (mi != mapSproutSpendingKeys.end()) - { - skOut = mi->second; - return true; - } - } - return false; - } - bool GetNoteDecryptor(const libzcash::SproutPaymentAddress &address, ZCNoteDecryption &decOut) const - { - { - LOCK(cs_SpendingKeyStore); - NoteDecryptorMap::const_iterator mi = mapNoteDecryptors.find(address); - if (mi != mapNoteDecryptors.end()) - { - decOut = mi->second; - return true; - } - } - return false; - } - void GetSproutPaymentAddresses(std::set &setAddress) const - { - setAddress.clear(); - { - LOCK(cs_SpendingKeyStore); - SproutSpendingKeyMap::const_iterator mi = mapSproutSpendingKeys.begin(); - while (mi != mapSproutSpendingKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } - SproutViewingKeyMap::const_iterator mvi = mapSproutViewingKeys.begin(); - while (mvi != mapSproutViewingKeys.end()) - { - setAddress.insert((*mvi).first); - mvi++; - } - } - } - //! Sapling bool AddSaplingSpendingKey( const libzcash::SaplingExtendedSpendingKey &sk, @@ -313,17 +237,10 @@ public: } } - virtual bool AddSproutViewingKey(const libzcash::SproutViewingKey &vk); - virtual bool RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk); - virtual bool HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const; - virtual bool GetSproutViewingKey( - const libzcash::SproutPaymentAddress &address, - libzcash::SproutViewingKey& vkOut) const; }; typedef std::vector > CKeyingMaterial; typedef std::map > > CryptedKeyMap; -typedef std::map > CryptedSproutSpendingKeyMap; //! Sapling typedef std::map > CryptedSaplingSpendingKeyMap; diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 39cf5da63..e1748c191 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -20,7 +20,7 @@ #include #include #include "consensus/params.h" -#include "primitives/nonce.h" +//#include "primitives/nonce.h" #include "komodo_defs.h" #include "script/standard.h" #include "cc/CCinclude.h" diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 34b1cc364..e482390bb 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -34,33 +34,34 @@ #define KOMODO_FIRSTFUNGIBLEID 100 #define KOMODO_SAPLING_ACTIVATION 1544832000 // Dec 15th, 2018 #define KOMODO_SAPLING_DEADLINE 1550188800 // Feb 15th, 2019 -#define ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX 57 -#define ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF 27 -#define ASSETCHAINS_STAKED_MIN_POW_DIFF 536900000 // 537000000 537300000 #define _COINBASE_MATURITY 100 // KMD Notary Seasons // 1: May 1st 2018 1530921600 // 2: July 15th 2019 1563148800 -> estimated height 1444000 -// 3: 3rd season ending isnt known, so use very far times in future. +// 3: 3rd season // 1751328000 = dummy timestamp, 1 July 2025! // 7113400 = 5x current KMD blockheight. -// to add 4th season, change NUM_KMD_SEASONS to 4, and add timestamp and height of activation to these arrays. +// to add seasons, change NUM_KMD_SEASONS, and add timestamp and height of activation to these arrays. -#define NUM_KMD_SEASONS 4 +#define NUM_KMD_SEASONS 6 #define NUM_KMD_NOTARIES 64 // $ ./contrib/block_time.pl 166250 // Hush Block 166250 will happen at roughly: // Wed Jan 29 08:14:12 2020 Eastern # 1580303652 // Wed Jan 29 13:14:12 2020 GMT # 1580303652 -const uint32_t nHushHardforkHeight = 166250; +const uint32_t nHushHardforkHeight = 166250; +// $ ./contrib/block_time.pl 245555 +// Hush Block 245555 will happen at roughly... now +const uint32_t nHushHardforkHeight2 = 245055; // No coins/code are currently using timestamp activated fork -const uint32_t nHushHardforkTimestamp = 1580303652; // Jan 29nd 1pm GMT +const uint32_t nHushHardforkTimestamp = 1580303652; // Jan 29nd 1pm GMT +const uint32_t nHushHardforkTimestamp2 = 1594425600; // Jul 11th 12a GMT -static const uint32_t KMD_SEASON_TIMESTAMPS[NUM_KMD_SEASONS] = {1525132800, 1563148800, nHushHardforkTimestamp, 1751328000}; -static const int32_t KMD_SEASON_HEIGHTS[NUM_KMD_SEASONS] = {1,2,nHushHardforkHeight, 5*nHushHardforkHeight}; +static const uint32_t KMD_SEASON_TIMESTAMPS[NUM_KMD_SEASONS] = {1525132800, 1563148800, nHushHardforkTimestamp, nHushHardforkTimestamp2, nHushHardforkTimestamp2*5, nHushHardforkTimestamp2*6}; +static const int32_t KMD_SEASON_HEIGHTS[NUM_KMD_SEASONS] = {1,2,nHushHardforkHeight, nHushHardforkHeight2, (int)340000, 5*nHushHardforkHeight2}; // Era array of pubkeys. Add extra seasons to bottom as requried, after adding appropriate info above. static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = @@ -329,7 +330,74 @@ static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = {"gt_AR", "0307c1cf89bd8ed4db1b09a0a98cf5f746fc77df3803ecc8611cf9455ec0ce6960" }, {"patchkez_SH", "03d7c187689bf829ca076a30bbf36d2e67bb74e16a3290d8a55df21d6cb15c80c1" }, {"decker_AR", "02a85540db8d41c7e60bf0d33d1364b4151cad883dd032878ea4c037f67b769635" } - } + }, + { + // Season 4 https://github.com/KomodoPlatform/dPoW/blob/s4/iguana/3rd_party + {"alien_AR", "024f20c096b085308e21893383f44b4faf1cdedea9ad53cc7d7e7fbfa0c30c1e71" }, + {"alien_EU", "022b85908191788f409506ebcf96a892f3274f352864c3ed566c5a16de63953236" }, + {"alien_NA", "022f62b56ddfd07c9860921c701285ac39bb3ac8f6f083d1b59c8f4943be3de162" }, + {"alright_DEV", "03b6f9493658bdd102503585a08ae642b49d6a68fb69ac3626f9737cd7581abdfa" }, + {"artemii235_DEV", "037a20916d2e9ea575300ac9d729507c23a606b9a200c8e913d7c9832f912a1fa7" }, + {"chainmakers_NA", "028803e07bcc521fde264b7191a944f9b3612e8ee4e24a99bcd903f6976240839a" }, + {"chainzilla_SH", "0311dde03c2dd654ce78323b718ed3ad73a464d1bde97820f3395f54788b5420dd" }, + {"chmex_AR", "030cd487e10fbf142e0e8d582e702ecb775f378569c3cb5acd0ff97b6b12803588" }, + {"chmex_EU", "030bf7bd7ad0515c33b5d5d9a91e0729baf801b9002f80495ae535ea1cebb352cb" }, + {"cipi_EU", "026f4f66385daaf8313ef30ffe4988e7db497132682dca185a70763d93e1417d9d" }, + {"cipi_NA", "03f4e69edcb4fa3b2095cb8cb1ca010f4ec4972eac5d8822397e5c8d87aa21a739" }, + {"daemonfox_NA", "023c7584b1006d4a62a4b4c9c1ede390a3789316547897d5ed49ff9385a3acb411" }, + {"dappvader_SH", "025199bc04bcb8a17976d9fe8bc87763a6150c2727321aa59bf34a2b49f2f3a0ce" }, + {"decker_AR", "02a85540db8d41c7e60bf0d33d1364b4151cad883dd032878ea4c037f67b769635" }, + {"decker_DEV", "02fca8ee50e49f480de275745618db7b0b3680b0bdcce7dcae7d2e0fd5c3345744" }, + {"decker_EU", "027777775b89ff548c3be54fb0c9455437d87f38bfce83bdef113899881b219c9e" }, + {"dragonhound_NA", "029912212d370ee0fb4d38eefd8bfcd8ab04e2c3b0354020789c29ddf2a35c72d6" }, + {"dudezmobi_AR", "033c121d3f8d450174674a73f3b7f140b2717a7d51ea19ee597e2e8e8f9d5ed87f" }, + {"etszombi_AR", "03bfcbca83f11e622fa4eed9a1fa25dba377981ea3b22e3d0a4015f9a932af9272" }, + {"etszombi_EU", "03a5c083c78ba397970f20b544a01c13e7ed36ca8a5ae26d5fe7bd38b92b6a0c94" }, + {"fullmoon_AR", "03639bc56d3fecf856f17759a441c5893668e7c2d460f3d216798a413cd6766bb2" }, + {"fullmoon_NA", "03e388bcc579ac2675f8fadfa921eec186dcea8d2b43de1eed6caba23d5a962b74" }, + {"fullmoon_SH", "03a5cfda2b097c808834ccdd805828c811b519611feabdfe6b3644312e53f6748f" }, + {"gcharang_SH", "02a654037d12cdd609f4fad48e15ec54538e03f61fdae1acb855f16ebacac6bd73" }, + {"greer_NA", "0262da6aaa0b295b8e2f120035924758a4a630f899316dc63ee15ef03e9b7b2b23" }, + {"indenodes_AR", "0242778789986d614f75bcf629081651b851a12ab1cc10c73995b27b90febb75a2" }, + {"indenodes_EU", "03a416533cace0814455a1bb1cd7861ce825a543c6f6284a432c4c8d8875b7ace9" }, + {"indenodes_NA", "02b3908eda4078f0e9b6704451cdc24d418e899c0f515fab338d7494da6f0a647b" }, + {"indenodes_SH", "031d1584cf0eb4a2d314465e49e2677226b1615c3718013b8d6b4854c15676a58c" }, + {"karasugoi_NA", "02f803e6f159824a181cc5d709f3d1e7ff65f19e1899920724aeb4e3d2d869f911" }, + {"madmax_AR", "027afddbcf690230dd8d435ec16a7bfb0083e6b77030f763437f291dfc40a579d0" }, + {"madmax_EU", "0397ec3a4ad84b3009566d260c89f1c4404e86e5d044964747c9371277e38f5995" }, + {"madmax_NA", "036d3afebe1eab09f4c38c3ee6a4659ad390f3df92787c11437a58c59a29e408e6" }, + {"marmarachain_AR", "028690ca1e3afdf8a38b421f6a41f5ff407afc96d5a7a6a488330aae26c8b086bb" }, + {"mcrypt_SH", "027a4ca7b11d3456ff558c08bb04483a89c7f383448461fd0b6b3b07424aabe9a4" }, + {"metaphilibert_AR", "0239e34ad22957bbf4c8df824401f237b2afe8d40f7a645ecd43e8f27dde1ab0da" }, + {"metaphilibert_SH", "03b21ff042bf1730b28bde43f44c064578b41996117ac7634b567c3773089e3be3" }, + {"mihailo_EU", "036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252" }, + {"mrlynch_AR", "03e67440141f53a08684c329ebc852b018e41f905da88e52aa4a6dc5aa4b12447a" }, + {"mylo_SH", "026d5f29d09ff3f33e14db4811606249b2438c6bcf964876714f81d1f2d952acde" }, + {"node9_EU", "0392e4c9400e69f28c6b9e89d586da69d5a6af7702f1045eaa6ebc1996f0496e1f" }, + {"nodeone_NA", "0310a249c6c2dcc29f2135715138a9ddb8e01c0eab701cbd0b96d9cec660dbdc58" }, + {"nutellalicka_SH", "0284c4d3cb97dd8a32d10fb32b1855ae18cf845dad542e3b8937ca0e998fb54ecc" }, + {"oszy_EU", "03c53bd421de4a29ce68c8cc83f802e1181e77c08f8f16684490d61452ea8d023a" }, + {"patchkez_SH", "028c08db6e7242681f50db6c234fe3d6e12fb1a915350311be26373bac0d457d49" }, + {"pbca26_NA", "03c18431bb6bc95672f640f19998a196becd2851d5dcba4795fe8d85b7d77eab81" }, + {"peer2cloud_AR", "0243958faf9ae4d43b598b859ddc595c170c4cf50f8e4517d660ae5bc72aeb821b" }, + {"phba2061_EU", "03369187ce134bd7793ee34af7756fe1ab27202e09306491cdd5d8ad2c71697937" }, + {"phm87_SH", "03889a10f9df2caef57220628515693cf25316fe1b0693b0241419e75d0d0e66ed" }, + {"pirate_EU", "0240011b95cde819f298fe0f507b2260c9fecdab784924076d4d1e54c522103cb1" }, + {"pirate_NA", "02ad7ef25d2dd461e361120cd3efe7cbce5e9512c361e9185aac33dd303d758613" }, + {"pungocloud_SH", "02641c36ae6747b88150a463a1fe65cf7a9d1c00a64387c73f296f0b64e77c7d3f" }, + {"smdmitry_AR", "0397b7584cb29717b721c0c587d4462477efc1f36a56921f133c9d17b0cd7f278a" }, + {"starfleet_EU", "03c6e047218f34644ccba67e317b9da5d28e68bbbb6b9973aef1281d2bafa46496" }, + {"strob_NA", "02285bf2f9e96068ecac14bc6f770e394927b4da9f5ba833eaa9468b5d47f203a3" }, + {"strob_SH", "0213751a1c59d3489ca85b3d62a3d606dcef7f0428aa021c1978ea16fb38a2fad6" }, + {"swisscertifiers_EU", "02e7722ebba9f8b5ebfb4e87d4fa58cc75aef677535b9cfc060c7d9471aacd9c9e" }, + {"titomane_AR", "03958bd8d13fe6946b8d0d0fbbc3861c72542560d0276e80a4c6b5fe55bc758b81" }, + {"titomane_EU", "02276090e483db1a01a802456b10831b3b6e0a6ad3ece9b2a01f4aad0e480c8edc" }, + {"titomane_SH", "02abf206bafc8048dbdc042b8eb6b1e356ea5dbe149eae3532b4811d4905e5cf01" }, + {"tonyl_AR", "0229e499e3f2e065ced402ceb8aaf3d5ab8bd3793aa074305e9fa30772ce604908" }, + {"tonyl_DEV", "0258b77d7dcfc6c2628b0b6b438951a6e74201fb2cd180a795e4c37fcf8e78a678" }, + {"webworker01_NA", "02de90c720c007229374772505a43917a84ed129d5fbcfa4949cc2e9b563351124" }, + {"zatjum_SH", "0241c5660ca540780be66603b1791127a1261d56abbcb7562c297eec8e4fc078fb" } + } }; #define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7))) diff --git a/src/komodo_notary.h b/src/komodo_notary.h index 8114a86af..d5591aa29 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * diff --git a/src/main.cpp b/src/main.cpp index 17b89a76b..fb53e2c28 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -114,6 +114,7 @@ bool fAlerts = DEFAULT_ALERTS; /* If the tip is older than this (in seconds), the node is considered to be in initial block download. */ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; +bool ishush3 = strncmp(ASSETCHAINS_SYMBOL, "HUSH3",5) == 0 ? true : false; unsigned int expiryDelta = DEFAULT_TX_EXPIRY_DELTA; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; @@ -1224,9 +1225,9 @@ bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockInde if (IsExpiredTx(tx, nHeight)) { // Don't increase banscore if the transaction only just expired int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? (dosLevel > 10 ? dosLevel : 10) : 0; - string strHex = EncodeHexTx(tx); + //string strHex = EncodeHexTx(tx); //fprintf(stderr, "transaction exipred.%s\n",strHex.c_str()); - return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n txhex.%s",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight,strHex), REJECT_INVALID, "tx-overwinter-expired"); + return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight), REJECT_INVALID, "tx-overwinter-expired"); } } @@ -1327,7 +1328,7 @@ bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockInde )) { librustzcash_sapling_verification_ctx_free(ctx); - fprintf(stderr,"%s: Invalid sapling binding sig! tx=%s valueBalance=%li, bindingSig.size=%d\n", __func__, tx.GetHash().ToString().c_str(), tx.valueBalance, tx.bindingSig.size() ); + fprintf(stderr,"%s: Invalid sapling binding sig! tx=%s valueBalance=%li, bindingSig.size=%li\n", __func__, tx.GetHash().ToString().c_str(), tx.valueBalance, tx.bindingSig.size() ); return state.DoS(100, error("ContextualCheckTransaction(): Sapling binding signature invalid"), REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid"); } @@ -1367,16 +1368,8 @@ bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState if (!CheckTransactionWithoutProofVerification(tiptime,tx, state)) { return false; - } else { - // Ensure that zk-SNARKs v|| y - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { - if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { - return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), - REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); - } - } - return true; } + return true; } int32_t komodo_isnotaryvout(char *coinaddr,uint32_t tiptime) // from ac_private chains only @@ -1585,17 +1578,18 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio else if ( joinsplit.vpub_new != 0 && joinsplit.vpub_old == 0 ) z_t++; } + if ( ASSETCHAINS_PRIVATE != 0 && invalid_private_taddr != 0 ) { static uint32_t counter; if ( counter++ < 10 ) fprintf(stderr,"found taddr in private chain: z_z.%d z_t.%d t_z.%d vinsize.%d\n",z_z,z_t,t_z,(int32_t)tx.vin.size()); if ( z_t == 0 || z_z != 0 || t_z != 0 || tx.vin.size() != 0 ) - return state.DoS(100, error("CheckTransaction(): this is a private chain, only sprout -> taddr allowed until deadline"),REJECT_INVALID, "bad-txns-acprivacy-chain"); + return state.DoS(100, error("CheckTransaction(): this is a private chain, sending to taddrs not allowed"),REJECT_INVALID, "bad-txns-acprivacy-chain"); } if ( ASSETCHAINS_TXPOW != 0 ) { - // genesis coinbase 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + // BTC genesis coinbase 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b uint256 txid = tx.GetHash(); if ( ((ASSETCHAINS_TXPOW & 2) != 0 && iscoinbase != 0) || ((ASSETCHAINS_TXPOW & 1) != 0 && iscoinbase == 0) ) { @@ -1741,6 +1735,23 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee, int dosLevel) { AssertLockHeld(cs_main); + const uint32_t z2zTransitionWindow = 10; + const uint32_t z2zTransitionStart = 340000 - z2zTransitionWindow; + const uint32_t nHeight = chainActive.Height(); + + // This only applies to HUSH3, other chains can start off z2z via ac_private=1 + if(ishush3) { + if((nHeight >= z2zTransitionStart) || (nHeight <= 340000)) { + // During the z2z transition window, only coinbase tx's as part of blocks are allowed + // Theory: We want an empty mempool at our fork block height, and the only way to assure that + // is to have an empty mempool for a few previous blocks, to take care of potential re-orgs + // and edge cases. This empty mempool assures there will be no transactions involving taddrs + // stuck in the mempool, when the z2z rule takes effect. + // Thanks to jl777 for helping design this + fprintf(stderr,"%s: rejecting all tx's during z2z transition window at height=%d\n", __func__,nHeight); + return false; + } + } if (pfMissingInputs) *pfMissingInputs = false; uint32_t tiptime; @@ -1894,10 +1905,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // are the joinsplit's requirements met? - if (!view.HaveJoinSplitRequirements(tx)) + if (!view.HaveShieldedRequirements(tx)) { //fprintf(stderr,"accept failure.2\n"); - return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); + return state.Invalid(error("AcceptToMemoryPool: shielded requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); } // Bring the best block into scope @@ -2718,9 +2729,9 @@ namespace Consensus { if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - // are the JoinSplit's requirements met? - if (!inputs.HaveJoinSplitRequirements(tx)) - return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString())); + // are the shielded requirements met? + if (!inputs.HaveShieldedRequirements(tx)) + return state.Invalid(error("CheckInputs(): %s shielded requirements not met", tx.GetHash().ToString())); CAmount nValueIn = 0; CAmount nFees = 0; @@ -3349,6 +3360,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return(false); //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->GetHeight()); AssertLockHeld(cs_main); + + bool ishush3 = strncmp(ASSETCHAINS_SYMBOL, "HUSH3",5) == 0 ? true : false; + if(!ASSETCHAINS_PRIVATE && ishush3) { + unsigned int nHeight = pindex->GetHeight(); + if(nHeight >= 340000) { + // At startup, HUSH3 doesn't know a block height yet and so we must wait until + // connecting a block + fprintf(stderr, "%s: Going full z2z at height %d!\n",__func__,nHeight); + ASSETCHAINS_PRIVATE = 1; + } + } + bool fExpensiveChecks = true; if (fCheckpointsEnabled) { CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); @@ -3442,7 +3465,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Before the genesis block, there was an empty tree SproutMerkleTree tree; pindex->hashSproutAnchor = tree.root(); - // The genesis block contained no JoinSplits + // The genesis block contained no JoinSplits, lulz pindex->hashFinalSproutRoot = pindex->hashSproutAnchor; } return true; @@ -3501,13 +3524,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // This should never fail: we should always be able to get the root // that is on the tip of our chain - assert(view.GetSproutAnchorAt(old_sprout_tree_root, sprout_tree)); + //assert(view.GetSproutAnchorAt(old_sprout_tree_root, sprout_tree)); - { + //{ // Consistency check: the root of the tree we're given should // match what we asked for. - assert(sprout_tree.root() == old_sprout_tree_root); - } + //assert(sprout_tree.root() == old_sprout_tree_root); + //} SaplingMerkleTree sapling_tree; assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); @@ -3535,10 +3558,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); } - // are the JoinSplit's requirements met? - if (!view.HaveJoinSplitRequirements(tx)) - return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), - REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); + // are the shielded requirements met? + if (!view.HaveShieldedRequirements(tx)) + return state.DoS(100, error("ConnectBlock(): shielded requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); if (fAddressIndex || fSpentIndex) { @@ -3636,21 +3658,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } - //if ( ASSETCHAINS_SYMBOL[0] == 0 ) - // komodo_earned_interest(pindex->GetHeight(),sum); CTxUndo undoDummy; if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); } UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->GetHeight()); - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { - BOOST_FOREACH(const uint256 ¬e_commitment, joinsplit.commitments) { - // Insert the note commitments into our temporary tree. - - sprout_tree.append(note_commitment); - } - } BOOST_FOREACH(const OutputDescription &outputDescription, tx.vShieldedOutput) { sapling_tree.append(outputDescription.cm); @@ -3660,7 +3673,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - view.PushAnchor(sprout_tree); + //view.PushAnchor(sprout_tree); view.PushAnchor(sapling_tree); if (!fJustCheck) { pindex->hashFinalSproutRoot = sprout_tree.root(); @@ -4210,7 +4223,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * mempool.removeForBlock(pblock->vtx, pindexNew->GetHeight(), txConflicted, !IsInitialBlockDownload()); // Remove transactions that expire at new block height from mempool - mempool.removeExpired(pindexNew->GetHeight()); + auto ids = mempool.removeExpired(pindexNew->GetHeight()); + + for (auto id : ids) { + uiInterface.NotifyTxExpiration(id); + } // Update chainActive & related variables. UpdateTip(pindexNew); @@ -6089,7 +6106,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) - throw runtime_error("LoadBlockIndex(): new CBlockIndex failed"); + throw runtime_error("InsertBlockIndex(): new CBlockIndex failed"); mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); //fprintf(stderr,"inserted to block index %s\n",hash.ToString().c_str()); @@ -6306,6 +6323,13 @@ bool static LoadBlockIndexDB() chainActive.SetTip(it->second); + // Try to detect if we are z2z based on height of blocks on disk + // This helps to set it correctly on startup before a new block is connected + if(ishush3 && chainActive.Height() >= 340000) { + LogPrintf("%s: enabled ac_private=1 at height=%d\n", __func__, chainActive.Height()); + ASSETCHAINS_PRIVATE = 1; + } + // Set hashFinalSproutRoot for the end of best chain it->second->hashFinalSproutRoot = pcoinsTip->GetBestAnchor(SPROUT); @@ -6368,7 +6392,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; - // No need to verify JoinSplits twice + // No need to verify shielded req's twice auto verifier = libzcash::ProofVerifier::Disabled(); //fprintf(stderr,"start VerifyDB %u\n",(uint32_t)time(NULL)); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) diff --git a/src/metrics.cpp b/src/metrics.cpp index ff0863a19..adbef02f2 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -288,7 +288,7 @@ int printMiningStatus(bool mining) } else if (IsInitialBlockDownload()) { std::cout << _("Mining is paused while downloading blocks.") << std::endl; } else { - std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl; + std::cout << _("Mining is paused, enhance your calm") << std::endl; } } lines++; diff --git a/src/miner.cpp b/src/miner.cpp index 8d3c5d4c4..a91fa9527 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -340,7 +340,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 if (!mempool.mapTx.count(txin.prevout.hash)) { LogPrintf("ERROR: mempool transaction missing input\n"); - if (fDebug) assert("mempool transaction missing input" == 0); + // if (fDebug) assert("mempool transaction missing input" == 0); fMissingInputs = true; if (porphan) vOrphan.pop_back(); diff --git a/src/paymentdisclosure.cpp b/src/paymentdisclosure.cpp deleted file mode 100644 index eb55a0536..000000000 --- a/src/paymentdisclosure.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "paymentdisclosure.h" - -#include "key_io.h" -#include "util.h" - -std::string PaymentDisclosureInfo::ToString() const { - return strprintf("PaymentDisclosureInfo(version=%d, esk=%s, joinSplitPrivKey=, address=%s)", - version, esk.ToString(), EncodePaymentAddress(zaddr)); -} - -std::string PaymentDisclosure::ToString() const { - std::string s = HexStr(payloadSig.begin(), payloadSig.end()); - return strprintf("PaymentDisclosure(payload=%s, payloadSig=%s)", payload.ToString(), s); -} - -std::string PaymentDisclosurePayload::ToString() const { - return strprintf("PaymentDisclosurePayload(version=%d, esk=%s, txid=%s, js=%d, n=%d, address=%s, message=%s)", - version, esk.ToString(), txid.ToString(), js, n, EncodePaymentAddress(zaddr), message); -} - -PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const PaymentDisclosureKey &key, const PaymentDisclosureInfo &info, const std::string &message) -{ - // Populate payload member variable - payload.version = info.version; // experimental = 0, production = 1 etc. - payload.esk = info.esk; - payload.txid = key.hash; - payload.js = key.js; - payload.n = key.n; - payload.zaddr = info.zaddr; - payload.message = message; - - // Serialize and hash the payload to generate a signature - uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); - - LogPrint("paymentdisclosure", "Payment Disclosure: signing raw payload = %s\n", dataToBeSigned.ToString()); - - // Prepare buffer to store ed25519 key pair in libsodium-compatible format - unsigned char bufferKeyPair[64]; - memcpy(&bufferKeyPair[0], info.joinSplitPrivKey.begin(), 32); - memcpy(&bufferKeyPair[32], joinSplitPubKey.begin(), 32); - - // Compute payload signature member variable - if (!(crypto_sign_detached(payloadSig.data(), NULL, - dataToBeSigned.begin(), 32, - &bufferKeyPair[0] - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(payloadSig.data(), - dataToBeSigned.begin(), 32, - joinSplitPubKey.begin()) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - std::string sigString = HexStr(payloadSig.data(), payloadSig.data() + payloadSig.size()); - LogPrint("paymentdisclosure", "Payment Disclosure: signature = %s\n", sigString); -} diff --git a/src/paymentdisclosure.h b/src/paymentdisclosure.h deleted file mode 100644 index 28a1d4cdc..000000000 --- a/src/paymentdisclosure.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef ZCASH_PAYMENTDISCLOSURE_H -#define ZCASH_PAYMENTDISCLOSURE_H - -#include "uint256.h" -#include "clientversion.h" -#include "serialize.h" -#include "streams.h" -#include "version.h" - -// For JSOutPoint -#include "wallet/wallet.h" - -#include -#include -#include - - -// Ensure that the two different protocol messages, payment disclosure blobs and transactions, -// which are signed with the same key, joinSplitPrivKey, have disjoint encodings such that an -// encoding from one context will be rejected in the other. We know that the set of valid -// transaction versions is currently ({1..INT32_MAX}) so we will use a negative value for -// payment disclosure of -10328976 which in hex is 0xFF626470. Serialization is in little endian -// format, so a payment disclosure hex string begins 706462FF, which in ISO-8859-1 is "pdbÿ". -#define PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES -10328976 - -#define PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL 0 - -#define PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX "zpd:" - -typedef JSOutPoint PaymentDisclosureKey; - -struct PaymentDisclosureInfo { - uint8_t version; // 0 = experimental, 1 = first production version, etc. - uint256 esk; // zcash/NoteEncryption.cpp - uint256 joinSplitPrivKey; // primitives/transaction.h - // ed25519 - not tied to implementation e.g. libsodium, see ed25519 rfc - - libzcash::SproutPaymentAddress zaddr; - - PaymentDisclosureInfo() : version(PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) { - } - - PaymentDisclosureInfo(uint8_t v, uint256 esk, uint256 key, libzcash::SproutPaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(version); - READWRITE(esk); - READWRITE(joinSplitPrivKey); - READWRITE(zaddr); - } - - std::string ToString() const; - - friend bool operator==(const PaymentDisclosureInfo& a, const PaymentDisclosureInfo& b) { - return (a.version == b.version && a.esk == b.esk && a.joinSplitPrivKey == b.joinSplitPrivKey && a.zaddr == b.zaddr); - } - - friend bool operator!=(const PaymentDisclosureInfo& a, const PaymentDisclosureInfo& b) { - return !(a == b); - } - -}; - - -struct PaymentDisclosurePayload { - int32_t marker = PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES; // to be disjoint from transaction encoding - uint8_t version; // 0 = experimental, 1 = first production version, etc. - uint256 esk; // zcash/NoteEncryption.cpp - uint256 txid; // primitives/transaction.h - uint64_t js; // Index into CTransaction.vjoinsplit - uint8_t n; // Index into JSDescription fields of length ZC_NUM_JS_OUTPUTS - libzcash::SproutPaymentAddress zaddr; // zcash/Address.hpp - std::string message; // parameter to RPC call - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(marker); - READWRITE(version); - READWRITE(esk); - READWRITE(txid); - READWRITE(js); - READWRITE(n); - READWRITE(zaddr); - READWRITE(message); - } - - std::string ToString() const; - - friend bool operator==(const PaymentDisclosurePayload& a, const PaymentDisclosurePayload& b) { - return ( - a.version == b.version && - a.esk == b.esk && - a.txid == b.txid && - a.js == b.js && - a.n == b.n && - a.zaddr == b.zaddr && - a.message == b.message - ); - } - - friend bool operator!=(const PaymentDisclosurePayload& a, const PaymentDisclosurePayload& b) { - return !(a == b); - } -}; - -struct PaymentDisclosure { - PaymentDisclosurePayload payload; - std::array payloadSig; - // We use boost array because serialize doesn't like char buffer, otherwise we could do: unsigned char payloadSig[64]; - - PaymentDisclosure() {}; - PaymentDisclosure(const PaymentDisclosurePayload payload, const std::array sig) : payload(payload), payloadSig(sig) {}; - PaymentDisclosure(const uint256& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message); - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(payload); - READWRITE(payloadSig); - } - - std::string ToString() const; - - friend bool operator==(const PaymentDisclosure& a, const PaymentDisclosure& b) { - return (a.payload == b.payload && a.payloadSig == b.payloadSig); - } - - friend bool operator!=(const PaymentDisclosure& a, const PaymentDisclosure& b) { - return !(a == b); - } -}; - - - -typedef std::pair PaymentDisclosureKeyInfo; - - -#endif // ZCASH_PAYMENTDISCLOSURE_H diff --git a/src/paymentdisclosuredb.cpp b/src/paymentdisclosuredb.cpp deleted file mode 100644 index 8840dcda0..000000000 --- a/src/paymentdisclosuredb.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "paymentdisclosuredb.h" - -#include "util.h" -#include "dbwrapper.h" - -#include - -using namespace std; - -static boost::filesystem::path emptyPath; - -/** - * Static method to return the shared/default payment disclosure database. - */ -shared_ptr PaymentDisclosureDB::sharedInstance() { - // Thread-safe in C++11 and gcc 4.3 - static shared_ptr ptr = std::make_shared(); - return ptr; -} - -// C++11 delegated constructor -PaymentDisclosureDB::PaymentDisclosureDB() : PaymentDisclosureDB(emptyPath) { -} - -PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath) { - boost::filesystem::path path(dbPath); - if (path.empty()) { - path = GetDataDir() / "paymentdisclosure"; - LogPrintf("PaymentDisclosure: using default path for database: %s\n", path.string()); - } else { - LogPrintf("PaymentDisclosure: using custom path for database: %s\n", path.string()); - } - - TryCreateDirectory(path); - options.create_if_missing = true; - leveldb::Status status = leveldb::DB::Open(options, path.string(), &db); - dbwrapper_private::HandleError(status); // throws exception - LogPrintf("PaymentDisclosure: Opened LevelDB successfully\n"); -} - -PaymentDisclosureDB::~PaymentDisclosureDB() { - if (db != nullptr) { - delete db; - } -} - -bool PaymentDisclosureDB::Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info) -{ - if (db == nullptr) { - return false; - } - - std::lock_guard guard(lock_); - - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(GetSerializeSize(ssValue, info)); - ssValue << info; - leveldb::Slice slice(&ssValue[0], ssValue.size()); - - leveldb::Status status = db->Put(writeOptions, key.ToString(), slice); - dbwrapper_private::HandleError(status); - return true; -} - -bool PaymentDisclosureDB::Get(const PaymentDisclosureKey& key, PaymentDisclosureInfo& info) -{ - if (db == nullptr) { - return false; - } - - std::lock_guard guard(lock_); - - std::string strValue; - leveldb::Status status = db->Get(readOptions, key.ToString(), &strValue); - if (!status.ok()) { - if (status.IsNotFound()) - return false; - LogPrintf("PaymentDisclosure: LevelDB read failure: %s\n", status.ToString()); - dbwrapper_private::HandleError(status); - } - - try { - CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); - ssValue >> info; - } catch (const std::exception&) { - return false; - } - return true; -} diff --git a/src/paymentdisclosuredb.h b/src/paymentdisclosuredb.h deleted file mode 100644 index 9352cac8f..000000000 --- a/src/paymentdisclosuredb.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef ZCASH_PAYMENTDISCLOSUREDB_H -#define ZCASH_PAYMENTDISCLOSUREDB_H - -#include "paymentdisclosure.h" - -#include -#include -#include -#include -#include - -#include - -#include - - -class PaymentDisclosureDB -{ -protected: - leveldb::DB* db = nullptr; - leveldb::Options options; - leveldb::ReadOptions readOptions; - leveldb::WriteOptions writeOptions; - mutable std::mutex lock_; - -public: - static std::shared_ptr sharedInstance(); - - PaymentDisclosureDB(); - PaymentDisclosureDB(const boost::filesystem::path& dbPath); - ~PaymentDisclosureDB(); - - bool Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info); - bool Get(const PaymentDisclosureKey& key, PaymentDisclosureInfo& info); -}; - - -#endif // ZCASH_PAYMENTDISCLOSUREDB_H diff --git a/src/primitives/block.h b/src/primitives/block.h index ea93d6f50..9a6ddf100 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin Core developers -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -23,7 +23,7 @@ #define BITCOIN_PRIMITIVES_BLOCK_H #include "primitives/transaction.h" -#include "primitives/nonce.h" +//#include "primitives/nonce.h" #include "serialize.h" #include "uint256.h" #include "arith_uint256.h" diff --git a/src/primitives/nonce.cpp b/src/primitives/nonce.cpp deleted file mode 100644 index a1fbd667d..000000000 --- a/src/primitives/nonce.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2019 The Hush Developers -// Copyright (c) 2018 Michael Toutonghi -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "hash.h" -#include "nonce.h" -#include - diff --git a/src/primitives/nonce.h b/src/primitives/nonce.h deleted file mode 100644 index b17a9f01b..000000000 --- a/src/primitives/nonce.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2019 Hush Developers -// Copyright (c) 2018 Michael Toutonghi -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#ifndef BITCOIN_PRIMITIVES_NONCE_H -#define BITCOIN_PRIMITIVES_NONCE_H - -#include "serialize.h" -#include "uint256.h" -#include "arith_uint256.h" - - -#endif // BITCOIN_PRIMITIVES_NONCE_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 2cde04f2c..53aeeda99 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -26,83 +27,6 @@ #include "librustzcash.h" -JSDescription::JSDescription( - ZCJoinSplit& params, - const uint256& joinSplitPubKey, - const uint256& anchor, - const std::array& inputs, - const std::array& outputs, - CAmount vpub_old, - CAmount vpub_new, - bool computeProof, - uint256 *esk // payment disclosure -) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor) -{ - std::array notes; - - proof = params.prove( - inputs, - outputs, - notes, - ciphertexts, - ephemeralKey, - joinSplitPubKey, - randomSeed, - macs, - nullifiers, - commitments, - vpub_old, - vpub_new, - anchor, - computeProof, - esk // payment disclosure - ); -} - -JSDescription JSDescription::Randomized( - ZCJoinSplit& params, - const uint256& joinSplitPubKey, - const uint256& anchor, - std::array& inputs, - std::array& outputs, - std::array& inputMap, - std::array& outputMap, - CAmount vpub_old, - CAmount vpub_new, - bool computeProof, - uint256 *esk, // payment disclosure - std::function gen -) -{ - // Randomize the order of the inputs and outputs - inputMap = {0, 1}; - outputMap = {0, 1}; - - assert(gen); - - MappedShuffle(inputs.begin(), inputMap.begin(), ZC_NUM_JS_INPUTS, gen); - MappedShuffle(outputs.begin(), outputMap.begin(), ZC_NUM_JS_OUTPUTS, gen); - - return JSDescription( - params, joinSplitPubKey, anchor, inputs, outputs, - vpub_old, vpub_new, computeProof, - esk // payment disclosure - ); -} - -bool JSDescription::Verify( - ZCJoinSplit& params, - libzcash::ProofVerifier& verifier, - const uint256& joinSplitPubKey -) const { - return false; -} - -uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& joinSplitPubKey) const -{ - return params.h_sig(randomSeed, nullifiers, joinSplitPubKey); -} - std::string COutPoint::ToString() const { return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 2ece7b255..7a46b9c00 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -44,6 +44,7 @@ #include "zcash/Zcash.h" #include "zcash/JoinSplit.hpp" #include "zcash/Proof.hpp" +#include "zcash/Note.hpp" extern uint32_t ASSETCHAINS_MAGIC; extern std::string ASSETCHAINS_SELFIMPORT; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 76375b80c..faed5b957 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1668,9 +1668,10 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& my obj.push_back(Pair("chainwork", chainActive.LastTip()->chainPower.chainWork.GetHex())); obj.push_back(Pair("pruned", fPruneMode)); - SproutMerkleTree tree; - pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); - obj.push_back(Pair("commitments", static_cast(tree.size()))); + //SproutMerkleTree tree; + //pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); + //obj.push_back(Pair("commitments", static_cast(tree.size()))); + obj.push_back(Pair("commitments", 0)); CBlockIndex* tip = chainActive.LastTip(); UniValue valuePools(UniValue::VARR); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4107ad6f8..4f11a476f 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -139,6 +139,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_listunspent", 2 }, { "z_listunspent", 3 }, { "z_getbalance", 1}, + { "z_getnotescount", 0}, { "z_gettotalbalance", 0}, { "z_gettotalbalance", 1}, { "z_gettotalbalance", 2}, @@ -163,8 +164,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "kvupdate", 4 }, { "z_importkey", 2 }, { "z_importviewingkey", 2 }, - { "z_getpaymentdisclosure", 1}, - { "z_getpaymentdisclosure", 2}, { "z_listsentbyaddress", 1}, { "z_listsentbyaddress", 2}, { "z_listsentbyaddress", 3}, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index fc4b39ef0..a41a8169d 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -203,7 +204,7 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) " \"version\": xxxxx, (numeric) the server version\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total Komodo balance of the wallet\n" + " \"balance\": xxxxxxx, (numeric) the total Hush balance of the wallet\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" " \"timeoffset\": xxxxx, (numeric) the time offset\n" " \"connections\": xxxxx, (numeric) the number of connections\n" @@ -536,14 +537,14 @@ UniValue validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk { if (fHelp || params.size() != 1) throw runtime_error( - "validateaddress \"komodoaddress\"\n" - "\nReturn information about the given Komodo address.\n" + "validateaddress \"hushaddress\"\n" + "\nReturn information about the given Hush address.\n" "\nArguments:\n" - "1. \"komodoaddress\" (string, required) The Komodo address to validate\n" + "1. \"hushaddress\" (string, required) The Hush address to validate\n" "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" - " \"address\" : \"komodoaddress\", (string) The Komodo address validated\n" + " \"address\" : \"hushaddress\", (string) The Hush address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" " \"ismine\" : true|false, (boolean) If the address is yours or not\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" @@ -589,43 +590,6 @@ UniValue validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk } -class DescribePaymentAddressVisitor : public boost::static_visitor -{ -public: - UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); } - - UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("type", "sprout")); - obj.push_back(Pair("payingkey", zaddr.a_pk.GetHex())); - obj.push_back(Pair("transmissionkey", zaddr.pk_enc.GetHex())); -#ifdef ENABLE_WALLET - if (pwalletMain) { - obj.push_back(Pair("ismine", pwalletMain->HaveSproutSpendingKey(zaddr))); - } -#endif - return obj; - } - - UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("type", "sapling")); - obj.push_back(Pair("diversifier", HexStr(zaddr.d))); - obj.push_back(Pair("diversifiedtransmissionkey", zaddr.pk_d.GetHex())); -#ifdef ENABLE_WALLET - if (pwalletMain) { - libzcash::SaplingIncomingViewingKey ivk; - libzcash::SaplingFullViewingKey fvk; - bool isMine = pwalletMain->GetSaplingIncomingViewingKey(zaddr, ivk) && - pwalletMain->GetSaplingFullViewingKey(ivk, fvk) && - pwalletMain->HaveSaplingSpendingKey(fvk); - obj.push_back(Pair("ismine", isMine)); - } -#endif - return obj; - } -}; - UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() != 1) @@ -662,11 +626,25 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& my UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); - if (isValid) + auto zaddr = boost::get(&address); + if (isValid && (zaddr != nullptr)) { ret.push_back(Pair("address", strAddress)); - UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(), address); - ret.pushKVs(detail); + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("type", "sapling")); + obj.push_back(Pair("diversifier", HexStr(zaddr->d))); + obj.push_back(Pair("diversifiedtransmissionkey", zaddr->pk_d.GetHex())); +#ifdef ENABLE_WALLET + if (pwalletMain) { + libzcash::SaplingIncomingViewingKey ivk; + libzcash::SaplingFullViewingKey fvk; + bool isMine = pwalletMain->GetSaplingIncomingViewingKey(*zaddr, ivk) && + pwalletMain->GetSaplingFullViewingKey(ivk, fvk) && + pwalletMain->HaveSaplingSpendingKey(fvk); + obj.push_back(Pair("ismine", isMine)); + } +#endif + ret.pushKVs(obj); } return ret; } @@ -745,9 +723,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp, const CPubKey& mypk) "\nArguments:\n" "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" - "2. \"keys\" (string, required) A json array of keys which are Komodo addresses or hex-encoded public keys\n" + "2. \"keys\" (string, required) A json array of keys which are Hush addresses or hex-encoded public keys\n" " [\n" - " \"key\" (string) Komodo address or hex-encoded public key\n" + " \"key\" (string) Hush address or hex-encoded public key\n" " ,...\n" " ]\n" @@ -781,10 +759,10 @@ UniValue verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() != 3) throw runtime_error( - "verifymessage \"komodoaddress\" \"signature\" \"message\"\n" + "verifymessage \"hushaddress\" \"signature\" \"message\"\n" "\nVerify a signed message\n" "\nArguments:\n" - "1. \"komodoaddress\" (string, required) The Komodo address to use for the signature.\n" + "1. \"hushaddress\" (string, required) The Hush address to use for the signature.\n" "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" "3. \"message\" (string, required) The message that was signed.\n" "\nResult:\n" diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 181de29bb..56c038596 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -669,11 +669,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "z_importviewingkey", &z_importviewingkey, true }, { "wallet", "z_exportwallet", &z_exportwallet, true }, { "wallet", "z_importwallet", &z_importwallet, true }, - { "wallet", "opreturn_burn", &opreturn_burn, true }, - - // TODO: rearrange into another category - { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, - { "disclosure", "z_validatepaymentdisclosure", &z_validatepaymentdisclosure, true } + { "wallet", "opreturn_burn", &opreturn_burn, true } #endif // ENABLE_WALLET }; diff --git a/src/rpc/server.h b/src/rpc/server.h index 1cd4e993d..6568977d7 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -489,6 +489,7 @@ extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp, const C extern UniValue z_getoperationresult(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_listoperationids(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue opreturn_burn(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +extern UniValue rescan(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcmisc.cpp extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp extern UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp diff --git a/src/rpcblockchain.old b/src/rpcblockchain.old deleted file mode 100644 index a91f73a63..000000000 --- a/src/rpcblockchain.old +++ /dev/null @@ -1,1625 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "crosschain.h" -#include "base58.h" -#include "consensus/validation.h" -#include "cc/eval.h" -#include "main.h" -#include "primitives/transaction.h" -#include "rpcserver.h" -#include "sync.h" -#include "util.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "script/standard.h" - -#include - -#include - -#include - -using namespace std; - -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); -void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -int32_t komodo_longestchain(); -int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); -<<<<<<< HEAD:src/rpcblockchain.old -extern int32_t KOMODO_LONGESTCHAIN; -======= ->>>>>>> master:src/rpcblockchain.cpp - -double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficulty) -{ - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - if (blockindex == NULL) - { - if (chainActive.LastTip() == NULL) - return 1.0; - else - blockindex = chainActive.LastTip(); - } - - uint32_t bits; - if (networkDifficulty) { - bits = GetNextWorkRequired(blockindex, nullptr, Params().GetConsensus()); - } else { - bits = blockindex->nBits; - } - - uint32_t powLimit = - UintToArith256(Params().GetConsensus().powLimit).GetCompact(); - int nShift = (bits >> 24) & 0xff; - int nShiftAmount = (powLimit >> 24) & 0xff; - - double dDiff = - (double)(powLimit & 0x00ffffff) / - (double)(bits & 0x00ffffff); - - while (nShift < nShiftAmount) - { - dDiff *= 256.0; - nShift++; - } - while (nShift > nShiftAmount) - { - dDiff /= 256.0; - nShift--; - } - - return dDiff; -} - -double GetDifficulty(const CBlockIndex* blockindex) -{ - return GetDifficultyINTERNAL(blockindex, false); -} - -double GetNetworkDifficulty(const CBlockIndex* blockindex) -{ - return GetDifficultyINTERNAL(blockindex, true); -} - -static UniValue ValuePoolDesc( - const std::string &name, - const boost::optional chainValue, - const boost::optional valueDelta) -{ - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("monitored", (bool)chainValue)); - if (chainValue) { - rv.push_back(Pair("chainValue", ValueFromAmount(*chainValue))); - rv.push_back(Pair("chainValueZat", *chainValue)); - } - if (valueDelta) { - rv.push_back(Pair("valueDelta", ValueFromAmount(*valueDelta))); - rv.push_back(Pair("valueDeltaZat", *valueDelta)); - } - return rv; -} - -UniValue blockheaderToJSON(const CBlockIndex* blockindex) -{ - UniValue result(UniValue::VOBJ); - if ( blockindex == 0 ) - { - result.push_back(Pair("error", "null blockhash")); - return(result); - } - result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) - confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->nHeight,confirmations))); - result.push_back(Pair("rawconfirmations", confirmations)); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", blockindex->nVersion)); - result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); - result.push_back(Pair("time", (int64_t)blockindex->nTime)); - result.push_back(Pair("nonce", blockindex->nNonce.GetHex())); - result.push_back(Pair("solution", HexStr(blockindex->nSolution))); - result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) -{ - UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", block.GetHash().GetHex())); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) { - confirmations = chainActive.Height() - blockindex->nHeight + 1; - } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); - } - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->nHeight,confirmations))); - result.push_back(Pair("rawconfirmations", confirmations)); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); - - UniValue deltas(UniValue::VARR); - - for (unsigned int i = 0; i < block.vtx.size(); i++) { - const CTransaction &tx = block.vtx[i]; - const uint256 txhash = tx.GetHash(); - - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", txhash.GetHex())); - entry.push_back(Pair("index", (int)i)); - - UniValue inputs(UniValue::VARR); - - if (!tx.IsCoinBase()) { - - for (size_t j = 0; j < tx.vin.size(); j++) { - const CTxIn input = tx.vin[j]; - - UniValue delta(UniValue::VOBJ); - - CSpentIndexValue spentInfo; - CSpentIndexKey spentKey(input.prevout.hash, input.prevout.n); - - if (GetSpentIndex(spentKey, spentInfo)) { - if (spentInfo.addressType == 1) { - delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); - } - else if (spentInfo.addressType == 2) { - delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); - } - else { - continue; - } - delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis)); - delta.push_back(Pair("index", (int)j)); - delta.push_back(Pair("prevtxid", input.prevout.hash.GetHex())); - delta.push_back(Pair("prevout", (int)input.prevout.n)); - - inputs.push_back(delta); - } else { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Spent information not available"); - } - - } - } - - entry.push_back(Pair("inputs", inputs)); - - UniValue outputs(UniValue::VARR); - - for (unsigned int k = 0; k < tx.vout.size(); k++) { - const CTxOut &out = tx.vout[k]; - - UniValue delta(UniValue::VOBJ); - - if (out.scriptPubKey.IsPayToScriptHash()) { - vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); - delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); - - } - else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); - delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); - } - else if (out.scriptPubKey.IsPayToPublicKey() || out.scriptPubKey.IsPayToCryptoCondition()) { - CTxDestination address; - if (ExtractDestination(out.scriptPubKey, address)) - { - //vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); - //xxx delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); - delta.push_back(Pair("address", CBitcoinAddress(address).ToString())); - } - } - else { - continue; - } - - delta.push_back(Pair("satoshis", out.nValue)); - delta.push_back(Pair("index", (int)k)); - - outputs.push_back(delta); - } - - entry.push_back(Pair("outputs", outputs)); - deltas.push_back(entry); - - } - result.push_back(Pair("deltas", deltas)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", block.nNonce.GetHex())); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) -{ - UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", block.GetHash().GetHex())); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) - confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->nHeight,confirmations))); - result.push_back(Pair("rawconfirmations", confirmations)); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); - UniValue txs(UniValue::VARR); - BOOST_FOREACH(const CTransaction&tx, block.vtx) - { - if(txDetails) - { - UniValue objTx(UniValue::VOBJ); - TxToJSON(tx, uint256(), objTx); - txs.push_back(objTx); - } - else - txs.push_back(tx.GetHash().GetHex()); - } - result.push_back(Pair("tx", txs)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("nonce", block.nNonce.GetHex())); - result.push_back(Pair("solution", HexStr(block.nSolution))); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - result.push_back(Pair("anchor", blockindex->hashAnchorEnd.GetHex())); - - UniValue valuePools(UniValue::VARR); - valuePools.push_back(ValuePoolDesc("sprout", blockindex->nChainSproutValue, blockindex->nSproutValue)); - result.push_back(Pair("valuePools", valuePools)); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue getblockcount(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "\nReturns the number of blocks in the best valid block chain.\n" - "\nResult:\n" - "n (numeric) The current block count\n" - "\nExamples:\n" - + HelpExampleCli("getblockcount", "") - + HelpExampleRpc("getblockcount", "") - ); - - LOCK(cs_main); - return chainActive.Height(); -} - -UniValue getbestblockhash(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getbestblockhash\n" - "\nReturns the hash of the best (tip) block in the longest block chain.\n" - "\nResult\n" - "\"hex\" (string) the block hash hex encoded\n" - "\nExamples\n" - + HelpExampleCli("getbestblockhash", "") - + HelpExampleRpc("getbestblockhash", "") - ); - - LOCK(cs_main); - return chainActive.LastTip()->GetBlockHash().GetHex(); -} - -UniValue getdifficulty(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nResult:\n" - "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nExamples:\n" - + HelpExampleCli("getdifficulty", "") - + HelpExampleRpc("getdifficulty", "") - ); - - LOCK(cs_main); - return GetNetworkDifficulty(); -} - -bool myIsutxo_spentinmempool(uint256 txid,int32_t vout) -{ - //char *uint256_str(char *str,uint256); char str[65]; - //LOCK(mempool.cs); - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - const uint256 &hash = tx.GetHash(); - BOOST_FOREACH(const CTxIn &txin,tx.vin) - { - //fprintf(stderr,"%s/v%d ",uint256_str(str,txin.prevout.hash),txin.prevout.n); - if ( txin.prevout.n == vout && txin.prevout.hash == txid ) - return(true); - } - //fprintf(stderr,"are vins for %s\n",uint256_str(str,hash)); - } - return(false); -} - -bool mytxid_inmempool(uint256 txid) -{ - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - const uint256 &hash = tx.GetHash(); - if ( txid == hash ) - return(true); - } - return(false); -} - -UniValue mempoolToJSON(bool fVerbose = false) -{ - if (fVerbose) - { - LOCK(mempool.cs); - UniValue o(UniValue::VOBJ); - BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) - { - const uint256& hash = e.GetTx().GetHash(); - UniValue info(UniValue::VOBJ); - info.push_back(Pair("size", (int)e.GetTxSize())); - info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); - info.push_back(Pair("time", e.GetTime())); - info.push_back(Pair("height", (int)e.GetHeight())); - info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); - info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); - const CTransaction& tx = e.GetTx(); - set setDepends; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - if (mempool.exists(txin.prevout.hash)) - setDepends.insert(txin.prevout.hash.ToString()); - } - - UniValue depends(UniValue::VARR); - BOOST_FOREACH(const string& dep, setDepends) - { - depends.push_back(dep); - } - - info.push_back(Pair("depends", depends)); - o.push_back(Pair(hash.ToString(), info)); - } - return o; - } - else - { - vector vtxid; - mempool.queryHashes(vtxid); - - UniValue a(UniValue::VARR); - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); - - return a; - } -} - -UniValue getrawmempool(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getrawmempool ( verbose )\n" - "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nArguments:\n" - "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" - "\nResult: (for verbose = false):\n" - "[ (json array of string)\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - "]\n" - "\nResult: (for verbose = true):\n" - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - " \"size\" : n, (numeric) transaction size in bytes\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " }, ...\n" - "}\n" - "\nExamples\n" - + HelpExampleCli("getrawmempool", "true") - + HelpExampleRpc("getrawmempool", "true") - ); - - LOCK(cs_main); - - bool fVerbose = false; - if (params.size() > 0) - fVerbose = params[0].get_bool(); - - return mempoolToJSON(fVerbose); -} - -UniValue getblockdeltas(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error(""); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - - if(!ReadBlockFromDisk(block, pblockindex,1)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - return blockToDeltasJSON(block, pblockindex); -} - -UniValue getblockhashes(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2) - throw runtime_error( - "getblockhashes timestamp\n" - "\nReturns array of hashes of blocks within the timestamp range provided.\n" - "\nArguments:\n" - "1. high (numeric, required) The newer block timestamp\n" - "2. low (numeric, required) The older block timestamp\n" - "3. options (string, required) A json object\n" - " {\n" - " \"noOrphans\":true (boolean) will only include blocks on the main chain\n" - " \"logicalTimes\":true (boolean) will include logical timestamps with hashes\n" - " }\n" - "\nResult:\n" - "[\n" - " \"hash\" (string) The block hash\n" - "]\n" - "[\n" - " {\n" - " \"blockhash\": (string) The block hash\n" - " \"logicalts\": (numeric) The logical timestamp\n" - " }\n" - "]\n" - "\nExamples:\n" - + HelpExampleCli("getblockhashes", "1231614698 1231024505") - + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") - + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") - ); - - unsigned int high = params[0].get_int(); - unsigned int low = params[1].get_int(); - bool fActiveOnly = false; - bool fLogicalTS = false; - - if (params.size() > 2) { - if (params[2].isObject()) { - UniValue noOrphans = find_value(params[2].get_obj(), "noOrphans"); - UniValue returnLogical = find_value(params[2].get_obj(), "logicalTimes"); - - if (noOrphans.isBool()) - fActiveOnly = noOrphans.get_bool(); - - if (returnLogical.isBool()) - fLogicalTS = returnLogical.get_bool(); - } - } - - std::vector > blockHashes; - - if (fActiveOnly) - LOCK(cs_main); - - if (!GetTimestampIndex(high, low, fActiveOnly, blockHashes)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes"); - } - - UniValue result(UniValue::VARR); - - for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { - if (fLogicalTS) { - UniValue item(UniValue::VOBJ); - item.push_back(Pair("blockhash", it->first.GetHex())); - item.push_back(Pair("logicalts", (int)it->second)); - result.push_back(item); - } else { - result.push_back(it->first.GetHex()); - } - } - - return result; -} - -UniValue getblockhash(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getblockhash index\n" - "\nReturns hash of block in best-block-chain at index provided.\n" - "\nArguments:\n" - "1. index (numeric, required) The block index\n" - "\nResult:\n" - "\"hash\" (string) The block hash\n" - "\nExamples:\n" - + HelpExampleCli("getblockhash", "1000") - + HelpExampleRpc("getblockhash", "1000") - ); - - LOCK(cs_main); - - int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > chainActive.Height()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - - CBlockIndex* pblockindex = chainActive[nHeight]; - return pblockindex->GetBlockHash().GetHex(); -} - -/*uint256 _komodo_getblockhash(int32_t nHeight) -{ - uint256 hash; - LOCK(cs_main); - if ( nHeight >= 0 && nHeight <= chainActive.Height() ) - { - CBlockIndex* pblockindex = chainActive[nHeight]; - hash = pblockindex->GetBlockHash(); - int32_t i; - for (i=0; i<32; i++) - printf("%02x",((uint8_t *)&hash)[i]); - printf(" blockhash.%d\n",nHeight); - } else memset(&hash,0,sizeof(hash)); - return(hash); -}*/ - -UniValue getblockheader(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblockheader \"hash\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" - "If verbose is true, returns an Object with information about blockheader .\n" - "\nArguments:\n" - "1. \"hash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - ); - - LOCK(cs_main); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - - bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; - } - - return blockheaderToJSON(pblockindex); -} - -UniValue getblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblock \"hash|height\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash|height'.\n" - "If verbose is true, returns an Object with information about block .\n" - "\nArguments:\n" - "1. \"hash|height\" (string, required) The block hash or height\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided hash)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"size\" : n, (numeric) The block size\n" - " \"height\" : n, (numeric) The block height or index (same as provided height)\n" - " \"version\" : n, (numeric) The block version\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"tx\" : [ (array of string) The transaction ids\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - " ],\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleCli("getblock", "12800") - + HelpExampleRpc("getblock", "12800") - ); - - LOCK(cs_main); - - std::string strHash = params[0].get_str(); - - // If height is supplied, find the hash - if (strHash.size() < (2 * sizeof(uint256))) { - // std::stoi allows characters, whereas we want to be strict - regex r("[[:digit:]]+"); - if (!regex_match(strHash, r)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); - } - - int nHeight = -1; - try { - nHeight = std::stoi(strHash); - } - catch (const std::exception &e) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); - } - - if (nHeight < 0 || nHeight > chainActive.Height()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - } - strHash = chainActive[nHeight]->GetBlockHash().GetHex(); - } - - uint256 hash(uint256S(strHash)); - - bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - - if(!ReadBlockFromDisk(block, pblockindex,1)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << block; - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; - } - - return blockToJSON(block, pblockindex); -} - -UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gettxoutsetinfo\n" - "\nReturns statistics about the unspent transaction output set.\n" - "Note this call may take some time.\n" - "\nResult:\n" - "{\n" - " \"height\":n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) the best block hash hex\n" - " \"transactions\": n, (numeric) The number of transactions\n" - " \"txouts\": n, (numeric) The number of output transactions\n" - " \"bytes_serialized\": n, (numeric) The serialized size\n" - " \"hash_serialized\": \"hash\", (string) The serialized hash\n" - " \"total_amount\": x.xxx (numeric) The total amount\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("gettxoutsetinfo", "") - + HelpExampleRpc("gettxoutsetinfo", "") - ); - - UniValue ret(UniValue::VOBJ); - - CCoinsStats stats; - FlushStateToDisk(); - if (pcoinsTip->GetStats(stats)) { - ret.push_back(Pair("height", (int64_t)stats.nHeight)); - ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); - ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); - ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); - ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); - ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); - } - return ret; -} - -#include "komodo_defs.h" -#include "komodo_structs.h" - -#define IGUANA_MAXSCRIPTSIZE 10001 -#define KOMODO_KVDURATION 1440 -#define KOMODO_KVBINARY 2 -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -uint64_t komodo_paxprice(uint64_t *seedp,int32_t height,char *base,char *rel,uint64_t basevolume); -int32_t komodo_paxprices(int32_t *heights,uint64_t *prices,int32_t max,char *base,char *rel); -int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); -char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); -int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); -int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); - -UniValue kvsearch(const UniValue& params, bool fHelp) -{ - UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE*8],key[IGUANA_MAXSCRIPTSIZE*8]; int32_t duration,j,height,valuesize,keylen; uint256 refpubkey; static uint256 zeroes; - if (fHelp || params.size() != 1 ) - throw runtime_error( - "kvsearch key\n" - "\nSearch for a key stored via the kvupdate command. This feature is only available for asset chains.\n" - "\nArguments:\n" - "1. key (string, required) search the chain for this key\n" - "\nResult:\n" - "{\n" - " \"coin\": \"xxxxx\", (string) chain the key is stored on\n" - " \"currentheight\": xxxxx, (numeric) current height of the chain\n" - " \"key\": \"xxxxx\", (string) key\n" - " \"keylen\": xxxxx, (string) length of the key \n" - " \"owner\": \"xxxxx\" (string) hex string representing the owner of the key \n" - " \"height\": xxxxx, (numeric) height the key was stored at\n" - " \"expiration\": xxxxx, (numeric) height the key will expire\n" - " \"flags\": x (numeric) 1 if the key was created with a password; 0 otherwise.\n" - " \"value\": \"xxxxx\", (string) stored value\n" - " \"valuesize\": xxxxx (string) amount of characters stored\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("kvsearch", "examplekey") - + HelpExampleRpc("kvsearch", "\"examplekey\"") - ); - LOCK(cs_main); - if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 ) - { - ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); - ret.push_back(Pair("currentheight", (int64_t)chainActive.LastTip()->nHeight)); - ret.push_back(Pair("key",params[0].get_str())); - ret.push_back(Pair("keylen",keylen)); - if ( keylen < sizeof(key) ) - { - memcpy(key,params[0].get_str().c_str(),keylen); - if ( (valuesize= komodo_kvsearch(&refpubkey,chainActive.LastTip()->nHeight,&flags,&height,value,key,keylen)) >= 0 ) - { - std::string val; char *valuestr; - val.resize(valuesize); - valuestr = (char *)val.data(); - memcpy(valuestr,value,valuesize); - if ( memcmp(&zeroes,&refpubkey,sizeof(refpubkey)) != 0 ) - ret.push_back(Pair("owner",refpubkey.GetHex())); - ret.push_back(Pair("height",height)); - duration = ((flags >> 2) + 1) * KOMODO_KVDURATION; - ret.push_back(Pair("expiration", (int64_t)(height+duration))); - ret.push_back(Pair("flags",(int64_t)flags)); - ret.push_back(Pair("value",val)); - ret.push_back(Pair("valuesize",valuesize)); - } else ret.push_back(Pair("error",(char *)"cant find key")); - } else ret.push_back(Pair("error",(char *)"key too big")); - } else ret.push_back(Pair("error",(char *)"null key")); - return ret; -} - -UniValue minerids(const UniValue& params, bool fHelp) -{ - uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129]; - if ( fHelp || params.size() != 1 ) - throw runtime_error("minerids needs height\n"); - LOCK(cs_main); - int32_t height = atoi(params[0].get_str().c_str()); - if ( height <= 0 ) - height = chainActive.LastTip()->nHeight; - else - { - CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) - timestamp = pblockindex->GetBlockTime(); - } - if ( (n= komodo_minerids(minerids,height,(int32_t)(sizeof(minerids)/sizeof(*minerids)))) > 0 ) - { - memset(tally,0,sizeof(tally)); - numnotaries = komodo_notaries(pubkeys,height,timestamp); - if ( numnotaries > 0 ) - { - for (i=0; i= numnotaries ) - tally[128]++; - else tally[minerids[i]]++; - } - for (i=0; i<64; i++) - { - UniValue item(UniValue::VOBJ); std::string hex,kmdaddress; char *hexstr,kmdaddr[64],*ptr; int32_t m; - hex.resize(66); - hexstr = (char *)hex.data(); - for (j=0; j<33; j++) - sprintf(&hexstr[j*2],"%02x",pubkeys[i][j]); - item.push_back(Pair("notaryid", i)); - - bitcoin_address(kmdaddr,60,pubkeys[i],33); - m = (int32_t)strlen(kmdaddr); - kmdaddress.resize(m); - ptr = (char *)kmdaddress.data(); - memcpy(ptr,kmdaddr,m); - item.push_back(Pair("KMDaddress", kmdaddress)); - - item.push_back(Pair("pubkey", hex)); - item.push_back(Pair("blocks", tally[i])); - a.push_back(item); - } - UniValue item(UniValue::VOBJ); - item.push_back(Pair("pubkey", (char *)"external miners")); - item.push_back(Pair("blocks", tally[128])); - a.push_back(item); - } - ret.push_back(Pair("mined", a)); - ret.push_back(Pair("numnotaries", numnotaries)); - } else ret.push_back(Pair("error", (char *)"couldnt extract minerids")); - return ret; -} - -UniValue notaries(const UniValue& params, bool fHelp) -{ - UniValue a(UniValue::VARR); uint32_t timestamp=0; UniValue ret(UniValue::VOBJ); int32_t i,j,n,m; char *hexstr; uint8_t pubkeys[64][33]; char btcaddr[64],kmdaddr[64],*ptr; - if ( fHelp || (params.size() != 1 && params.size() != 2) ) - throw runtime_error("notaries height timestamp\n"); - LOCK(cs_main); - int32_t height = atoi(params[0].get_str().c_str()); - if ( params.size() == 2 ) - timestamp = (uint32_t)atol(params[1].get_str().c_str()); - else timestamp = (uint32_t)time(NULL); - if ( height < 0 ) - { - height = chainActive.LastTip()->nHeight; - timestamp = chainActive.LastTip()->GetBlockTime(); - } - else if ( params.size() < 2 ) - { - CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) - timestamp = pblockindex->GetBlockTime(); - } - if ( (n= komodo_notaries(pubkeys,height,timestamp)) > 0 ) - { - for (i=0; i 0 ) - ret.push_back(Pair("withdraws", opretbuf)); - else ret.push_back(Pair("withdraws", (char *)"")); - for (baseid=0; baseid<32; baseid++) - { - UniValue item(UniValue::VOBJ); UniValue obj(UniValue::VOBJ); - if ( pax_fiatstatus(&available,&deposited,&issued,&withdrawn,&approved,&redeemed,CURRENCIES[baseid]) == 0 ) - { - if ( deposited != 0 || issued != 0 || withdrawn != 0 || approved != 0 || redeemed != 0 ) - { - item.push_back(Pair("available", ValueFromAmount(available))); - item.push_back(Pair("deposited", ValueFromAmount(deposited))); - item.push_back(Pair("issued", ValueFromAmount(issued))); - item.push_back(Pair("withdrawn", ValueFromAmount(withdrawn))); - item.push_back(Pair("approved", ValueFromAmount(approved))); - item.push_back(Pair("redeemed", ValueFromAmount(redeemed))); - obj.push_back(Pair(CURRENCIES[baseid],item)); - a.push_back(obj); - } - } - } - ret.push_back(Pair("fiatstatus", a)); - return ret; -} - -UniValue paxprice(const UniValue& params, bool fHelp) -{ - if ( fHelp || params.size() > 4 || params.size() < 2 ) - throw runtime_error("paxprice \"base\" \"rel\" height\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t basevolume=0,relvolume,seed; - std::string base = params[0].get_str(); - std::string rel = params[1].get_str(); - int32_t height; - if ( params.size() == 2 ) - height = chainActive.LastTip()->nHeight; - else height = atoi(params[2].get_str().c_str()); - //if ( params.size() == 3 || (basevolume= COIN * atof(params[3].get_str().c_str())) == 0 ) - basevolume = 100000; - relvolume = komodo_paxprice(&seed,height,(char *)base.c_str(),(char *)rel.c_str(),basevolume); - ret.push_back(Pair("base", base)); - ret.push_back(Pair("rel", rel)); - ret.push_back(Pair("height", height)); - char seedstr[32]; - sprintf(seedstr,"%llu",(long long)seed); - ret.push_back(Pair("seed", seedstr)); - if ( height < 0 || height > chainActive.Height() ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - else - { - CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) - ret.push_back(Pair("timestamp", (int64_t)pblockindex->nTime)); - if ( basevolume != 0 && relvolume != 0 ) - { - ret.push_back(Pair("price",((double)relvolume / (double)basevolume))); - ret.push_back(Pair("invprice",((double)basevolume / (double)relvolume))); - ret.push_back(Pair("basevolume",ValueFromAmount(basevolume))); - ret.push_back(Pair("relvolume",ValueFromAmount(relvolume))); - } else ret.push_back(Pair("error", "overflow or error in one or more of parameters")); - } - return ret; -} - -UniValue paxprices(const UniValue& params, bool fHelp) -{ - if ( fHelp || params.size() != 3 ) - throw runtime_error("paxprices \"base\" \"rel\" maxsamples\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t relvolume,prices[4096]; uint32_t i,n; int32_t heights[sizeof(prices)/sizeof(*prices)]; - std::string base = params[0].get_str(); - std::string rel = params[1].get_str(); - int32_t maxsamples = atoi(params[2].get_str().c_str()); - if ( maxsamples < 1 ) - maxsamples = 1; - else if ( maxsamples > sizeof(heights)/sizeof(*heights) ) - maxsamples = sizeof(heights)/sizeof(*heights); - ret.push_back(Pair("base", base)); - ret.push_back(Pair("rel", rel)); - n = komodo_paxprices(heights,prices,maxsamples,(char *)base.c_str(),(char *)rel.c_str()); - UniValue a(UniValue::VARR); - for (i=0; i chainActive.Height() ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - else - { - CBlockIndex *pblockindex = chainActive[heights[i]]; - - item.push_back(Pair("t", (int64_t)pblockindex->nTime)); - item.push_back(Pair("p", (double)prices[i] / COIN)); - a.push_back(item); - } - } - ret.push_back(Pair("array", a)); - return ret; -} - -uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); - -UniValue gettxout(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - throw runtime_error( - "gettxout \"txid\" n ( includemempool )\n" - "\nReturns details about an unspent transaction output.\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. n (numeric, required) vout value\n" - "3. includemempool (boolean, optional) Whether to include the mempool\n" - "\nResult:\n" - "{\n" - " \"bestblock\" : \"hash\", (string) the block hash\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"code\", (string) \n" - " \"hex\" : \"hex\", (string) \n" - " \"reqSigs\" : n, (numeric) Number of required signatures\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" - " \"addresses\" : [ (array of string) array of Komodo addresses\n" - " \"komodoaddress\" (string) Komodo address\n" - " ,...\n" - " ]\n" - " },\n" - " \"version\" : n, (numeric) The version\n" - " \"coinbase\" : true|false (boolean) Coinbase or not\n" - "}\n" - - "\nExamples:\n" - "\nGet unspent transactions\n" - + HelpExampleCli("listunspent", "") + - "\nView the details\n" - + HelpExampleCli("gettxout", "\"txid\" 1") + - "\nAs a json rpc call\n" - + HelpExampleRpc("gettxout", "\"txid\", 1") - ); - - LOCK(cs_main); - - UniValue ret(UniValue::VOBJ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - int n = params[1].get_int(); - bool fMempool = true; - if (params.size() > 2) - fMempool = params[2].get_bool(); - - CCoins coins; - if (fMempool) { - LOCK(mempool.cs); - CCoinsViewMemPool view(pcoinsTip, mempool); - if (!view.GetCoins(hash, coins)) - return NullUniValue; - mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool - } else { - if (!pcoinsTip->GetCoins(hash, coins)) - return NullUniValue; - } - if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) - return NullUniValue; - - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - CBlockIndex *pindex = it->second; - ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); - if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) - ret.push_back(Pair("confirmations", 0)); - else - { - ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight,pindex->nHeight - coins.nHeight + 1))); - ret.push_back(Pair("rawconfirmations", pindex->nHeight - coins.nHeight + 1)); - } - ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); - uint64_t interest; int32_t txheight; uint32_t locktime; - if ( (interest= komodo_accrued_interest(&txheight,&locktime,hash,n,coins.nHeight,coins.vout[n].nValue,(int32_t)pindex->nHeight)) != 0 ) - ret.push_back(Pair("interest", ValueFromAmount(interest))); - UniValue o(UniValue::VOBJ); - ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); - ret.push_back(Pair("scriptPubKey", o)); - ret.push_back(Pair("version", coins.nVersion)); - ret.push_back(Pair("coinbase", coins.fCoinBase)); - - return ret; -} - -UniValue verifychain(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "verifychain ( checklevel numblocks )\n" - "\nVerifies blockchain database.\n" - "\nArguments:\n" - "1. checklevel (numeric, optional, 0-4, default=3) How thorough the block verification is.\n" - "2. numblocks (numeric, optional, default=288, 0=all) The number of blocks to check.\n" - "\nResult:\n" - "true|false (boolean) Verified or not\n" - "\nExamples:\n" - + HelpExampleCli("verifychain", "") - + HelpExampleRpc("verifychain", "") - ); - - LOCK(cs_main); - - int nCheckLevel = GetArg("-checklevel", 3); - int nCheckDepth = GetArg("-checkblocks", 288); - if (params.size() > 0) - nCheckLevel = params[0].get_int(); - if (params.size() > 1) - nCheckDepth = params[1].get_int(); - - return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth); -} - -/** Implementation of IsSuperMajority with better feedback */ -static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nRequired, const Consensus::Params& consensusParams) -{ - int nFound = 0; - CBlockIndex* pstart = pindex; - for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++) - { - if (pstart->nVersion >= minVersion) - ++nFound; - pstart = pstart->pprev; - } - - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("status", nFound >= nRequired)); - rv.push_back(Pair("found", nFound)); - rv.push_back(Pair("required", nRequired)); - rv.push_back(Pair("window", consensusParams.nMajorityWindow)); - return rv; -} - -static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams) -{ - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("version", version)); - rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))); - rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams))); - return rv; -} - -static UniValue NetworkUpgradeDesc(const Consensus::Params& consensusParams, Consensus::UpgradeIndex idx, int height) -{ - UniValue rv(UniValue::VOBJ); - auto upgrade = NetworkUpgradeInfo[idx]; - rv.push_back(Pair("name", upgrade.strName)); - rv.push_back(Pair("activationheight", consensusParams.vUpgrades[idx].nActivationHeight)); - switch (NetworkUpgradeState(height, consensusParams, idx)) { - case UPGRADE_DISABLED: rv.push_back(Pair("status", "disabled")); break; - case UPGRADE_PENDING: rv.push_back(Pair("status", "pending")); break; - case UPGRADE_ACTIVE: rv.push_back(Pair("status", "active")); break; - } - rv.push_back(Pair("info", upgrade.strInfo)); - return rv; -} - -void NetworkUpgradeDescPushBack( - UniValue& networkUpgrades, - const Consensus::Params& consensusParams, - Consensus::UpgradeIndex idx, - int height) -{ - // Network upgrades with an activation height of NO_ACTIVATION_HEIGHT are - // hidden. This is used when network upgrade implementations are merged - // without specifying the activation height. - if (consensusParams.vUpgrades[idx].nActivationHeight != Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) { - networkUpgrades.push_back(Pair( - HexInt(NetworkUpgradeInfo[idx].nBranchId), - NetworkUpgradeDesc(consensusParams, idx, height))); - } -} - - -UniValue getblockchaininfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockchaininfo\n" - "Returns an object containing various state info regarding block chain processing.\n" - "\nNote that when the chain tip is at the last block before a network upgrade activation,\n" - "consensus.chaintip != consensus.nextblock.\n" - "\nResult:\n" - "{\n" - " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" - " \"commitments\": xxxxxx, (numeric) the current number of note commitments in the commitment tree\n" - " \"softforks\": [ (array) status of softforks in progress\n" - " {\n" - " \"id\": \"xxxx\", (string) name of softfork\n" - " \"version\": xx, (numeric) block version\n" - " \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n" - " \"status\": xx, (boolean) true if threshold reached\n" - " \"found\": xx, (numeric) number of blocks with the new version found\n" - " \"required\": xx, (numeric) number of blocks required to trigger\n" - " \"window\": xx, (numeric) maximum size of examined window of recent blocks\n" - " },\n" - " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" - " }, ...\n" - " ],\n" - " \"upgrades\": { (object) status of network upgrades\n" - " \"xxxx\" : { (string) branch ID of the upgrade\n" - " \"name\": \"xxxx\", (string) name of upgrade\n" - " \"activationheight\": xxxxxx, (numeric) block height of activation\n" - " \"status\": \"xxxx\", (string) status of upgrade\n" - " \"info\": \"xxxx\", (string) additional information about upgrade\n" - " }, ...\n" - " },\n" - " \"consensus\": { (object) branch IDs of the current and upcoming consensus rules\n" - " \"chaintip\": \"xxxxxxxx\", (string) branch ID used to validate the current chain tip\n" - " \"nextblock\": \"xxxxxxxx\" (string) branch ID that the next block will be validated under\n" - " }\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getblockchaininfo", "") - + HelpExampleRpc("getblockchaininfo", "") - ); - - LOCK(cs_main); - double progress; - if ( ASSETCHAINS_SYMBOL[0] == 0 ) { - progress = Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.LastTip()); - } else { - int32_t longestchain = KOMODO_LONGESTCHAIN;//komodo_longestchain(); - progress = (longestchain > 0 ) ? (double) chainActive.Height() / longestchain : 1.0; - } - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); - obj.push_back(Pair("bestblockhash", chainActive.LastTip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); - obj.push_back(Pair("verificationprogress", progress)); - obj.push_back(Pair("chainwork", chainActive.LastTip()->nChainWork.GetHex())); - obj.push_back(Pair("pruned", fPruneMode)); - obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); - - ZCIncrementalMerkleTree tree; - pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), tree); - #ifdef __APPLE__ - obj.push_back(Pair("commitments", (uint64_t)tree.size())); - #else - obj.push_back(Pair("commitments", tree.size())); - #endif - - CBlockIndex* tip = chainActive.LastTip(); - UniValue valuePools(UniValue::VARR); - valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); - obj.push_back(Pair("valuePools", valuePools)); - - const Consensus::Params& consensusParams = Params().GetConsensus(); - UniValue softforks(UniValue::VARR); - softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - obj.push_back(Pair("softforks", softforks)); - - UniValue upgrades(UniValue::VOBJ); - for (int i = Consensus::UPGRADE_OVERWINTER; i < Consensus::MAX_NETWORK_UPGRADES; i++) { - NetworkUpgradeDescPushBack(upgrades, consensusParams, Consensus::UpgradeIndex(i), tip->nHeight); - } - obj.push_back(Pair("upgrades", upgrades)); - - UniValue consensus(UniValue::VOBJ); - consensus.push_back(Pair("chaintip", HexInt(CurrentEpochBranchId(tip->nHeight, consensusParams)))); - consensus.push_back(Pair("nextblock", HexInt(CurrentEpochBranchId(tip->nHeight + 1, consensusParams)))); - obj.push_back(Pair("consensus", consensus)); - - if (fPruneMode) - { - CBlockIndex *block = chainActive.LastTip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) - block = block->pprev; - - obj.push_back(Pair("pruneheight", block->nHeight)); - } - return obj; -} - -/** Comparison function for sorting the getchaintips heads. */ -struct CompareBlocksByHeight -{ - bool operator()(const CBlockIndex* a, const CBlockIndex* b) const - { - /* Make sure that unequal blocks with the same height do not compare - equal. Use the pointers themselves to make a distinction. */ - - if (a->nHeight != b->nHeight) - return (a->nHeight > b->nHeight); - - return a < b; - } -}; - -#include - -UniValue getchaintips(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getchaintips\n" - "Return information about all known tips in the block tree," - " including the main chain as well as orphaned branches.\n" - "\nResult:\n" - "[\n" - " {\n" - " \"height\": xxxx, (numeric) height of the chain tip\n" - " \"hash\": \"xxxx\", (string) block hash of the tip\n" - " \"branchlen\": 0 (numeric) zero for main chain\n" - " \"status\": \"active\" (string) \"active\" for the main chain\n" - " },\n" - " {\n" - " \"height\": xxxx,\n" - " \"hash\": \"xxxx\",\n" - " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" - " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" - " }\n" - "]\n" - "Possible values for status:\n" - "1. \"invalid\" This branch contains at least one invalid block\n" - "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" - "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" - "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" - "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" - "\nExamples:\n" - + HelpExampleCli("getchaintips", "") - + HelpExampleRpc("getchaintips", "") - ); - - LOCK(cs_main); - - /* Build up a list of chain tips. We start with the list of all - known blocks, and successively remove blocks that appear as pprev - of another block. */ - /*static pthread_mutex_t mutex; static int32_t didinit; - if ( didinit == 0 ) - { - pthread_mutex_init(&mutex,NULL); - didinit = 1; - } - pthread_mutex_lock(&mutex);*/ - std::set setTips; - int32_t n = 0; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - n++; - setTips.insert(item.second); - } - fprintf(stderr,"iterations getchaintips %d\n",n); - n = 0; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - const CBlockIndex* pprev=0; - n++; - if ( item.second != 0 ) - pprev = item.second->pprev; - if (pprev) - setTips.erase(pprev); - } - fprintf(stderr,"iterations getchaintips %d\n",n); - //pthread_mutex_unlock(&mutex); - - // Always report the currently active tip. - setTips.insert(chainActive.LastTip()); - - /* Construct the output array. */ - UniValue res(UniValue::VARR); const CBlockIndex *forked; - BOOST_FOREACH(const CBlockIndex* block, setTips) - BOOST_FOREACH(const CBlockIndex* block, setTips) - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->nHeight)); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); - forked = chainActive.FindFork(block); - if ( forked != 0 ) - { - const int branchLen = block->nHeight - forked->nHeight; - obj.push_back(Pair("branchlen", branchLen)); - - string status; - if (chainActive.Contains(block)) { - // This block is part of the currently active chain. - status = "active"; - } else if (block->nStatus & BLOCK_FAILED_MASK) { - // This block or one of its ancestors is invalid. - status = "invalid"; - } else if (block->nChainTx == 0) { - // This block cannot be connected because full block data for it or one of its parents is missing. - status = "headers-only"; - } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { - // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. - status = "valid-fork"; - } else if (block->IsValid(BLOCK_VALID_TREE)) { - // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. - status = "valid-headers"; - } else { - // No clue. - status = "unknown"; - } - obj.push_back(Pair("status", status)); - } - res.push_back(obj); - } - - return res; -} - -UniValue mempoolInfoToJSON() -{ - UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t) mempool.size())); - ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); - - return ret; -} - -UniValue getmempoolinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getmempoolinfo\n" - "\nReturns details on the active state of the TX memory pool.\n" - "\nResult:\n" - "{\n" - " \"size\": xxxxx (numeric) Current tx count\n" - " \"bytes\": xxxxx (numeric) Sum of all tx sizes\n" - " \"usage\": xxxxx (numeric) Total memory usage for the mempool\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempoolinfo", "") - + HelpExampleRpc("getmempoolinfo", "") - ); - - return mempoolInfoToJSON(); -} - -UniValue invalidateblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "invalidateblock \"hash\"\n" - "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" - "\nArguments:\n" - "1. hash (string, required) the hash of the block to mark as invalid\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("invalidateblock", "\"blockhash\"") - + HelpExampleRpc("invalidateblock", "\"blockhash\"") - ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - CValidationState state; - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - InvalidateBlock(state, pblockindex); - } - - if (state.IsValid()) { - ActivateBestChain(state); - } - - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } - - return NullUniValue; -} - -UniValue reconsiderblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "reconsiderblock \"hash\"\n" - "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" - "This can be used to undo the effects of invalidateblock.\n" - "\nArguments:\n" - "1. hash (string, required) the hash of the block to reconsider\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("reconsiderblock", "\"blockhash\"") - + HelpExampleRpc("reconsiderblock", "\"blockhash\"") - ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - CValidationState state; - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - ReconsiderBlock(state, pblockindex); - } - - if (state.IsValid()) { - ActivateBestChain(state); - } - - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } - - return NullUniValue; -} diff --git a/src/serialize.h b/src/serialize.h index 3d9c3fae6..9d23b469c 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2014 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index dc795ad7a..8067b42b9 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -39,22 +40,6 @@ public: hashBestSaplingAnchor_ = SaplingMerkleTree::empty_root(); } - bool GetSproutAnchorAt(const uint256& rt, SproutMerkleTree &tree) const { - if (rt == SproutMerkleTree::empty_root()) { - SproutMerkleTree new_tree; - tree = new_tree; - return true; - } - - std::map::const_iterator it = mapSproutAnchors_.find(rt); - if (it == mapSproutAnchors_.end()) { - return false; - } else { - tree = it->second; - return true; - } - } - bool GetSaplingAnchorAt(const uint256& rt, SaplingMerkleTree &tree) const { if (rt == SaplingMerkleTree::empty_root()) { SaplingMerkleTree new_tree; @@ -632,7 +617,7 @@ BOOST_AUTO_TEST_CASE(chained_joinsplits) CMutableTransaction mtx; mtx.vjoinsplit.push_back(js2); - BOOST_CHECK(!cache.HaveJoinSplitRequirements(mtx)); + BOOST_CHECK(!cache.HaveShieldedRequirements(mtx)); } { @@ -642,7 +627,7 @@ BOOST_AUTO_TEST_CASE(chained_joinsplits) mtx.vjoinsplit.push_back(js2); mtx.vjoinsplit.push_back(js1); - BOOST_CHECK(!cache.HaveJoinSplitRequirements(mtx)); + BOOST_CHECK(!cache.HaveShieldedRequirements(mtx)); } { @@ -650,7 +635,7 @@ BOOST_AUTO_TEST_CASE(chained_joinsplits) mtx.vjoinsplit.push_back(js1); mtx.vjoinsplit.push_back(js2); - BOOST_CHECK(cache.HaveJoinSplitRequirements(mtx)); + BOOST_CHECK(cache.HaveShieldedRequirements(mtx)); } { @@ -659,7 +644,7 @@ BOOST_AUTO_TEST_CASE(chained_joinsplits) mtx.vjoinsplit.push_back(js2); mtx.vjoinsplit.push_back(js3); - BOOST_CHECK(cache.HaveJoinSplitRequirements(mtx)); + BOOST_CHECK(cache.HaveShieldedRequirements(mtx)); } { @@ -669,7 +654,7 @@ BOOST_AUTO_TEST_CASE(chained_joinsplits) mtx.vjoinsplit.push_back(js2); mtx.vjoinsplit.push_back(js3); - BOOST_CHECK(cache.HaveJoinSplitRequirements(mtx)); + BOOST_CHECK(cache.HaveShieldedRequirements(mtx)); } } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 220d6a9b7..390b2bc7b 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2012-2013 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,6 +23,7 @@ using namespace std; using namespace libzcash; +//TODO: convert to Hush addresses static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; @@ -186,41 +188,6 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(detsigc == ParseHex("2052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d")); } -BOOST_AUTO_TEST_CASE(zc_address_test) -{ - for (size_t i = 0; i < 1000; i++) { - auto sk = SproutSpendingKey::random(); - { - string sk_string = EncodeSpendingKey(sk); - - BOOST_CHECK(sk_string[0] == 'S'); - BOOST_CHECK(sk_string[1] == 'K'); - - auto spendingkey2 = DecodeSpendingKey(sk_string); - BOOST_CHECK(IsValidSpendingKey(spendingkey2)); - BOOST_ASSERT(boost::get(&spendingkey2) != nullptr); - auto sk2 = boost::get(spendingkey2); - BOOST_CHECK(sk.inner() == sk2.inner()); - } - { - auto addr = sk.address(); - - std::string addr_string = EncodePaymentAddress(addr); - - BOOST_CHECK(addr_string[0] == 'z'); - BOOST_CHECK(addr_string[1] == 'c'); - - auto paymentaddr2 = DecodePaymentAddress(addr_string); - BOOST_ASSERT(IsValidPaymentAddress(paymentaddr2)); - - BOOST_ASSERT(boost::get(&paymentaddr2) != nullptr); - auto addr2 = boost::get(paymentaddr2); - BOOST_CHECK(addr.a_pk == addr2.a_pk); - BOOST_CHECK(addr.pk_enc == addr2.pk_enc); - } - } -} - BOOST_AUTO_TEST_CASE(zs_address_test) { SelectParams(CBaseChainParams::REGTEST); diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 1eaade55b..2abe2d782 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2013-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://www.opensource.org/licenses/mit-license.php #include "rpc/server.h" #include "rpc/client.h" @@ -341,38 +342,12 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_validateaddress) BOOST_CHECK_THROW(CallRPC("z_validateaddress"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_validateaddress toomany args"), runtime_error); - // Wallet should be empty - std::set addrs; - pwalletMain->GetSproutPaymentAddresses(addrs); - BOOST_CHECK(addrs.size()==0); - // This address is not valid, it belongs to another network BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress ztaaga95QAPyp1kSQ1hD2kguCpzyMHjxWZqaYDEkzbvo7uYQYAw2S8X4Kx98AvhhofMtQL8PAXKHuZsmhRcanavKRKmdCzk")); UniValue resultObj = retValue.get_obj(); bool b = find_value(resultObj, "isvalid").get_bool(); BOOST_CHECK_EQUAL(b, false); - // This address is valid, but the spending key is not in this wallet - BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zcfA19SDAKRYHLoRDoShcoz4nPohqWxuHcqg8WAxsiB2jFrrs6k7oSvst3UZvMYqpMNSRBkxBsnyjjngX5L55FxMzLKach8")); - resultObj = retValue.get_obj(); - b = find_value(resultObj, "isvalid").get_bool(); - BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sprout"); - b = find_value(resultObj, "ismine").get_bool(); - BOOST_CHECK_EQUAL(b, false); - - // Let's import a spending key to the wallet and validate its payment address - BOOST_CHECK_NO_THROW(CallRPC("z_importkey SKxoWv77WGwFnUJitQKNEcD636bL4X5Gd6wWmgaA4Q9x8jZBPJXT")); - BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL")); - resultObj = retValue.get_obj(); - b = find_value(resultObj, "isvalid").get_bool(); - BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sprout"); - b = find_value(resultObj, "ismine").get_bool(); - BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "payingkey").get_str(), "f5bb3c888ccc9831e3f6ba06e7528e26a312eec3acc1823be8918b6a3a5e20ad"); - BOOST_CHECK_EQUAL(find_value(resultObj, "transmissionkey").get_str(), "7a58c7132446564e6b810cf895c20537b3528357dc00150a8e201f491efa9c1a"); - // This Sapling address is not valid, it belongs to another network BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress ztestsapling1knww2nyjc62njkard0jmx7hlsj6twxmxwprn7anvrv4dc2zxanl3nemc0qx2hvplxmd2uau8gyw")); resultObj = retValue.get_obj(); @@ -398,16 +373,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) { LOCK2(cs_main, pwalletMain->cs_wallet); - // wallet should be empty - std::set addrs; - pwalletMain->GetSproutPaymentAddresses(addrs); - BOOST_CHECK(addrs.size()==0); - - // wallet should have one key - libzcash::SproutPaymentAddress addr = pwalletMain->GenerateNewSproutZKey(); - pwalletMain->GetSproutPaymentAddresses(addrs); - BOOST_CHECK(addrs.size()==1); - // Set up paths boost::filesystem::path tmppath = boost::filesystem::temp_directory_path(); boost::filesystem::path tmpfilename = boost::filesystem::unique_path("%%%%%%%%"); @@ -429,9 +394,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) BOOST_CHECK_NO_THROW(CallRPC(string("z_exportwallet ") + tmpfilename.string())); - libzcash::SproutSpendingKey key; - BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, key)); - std::string s1 = EncodePaymentAddress(addr); std::string s2 = EncodeSpendingKey(key); @@ -474,13 +436,13 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) BOOST_CHECK_THROW(CallRPC("z_importwallet toomany args"), runtime_error); // create a random key locally - auto testSpendingKey = libzcash::SproutSpendingKey::random(); + auto testSpendingKey = libzcash::SaplingSpendingKey::random(); auto testPaymentAddress = testSpendingKey.address(); std::string testAddr = EncodePaymentAddress(testPaymentAddress); std::string testKey = EncodeSpendingKey(testSpendingKey); // create test data using the random key - std::string format_str = "# Wallet dump created by Komodo v0.11.2.0.z8-9155cc6-dirty (2016-08-11 11:37:00 -0700)\n" + std::string format_str = "# Wallet dump created by Hush v0.11.2.0.z8-9155cc6-dirty (2016-08-11 11:37:00 -0700)\n" "# * Created on 2016-08-12T21:55:36Z\n" "# * Best block at time of backup was 0 (0de0a3851fef2d433b9b4f51d4342bdd24c5ddd793eb8fba57189f07e9235d52),\n" "# mined on 2009-01-03T18:15:05Z\n" @@ -503,28 +465,31 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) file << std::flush; // wallet should currently be empty - std::set addrs; - pwalletMain->GetSproutPaymentAddresses(addrs); + std::set addrs; + pwalletMain->GetSaplingPaymentAddresses(addrs); BOOST_CHECK(addrs.size()==0); // import test data from file into wallet BOOST_CHECK_NO_THROW(CallRPC(string("z_importwallet ") + path)); // wallet should now have one zkey - pwalletMain->GetSproutPaymentAddresses(addrs); + pwalletMain->GetSaplingPaymentAddresses(addrs); BOOST_CHECK(addrs.size()==1); // check that we have the spending key for the address + /* auto address = DecodePaymentAddress(testAddr); BOOST_CHECK(IsValidPaymentAddress(address)); BOOST_ASSERT(boost::get(&address) != nullptr); auto addr = boost::get(address); BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(addr)); + */ + // Verify the spending key is the same as the test data - libzcash::SproutSpendingKey k; - BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k)); - BOOST_CHECK_EQUAL(testKey, EncodeSpendingKey(k)); + //libzcash::SproutSpendingKey k; + //BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k)); + //BOOST_CHECK_EQUAL(testKey, EncodeSpendingKey(k)); } @@ -606,9 +571,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) // Verify number of addresses stored in wallet is n1+n2 int numAddrs = myaddrs.size(); BOOST_CHECK(numAddrs == (2 * n1) + n2); - pwalletMain->GetSproutPaymentAddresses(addrs); pwalletMain->GetSaplingPaymentAddresses(saplingAddrs); - BOOST_CHECK(addrs.size() + saplingAddrs.size() == numAddrs); + BOOST_CHECK(saplingAddrs.size() == numAddrs); // Ask wallet to list addresses BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); @@ -630,9 +594,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) std::string newaddress = retValue.get_str(); auto address = DecodePaymentAddress(newaddress); BOOST_CHECK(IsValidPaymentAddress(address)); - BOOST_ASSERT(boost::get(&address) != nullptr); - auto newAddr = boost::get(address); - BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(newAddr)); // Check if too many args BOOST_CHECK_THROW(CallRPC("z_getnewaddress toomanyargs"), runtime_error); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index b6a6ebc26..eb948f92d 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers // Copyright (c) 2011-2014 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/timedata.cpp b/src/timedata.cpp index 584e0db8c..64a3955ff 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/timedata.h b/src/timedata.h index 2296baf11..13cff12b3 100644 --- a/src/timedata.h +++ b/src/timedata.h @@ -1,4 +1,5 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 99c76995b..c2eb490da 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2015-2017 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/torcontrol.h b/src/torcontrol.h index 72dc82c5b..44611c7b6 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -1,4 +1,5 @@ // Copyright (c) 2015 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index 8b1acaca9..e1879e1d4 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -1,7 +1,5 @@ // Copyright (c) 2018 The Zcash developers // Copyright (c) 2019-2020 The Hush developers -// Released under the GPLv3 - // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -14,6 +12,7 @@ #include #include #include +#include "zcash/Note.hpp" SpendDescriptionInfo::SpendDescriptionInfo( libzcash::SaplingExpandedSpendingKey expsk, diff --git a/src/transaction_builder.h b/src/transaction_builder.h index 49c09294d..39225433a 100644 --- a/src/transaction_builder.h +++ b/src/transaction_builder.h @@ -1,4 +1,5 @@ // Copyright (c) 2018 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/txdb.cpp b/src/txdb.cpp index 46140f546..6fe8bd575 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -67,18 +67,6 @@ CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(Get } -bool CCoinsViewDB::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { - if (rt == SproutMerkleTree::empty_root()) { - SproutMerkleTree new_tree; - tree = new_tree; - return true; - } - - bool read = db.Read(make_pair(DB_SPROUT_ANCHOR, rt), tree); - - return read; -} - bool CCoinsViewDB::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { if (rt == SaplingMerkleTree::empty_root()) { SaplingMerkleTree new_tree; diff --git a/src/txdb.h b/src/txdb.h index e089d0190..81c0bb3ad 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -63,7 +63,7 @@ protected: public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; + //bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; bool GetNullifier(const uint256 &nf, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8a99a99e6..2f10856d4 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -513,7 +513,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag); extern char ASSETCHAINS_SYMBOL[]; -void CTxMemPool::removeExpired(unsigned int nBlockHeight) +std::vector CTxMemPool::removeExpired(unsigned int nBlockHeight) { CBlockIndex *tipindex; // Remove expired txs from the mempool @@ -523,16 +523,23 @@ void CTxMemPool::removeExpired(unsigned int nBlockHeight) { const CTransaction& tx = it->GetTx(); tipindex = chainActive.LastTip(); - if (IsExpiredTx(tx, nBlockHeight) || (ASSETCHAINS_SYMBOL[0] == 0 && tipindex != 0 && komodo_validate_interest(tx,tipindex->GetHeight()+1,tipindex->GetMedianTimePast() + 777,0)) < 0) + + bool fInterestNotValidated = ASSETCHAINS_SYMBOL[0] == 0 && tipindex != 0 && komodo_validate_interest(tx,tipindex->GetHeight()+1,tipindex->GetMedianTimePast() + 777,0) < 0; + if (IsExpiredTx(tx, nBlockHeight) || fInterestNotValidated) { + if (fInterestNotValidated && tipindex != 0) + LogPrintf("Removing interest violate txid.%s nHeight.%d nTime.%u vs locktime.%u\n",tx.GetHash().ToString(),tipindex->GetHeight()+1,tipindex->GetMedianTimePast() + 777,tx.nLockTime); transactionsToRemove.push_back(tx); } } + std::vector ids; for (const CTransaction& tx : transactionsToRemove) { list removed; remove(tx, removed, true); + ids.push_back(tx.GetHash()); LogPrint("mempool", "Removing expired txid: %s\n", tx.GetHash().ToString()); } + return ids; } /** @@ -637,8 +644,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const i++; } - boost::unordered_map intermediates; + /* + boost::unordered_map intermediates; BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { assert(!pcoins->GetNullifier(nf, SPROUT)); @@ -659,6 +667,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const intermediates.insert(std::make_pair(tree.root(), tree)); } + */ for (const SpendDescription &spendDescription : tx.vShieldedSpend) { SaplingMerkleTree tree; diff --git a/src/txmempool.h b/src/txmempool.h index f8aa0e9a7..59eeb0d98 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -218,7 +218,7 @@ public: void removeWithAnchor(const uint256 &invalidRoot, ShieldedType type); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx, std::list& removed); - void removeExpired(unsigned int nBlockHeight); + std::vector removeExpired(unsigned int nBlockHeight); void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight, std::list& conflicts, bool fCurrentEstimate = true); void removeWithoutBranchId(uint32_t nMemPoolBranchId); diff --git a/src/ui_interface.h b/src/ui_interface.h index ee0fd9113..59a549a9d 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -98,6 +98,9 @@ public: /** New block has been accepted */ boost::signals2::signal NotifyBlockTip; + + /** Transaction expired */ + boost::signals2::signal NotifyTxExpiration; }; extern CClientUIInterface uiInterface; diff --git a/src/util.h b/src/util.h index 2e8232871..d1bbdd26d 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index d4bba72ee..132ae82ab 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -20,7 +20,8 @@ static const string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO static const string SAFE_CHARS[] = { CHARS_ALPHA_NUM + " .,;_/:?@()", // SAFE_CHARS_DEFAULT - CHARS_ALPHA_NUM + " .,;_?@" // SAFE_CHARS_UA_COMMENT + CHARS_ALPHA_NUM + " .,;_?@", // SAFE_CHARS_UA_COMMENT + CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%" // SAFE_CHARS_URI }; string SanitizeString(const string& str, int rule) diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 37a07ea06..2d851093f 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -26,7 +26,8 @@ enum SafeChars { SAFE_CHARS_DEFAULT, //!< The full set of allowed chars - SAFE_CHARS_UA_COMMENT //!< BIP-0014 subset + SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset + SAFE_CHARS_URI //!< Chars allowed in URIs (RFC 3986) }; std::string SanitizeFilename(const std::string& str); diff --git a/src/utiltest.cpp b/src/utiltest.cpp deleted file mode 100644 index ead7fe6a0..000000000 --- a/src/utiltest.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2016 The Zcash developers -// Copyright (c) 2019-2020 The Hush developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "utiltest.h" - -#include "consensus/upgrades.h" - -#include - -CWalletTx GetValidReceive(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, CAmount value, - bool randomInputs, - int32_t version /* = 2 */) { - CMutableTransaction mtx; - mtx.nVersion = version; - mtx.vin.resize(2); - if (randomInputs) { - mtx.vin[0].prevout.hash = GetRandHash(); - mtx.vin[1].prevout.hash = GetRandHash(); - } else { - mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); - mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); - } - mtx.vin[0].prevout.n = 0; - mtx.vin[1].prevout.n = 0; - - // Generate an ephemeral keypair. - uint256 joinSplitPubKey; - unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); - mtx.joinSplitPubKey = joinSplitPubKey; - - std::array inputs = { - libzcash::JSInput(), // dummy input - libzcash::JSInput() // dummy input - }; - - std::array outputs = { - libzcash::JSOutput(sk.address(), value), - libzcash::JSOutput(sk.address(), value) - }; - - // Prepare JoinSplits - uint256 rt; - JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, - inputs, outputs, 2*value, 0, false}; - mtx.vjoinsplit.push_back(jsdesc); - - if (version >= 4) { - // Shielded Output - OutputDescription od; - mtx.vShieldedOutput.push_back(od); - } - - // Empty output script. - uint32_t consensusBranchId = SPROUT_BRANCH_ID; - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); - - // Add the signature - assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey - ) == 0); - - CTransaction tx {mtx}; - CWalletTx wtx {NULL, tx}; - return wtx; -} - -libzcash::SproutNote GetNote(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, - const CTransaction& tx, size_t js, size_t n) { - ZCNoteDecryption decryptor {sk.receiving_key()}; - auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey); - auto note_pt = libzcash::SproutNotePlaintext::decrypt( - decryptor, - tx.vjoinsplit[js].ciphertexts[n], - tx.vjoinsplit[js].ephemeralKey, - hSig, - (unsigned char) n); - return note_pt.note(sk.address()); -} - -CWalletTx GetValidSpend(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, - const libzcash::SproutNote& note, CAmount value) { - CMutableTransaction mtx; - mtx.vout.resize(2); - mtx.vout[0].nValue = value; - mtx.vout[1].nValue = 0; - - // Generate an ephemeral keypair. - uint256 joinSplitPubKey; - unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); - mtx.joinSplitPubKey = joinSplitPubKey; - - // Fake tree for the unused witness - SproutMerkleTree tree; - - libzcash::JSOutput dummyout; - libzcash::JSInput dummyin; - - { - if (note.value() > value) { - libzcash::SproutSpendingKey dummykey = libzcash::SproutSpendingKey::random(); - libzcash::SproutPaymentAddress dummyaddr = dummykey.address(); - dummyout = libzcash::JSOutput(dummyaddr, note.value() - value); - } else if (note.value() < value) { - libzcash::SproutSpendingKey dummykey = libzcash::SproutSpendingKey::random(); - libzcash::SproutPaymentAddress dummyaddr = dummykey.address(); - libzcash::SproutNote dummynote(dummyaddr.a_pk, (value - note.value()), uint256(), uint256()); - tree.append(dummynote.cm()); - dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey); - } - } - - tree.append(note.cm()); - - std::array inputs = { - libzcash::JSInput(tree.witness(), note, sk), - dummyin - }; - - std::array outputs = { - dummyout, // dummy output - libzcash::JSOutput() // dummy output - }; - - // Prepare JoinSplits - uint256 rt = tree.root(); - JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, - inputs, outputs, 0, value, false}; - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - uint32_t consensusBranchId = SPROUT_BRANCH_ID; - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); - - // Add the signature - assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey - ) == 0); - CTransaction tx {mtx}; - CWalletTx wtx {NULL, tx}; - return wtx; -} diff --git a/src/utiltest.h b/src/utiltest.h deleted file mode 100644 index 327dc7be4..000000000 --- a/src/utiltest.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2016 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "wallet/wallet.h" -#include "zcash/JoinSplit.hpp" -#include "zcash/Note.hpp" -#include "zcash/NoteEncryption.hpp" - -CWalletTx GetValidReceive(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, CAmount value, - bool randomInputs, - int32_t version = 2); -libzcash::SproutNote GetNote(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, - const CTransaction& tx, size_t js, size_t n); -CWalletTx GetValidSpend(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, - const libzcash::SproutNote& note, CAmount value); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 799a60ceb..44b5115d6 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -138,7 +139,8 @@ void ThreadNotifyWallets(CBlockIndex *pindexLastTip) while (pindex && pindex != pindexFork) { // Get the Sprout commitment tree as of the start of this block. SproutMerkleTree oldSproutTree; - assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, oldSproutTree)); + //TODO: how important is oldSproutTree ? + //assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, oldSproutTree)); // Get the Sapling commitment tree as of the start of this block. // We can get this from the `hashFinalSaplingRoot` of the last block diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 2e77195f3..79eaee5a9 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -46,7 +46,6 @@ #include #include -#include "paymentdisclosuredb.h" int32_t komodo_blockheight(uint256 hash); using namespace libzcash; @@ -75,19 +74,17 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( boost::optional builder, CMutableTransaction contextualTx, std::vector utxoInputs, - std::vector sproutNoteInputs, std::vector saplingNoteInputs, MergeToAddressRecipient recipient, CAmount fee, UniValue contextInfo) : -tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs), -saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo) +tx_(contextualTx), utxoInputs_(utxoInputs), saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo) { if (fee < 0 || fee > MAX_MONEY) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); } - if (utxoInputs.empty() && sproutNoteInputs.empty() && saplingNoteInputs.empty()) { + if (utxoInputs.empty() && saplingNoteInputs.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs"); } @@ -95,14 +92,6 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing"); } - if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); - } - - if (sproutNoteInputs.size() > 0 && builder) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Sprout notes are not supported by the TransactionBuilder"); - } - isUsingBuilder_ = false; if (builder) { isUsingBuilder_ = true; @@ -133,9 +122,6 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context // Lock UTXOs lock_utxos(); lock_notes(); - - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_mergetoaddress::~AsyncRPCOperation_mergetoaddress() @@ -210,31 +196,16 @@ void AsyncRPCOperation_mergetoaddress::main() unlock_utxos(); // clean up unlock_notes(); // clean up - - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } // Notes: -// 1. #1359 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. +// 1. #1359 Currently there is no limit set on the number of inputs+outputs, so size of tx could be invalid. // 2. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them. bool AsyncRPCOperation_mergetoaddress::main_impl() { assert(isToTaddr_ != isToZaddr_); - bool isPureTaddrOnlyTx = (sproutNoteInputs_.empty() && saplingNoteInputs_.empty() && isToTaddr_); + bool isPureTaddrOnlyTx = (saplingNoteInputs_.empty() && isToTaddr_); CAmount minersFee = fee_; size_t numInputs = utxoInputs_.size(); @@ -259,9 +230,6 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } CAmount z_inputs_total = 0; - for (const MergeToAddressInputSproutNote& t : sproutNoteInputs_) { - z_inputs_total += std::get<2>(t); - } for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) { z_inputs_total += std::get<2>(t); @@ -312,7 +280,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() /** * SCENARIO #0 * - * Sprout not involved, so we just use the TransactionBuilder and we're done. + * Only sapling involved, so we just use the TransactionBuilder and we're done. * * This is based on code from AsyncRPCOperation_sendmany::main_impl() and should be refactored. */ @@ -429,7 +397,6 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() * END SCENARIO #0 */ - /** * SCENARIO #1 * @@ -447,337 +414,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() * END SCENARIO #1 */ - - // Prepare raw transaction to handle JoinSplits - CMutableTransaction mtx(tx_); - crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); - mtx.joinSplitPubKey = joinSplitPubKey_; - tx_ = CTransaction(mtx); - std::string hexMemo = std::get<1>(recipient_); - - - /** - * SCENARIO #2 - * - * taddrs -> zaddr - * - * We only need a single JoinSplit. - */ - if (sproutNoteInputs_.empty() && isToZaddr_) { - // Create JoinSplit to target z-addr. - MergeToAddressJSInfo info; - info.vpub_old = sendAmount; - info.vpub_new = 0; - - JSOutput jso = JSOutput(boost::get(toPaymentAddress_), sendAmount); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); - } - info.vjsout.push_back(jso); - - UniValue obj(UniValue::VOBJ); - obj = perform_joinsplit(info); - sign_send_raw_transaction(obj); - return true; - } - /** - * END SCENARIO #2 - */ - - - // Copy zinputs to more flexible containers - std::deque zInputsDeque; - for (const auto& o : sproutNoteInputs_) { - zInputsDeque.push_back(o); - } - - // When spending notes, take a snapshot of note witnesses and anchors as the treestate will - // change upon arrival of new blocks which contain joinsplit transactions. This is likely - // to happen as creating a chained joinsplit transaction can take longer than the block interval. - { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto t : sproutNoteInputs_) { - JSOutPoint jso = std::get<0>(t); - std::vector vOutPoints = {jso}; - uint256 inputAnchor; - std::vector> vInputWitnesses; - pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); - jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor}; - } - } - - /** - * SCENARIO #3 - * - * zaddrs -> zaddr - * taddrs -> - * - * zaddrs -> - * taddrs -> taddr - * - * Send to zaddr by chaining JoinSplits together and immediately consuming any change - * Send to taddr by creating dummy z outputs and accumulating value in a change note - * which is used to set vpub_new in the last chained joinsplit. - */ - UniValue obj(UniValue::VOBJ); - CAmount jsChange = 0; // this is updated after each joinsplit - int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 - bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit - bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit - - // At this point, we are guaranteed to have at least one input note. - // Use address of first input note as the temporary change address. - SproutSpendingKey changeKey = std::get<3>(zInputsDeque.front()); - SproutPaymentAddress changeAddress = changeKey.address(); - - CAmount vpubOldTarget = 0; - CAmount vpubNewTarget = 0; - if (isToTaddr_) { - vpubNewTarget = z_inputs_total; - } else { - if (utxoInputs_.empty()) { - vpubNewTarget = minersFee; - } else { - vpubOldTarget = t_inputs_total - minersFee; - } - } - - // Keep track of treestate within this transaction - boost::unordered_map intermediates; - std::vector previousCommitments; - - while (!vpubNewProcessed) { - MergeToAddressJSInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - - // Set vpub_old in the first joinsplit - if (!vpubOldProcessed) { - if (t_inputs_total < vpubOldTarget) { - throw JSONRPCError(RPC_WALLET_ERROR, - strprintf("Insufficient transparent funds for vpub_old %s (miners fee %s, taddr inputs %s)", - FormatMoney(vpubOldTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total))); - } - info.vpub_old += vpubOldTarget; // funds flowing from public pool - vpubOldProcessed = true; - } - - CAmount jsInputValue = 0; - uint256 jsAnchor; - std::vector> witnesses; - - JSDescription prevJoinSplit; - - // Keep track of previous JoinSplit and its commitments - if (tx_.vjoinsplit.size() > 0) { - prevJoinSplit = tx_.vjoinsplit.back(); - } - - // If there is no change, the chain has terminated so we can reset the tracked treestate. - if (jsChange == 0 && tx_.vjoinsplit.size() > 0) { - intermediates.clear(); - previousCommitments.clear(); - } - - // - // Consume change as the first input of the JoinSplit. - // - if (jsChange > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Update tree state with previous joinsplit - SproutMerkleTree tree; - auto it = intermediates.find(prevJoinSplit.anchor); - if (it != intermediates.end()) { - tree = it->second; - } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); - } - - assert(changeOutputIndex != -1); - boost::optional changeWitness; - int n = 0; - for (const uint256& commitment : prevJoinSplit.commitments) { - tree.append(commitment); - previousCommitments.push_back(commitment); - if (!changeWitness && changeOutputIndex == n++) { - changeWitness = tree.witness(); - } else if (changeWitness) { - changeWitness.get().append(commitment); - } - } - if (changeWitness) { - witnesses.push_back(changeWitness); - } - jsAnchor = tree.root(); - intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) - - // Decrypt the change note's ciphertext to retrieve some data we need - ZCNoteDecryption decryptor(changeKey.receiving_key()); - auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); - try { - SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( - decryptor, - prevJoinSplit.ciphertexts[changeOutputIndex], - prevJoinSplit.ephemeralKey, - hSig, - (unsigned char)changeOutputIndex); - - SproutNote note = plaintext.note(changeAddress); - info.notes.push_back(note); - info.zkeys.push_back(changeKey); - - jsInputValue += plaintext.value(); - - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", - getId(), - FormatMoney(plaintext.value())); - - } catch (const std::exception& e) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); - } - } - - - // - // Consume spendable non-change notes - // - std::vector vInputNotes; - std::vector vInputZKeys; - std::vector vOutPoints; - std::vector> vInputWitnesses; - uint256 inputAnchor; - int numInputsNeeded = (jsChange > 0) ? 1 : 0; - while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { - MergeToAddressInputSproutNote t = zInputsDeque.front(); - JSOutPoint jso = std::get<0>(t); - SproutNote note = std::get<1>(t); - CAmount noteFunds = std::get<2>(t); - SproutSpendingKey zkey = std::get<3>(t); - zInputsDeque.pop_front(); - - MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()]; - vInputWitnesses.push_back(wad.witness); - if (inputAnchor.IsNull()) { - inputAnchor = wad.anchor; - } else if (inputAnchor != wad.anchor) { - throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); - } - - vOutPoints.push_back(jso); - vInputNotes.push_back(note); - vInputZKeys.push_back(zkey); - - jsInputValue += noteFunds; - - int wtxHeight = -1; - int wtxDepth = -1; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; - // Zero confirmation notes belong to transactions which have not yet been mined - if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); - } - wtxHeight = komodo_blockheight(wtx.hashBlock); - wtxDepth = wtx.GetDepthInMainChain(); - } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", - getId(), - jso.hash.ToString().substr(0, 10), - jso.js, - int(jso.n), // uint8_t - FormatMoney(noteFunds), - wtxHeight, - wtxDepth); - } - - // Add history of previous commitments to witness - if (vInputNotes.size() > 0) { - if (vInputWitnesses.size() == 0) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); - } - - for (auto& optionalWitness : vInputWitnesses) { - if (!optionalWitness) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); - } - SproutWitness w = *optionalWitness; // could use .get(); - if (jsChange > 0) { - for (const uint256& commitment : previousCommitments) { - w.append(commitment); - } - if (jsAnchor != w.root()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); - } - } - witnesses.push_back(w); - } - - // The jsAnchor is null if this JoinSplit is at the start of a new chain - if (jsAnchor.IsNull()) { - jsAnchor = inputAnchor; - } - - // Add spendable notes as inputs - std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); - std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys)); - } - - // Accumulate change - jsChange = jsInputValue + info.vpub_old; - - // Set vpub_new in the last joinsplit (when there are no more notes to spend) - if (zInputsDeque.empty()) { - assert(!vpubNewProcessed); - if (jsInputValue < vpubNewTarget) { - throw JSONRPCError(RPC_WALLET_ERROR, - strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr inputs %s)", - FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total))); - } - info.vpub_new += vpubNewTarget; // funds flowing back to public pool - vpubNewProcessed = true; - jsChange -= vpubNewTarget; - // If we are merging to a t-addr, there should be no change - if (isToTaddr_) assert(jsChange == 0); - } - - // create dummy output - info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new - - // create output for any change - if (jsChange > 0) { - std::string outputType = "change"; - auto jso = JSOutput(changeAddress, jsChange); - // If this is the final output, set the target and memo - if (isToZaddr_ && vpubNewProcessed) { - outputType = "target"; - jso.addr = boost::get(toPaymentAddress_); - if (!hexMemo.empty()) { - jso.memo = get_memo_from_hex_string(hexMemo); - } - } - info.vjsout.push_back(jso); - - LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n", - getId(), - outputType, - FormatMoney(jsChange)); - } - - obj = perform_joinsplit(info, witnesses, jsAnchor); - - if (jsChange > 0) { - changeOutputIndex = mta_find_output(obj, 1); - } - } - - // Sanity check in case changes to code block above exits loop by invoking 'break' - assert(zInputsDeque.size() == 0); - assert(vpubNewProcessed); - - sign_send_raw_transaction(obj); - return true; + return false; } @@ -849,190 +486,6 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj) tx_ = tx; } - -UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInfo& info) -{ - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - anchor = pcoinsTip->GetBestAnchor(SPROUT); // As there are no inputs, ask the wallet for the best anchor - } - return perform_joinsplit(info, witnesses, anchor); -} - - -UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInfo& info, std::vector& outPoints) -{ - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - pwalletMain->GetSproutNoteWitnesses(outPoints, witnesses, anchor); - } - return perform_joinsplit(info, witnesses, anchor); -} - -UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor) -{ - if (anchor.IsNull()) { - throw std::runtime_error("anchor is null"); - } - - if (witnesses.size() != info.notes.size()) { - throw runtime_error("number of notes and witnesses do not match"); - } - - if (info.notes.size() != info.zkeys.size()) { - throw runtime_error("number of notes and spending keys do not match"); - } - - for (size_t i = 0; i < witnesses.size(); i++) { - if (!witnesses[i]) { - throw runtime_error("joinsplit input could not be found in tree"); - } - info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], info.zkeys[i])); - } - - // Make sure there are two inputs and two outputs - while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { - info.vjsin.push_back(JSInput()); - } - - while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { - info.vjsout.push_back(JSOutput()); - } - - if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { - throw runtime_error("unsupported joinsplit input/output counts"); - } - - CMutableTransaction mtx(tx_); - - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", - getId(), - tx_.vjoinsplit.size(), - FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), - FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), - FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value)); - - // Generate the proof, this can take over a minute. - std::array inputs{info.vjsin[0], info.vjsin[1]}; - std::array outputs{info.vjsout[0], info.vjsout[1]}; - std::array inputMap; - std::array outputMap; - - uint256 esk; // payment disclosure - secret - - JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, - joinSplitPubKey_, - anchor, - inputs, - outputs, - inputMap, - outputMap, - info.vpub_old, - info.vpub_new, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address - { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); - } - } - - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); - - // Add the signature - if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey_) == 0)) { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - mtx.joinSplitPubKey.begin()) == 0)) { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - CTransaction rawTx(mtx); - tx_ = rawTx; - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - - std::string encryptedNote1; - std::string encryptedNote2; - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char)0x00); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote1 = HexStr(ss2.begin(), ss2.end()); - } - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char)0x01); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote2 = HexStr(ss2.begin(), ss2.end()); - } - - UniValue arrInputMap(UniValue::VARR); - UniValue arrOutputMap(UniValue::VARR); - for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { - arrInputMap.push_back(static_cast(inputMap[i])); - } - for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - arrOutputMap.push_back(static_cast(outputMap[i])); - } - - - // !!! Payment disclosure START - unsigned char buffer[32] = {0}; - memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer - std::vector vch(&buffer[0], &buffer[0] + 32); - uint256 joinSplitPrivKey = uint256(vch); - size_t js_index = tx_.vjoinsplit.size() - 1; - uint256 placeholder; - for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - uint8_t mapped_index = outputMap[i]; - // placeholder for txid will be filled in later when tx has been finalized and signed. - PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; - JSOutput output = outputs[mapped_index]; - libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output - PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; - paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); - } - // !!! Payment disclosure END - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("encryptednote1", encryptedNote1)); - obj.push_back(Pair("encryptednote2", encryptedNote2)); - obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); - obj.push_back(Pair("inputmap", arrInputMap)); - obj.push_back(Pair("outputmap", arrOutputMap)); - return obj; -} - std::array AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s) { std::array memo = {{0x00}}; @@ -1099,9 +552,6 @@ void AsyncRPCOperation_mergetoaddress::unlock_utxos() { */ void AsyncRPCOperation_mergetoaddress::lock_notes() { LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto note : sproutNoteInputs_) { - pwalletMain->LockNote(std::get<0>(note)); - } for (auto note : saplingNoteInputs_) { pwalletMain->LockNote(std::get<0>(note)); } @@ -1112,9 +562,6 @@ void AsyncRPCOperation_mergetoaddress::lock_notes() { */ void AsyncRPCOperation_mergetoaddress::unlock_notes() { LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto note : sproutNoteInputs_) { - pwalletMain->UnlockNote(std::get<0>(note)); - } for (auto note : saplingNoteInputs_) { pwalletMain->UnlockNote(std::get<0>(note)); } diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.h b/src/wallet/asyncrpcoperation_mergetoaddress.h index be49baff0..69150161e 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.h +++ b/src/wallet/asyncrpcoperation_mergetoaddress.h @@ -1,4 +1,6 @@ // Copyright (c) 2017 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers + // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,7 +24,6 @@ #include "amount.h" #include "asyncrpcoperation.h" -#include "paymentdisclosure.h" #include "primitives/transaction.h" #include "transaction_builder.h" #include "wallet.h" @@ -43,29 +44,11 @@ using namespace libzcash; // Input UTXO is a tuple of txid, vout, amount, script typedef std::tuple MergeToAddressInputUTXO; -// Input JSOP is a tuple of JSOutpoint, note, amount, spending key -typedef std::tuple MergeToAddressInputSproutNote; - typedef std::tuple MergeToAddressInputSaplingNote; // A recipient is a tuple of address, memo (optional if zaddr) typedef std::tuple MergeToAddressRecipient; -// Package of info which is passed to perform_joinsplit methods. -struct MergeToAddressJSInfo { - std::vector vjsin; - std::vector vjsout; - std::vector notes; - std::vector zkeys; - CAmount vpub_old = 0; - CAmount vpub_new = 0; -}; - -// A struct to help us track the witness and anchor for a given JSOutPoint -struct MergeToAddressWitnessAnchorData { - boost::optional witness; - uint256 anchor; -}; class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation { @@ -74,7 +57,6 @@ public: boost::optional builder, CMutableTransaction contextualTx, std::vector utxoInputs, - std::vector sproutNoteInputs, std::vector saplingNoteInputs, MergeToAddressRecipient recipient, CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE, @@ -93,8 +75,6 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs - bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. - private: friend class TEST_FRIEND_AsyncRPCOperation_mergetoaddress; // class for unit testing @@ -113,11 +93,7 @@ private: uint256 joinSplitPubKey_; unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES]; - // The key is the result string from calling JSOutPoint::ToString() - std::unordered_map jsopWitnessAnchorMap; - std::vector utxoInputs_; - std::vector sproutNoteInputs_; std::vector saplingNoteInputs_; TransactionBuilder builder_; @@ -126,18 +102,6 @@ private: std::array get_memo_from_hex_string(std::string s); bool main_impl(); - // JoinSplit without any input notes to spend - UniValue perform_joinsplit(MergeToAddressJSInfo&); - - // JoinSplit with input notes to spend (JSOutPoints)) - UniValue perform_joinsplit(MergeToAddressJSInfo&, std::vector&); - - // JoinSplit where you have the witnesses and anchor - UniValue perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor); - void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error void lock_utxos(); @@ -148,8 +112,6 @@ private: void unlock_notes(); - // payment disclosure! - std::vector paymentDisclosureData_; }; @@ -183,24 +145,6 @@ public: return delegate->main_impl(); } - UniValue perform_joinsplit(MergeToAddressJSInfo& info) - { - return delegate->perform_joinsplit(info); - } - - UniValue perform_joinsplit(MergeToAddressJSInfo& info, std::vector& v) - { - return delegate->perform_joinsplit(info, v); - } - - UniValue perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor) - { - return delegate->perform_joinsplit(info, witnesses, anchor); - } - void sign_send_raw_transaction(UniValue obj) { delegate->sign_send_raw_transaction(obj); diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 8f7078c81..0c6cae7c1 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Copyright (c) 2019 CryptoForge // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -87,24 +87,30 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { return status; } - std::vector sproutEntries; std::vector saplingEntries; std::set addresses; { LOCK2(cs_main, pwalletMain->cs_wallet); // We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying - // an anchor at height N-10 for each Sprout JoinSplit description + // an anchor at height N-10 for each SpendDescription // Consider, should notes be sorted? - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11); + pwalletMain->GetFilteredNotes(saplingEntries, "", 11); + + if(saplingEntries.size() == 0) { + LogPrint("zrpcunsafe", "%s: Nothing to consolidate, done.\n",opid); + return true; + } + if (fConsolidationMapUsed) { const vector& v = mapMultiArgs["-consolidatesaplingaddress"]; for(int i = 0; i < v.size(); i++) { auto zAddress = DecodePaymentAddress(v[i]); if (boost::get(&zAddress) != nullptr) { libzcash::SaplingPaymentAddress saplingAddress = boost::get(zAddress); - addresses.insert(saplingAddress ); + addresses.insert(saplingAddress); } else { - //TODO: how to handle invalid zaddrs? + LogPrint("zrpcunsafe", "%s: Invalid zaddr, exiting\n", opid); + return false; } } } else { diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.h b/src/wallet/asyncrpcoperation_saplingconsolidation.h index 9a80ff7c7..5d14b9335 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.h +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.h @@ -1,5 +1,5 @@ // Copyright (c) 2020 The Hush developers -// TODO: Forge should add his preferred copyright line here +// Copyright (c) 2019 CryptoForge // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index db7f7bd66..1b709b142 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2016 The Zcash developers // Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://www.opensource.org/licenses/mit-license.php /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * @@ -49,7 +49,6 @@ #include #include -#include "paymentdisclosuredb.h" #include using namespace libzcash; @@ -141,10 +140,6 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( } else { LogPrint("zrpc", "%s: z_sendmany initialized\n", getId()); } - - - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_sendmany::~AsyncRPCOperation_sendmany() { @@ -211,25 +206,10 @@ void AsyncRPCOperation_sendmany::main() { s += strprintf(", error=%s)\n", getErrorMessage()); } LogPrintf("%s",s); - - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } // Notes: -// 1. #1159 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. +// 1. #1159 Currently there is no limit set on the number of shielded spends, so size of tx could be invalid. // 2. #1360 Note selection is not optimal // 3. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them bool AsyncRPCOperation_sendmany::main_impl() { @@ -265,18 +245,12 @@ bool AsyncRPCOperation_sendmany::main_impl() { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no unspent notes found for zaddr from address."); } - // At least one of z_sprout_inputs_ and z_sapling_inputs_ must be empty by design - assert(z_sprout_inputs_.empty() || z_sapling_inputs_.empty()); - CAmount t_inputs_total = 0; for (SendManyInputUTXO & t : t_inputs_) { t_inputs_total += std::get<2>(t); } CAmount z_inputs_total = 0; - for (SendManyInputJSOP & t : z_sprout_inputs_) { - z_inputs_total += std::get<2>(t); - } for (auto t : z_sapling_inputs_) { z_inputs_total += t.note.value(); } @@ -566,430 +540,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { * END SCENARIO #0 */ - - // Grab the current consensus branch ID - { - LOCK(cs_main); - consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - } - - /** - * SCENARIO #1 - * - * taddr -> taddrs - * - * There are no zaddrs or joinsplits involved. - */ - if (isPureTaddrOnlyTx) { - add_taddr_outputs_to_tx(); - - CAmount funds = selectedUTXOAmount; - CAmount fundsSpent = t_outputs_total + minersFee; - CAmount change = funds - fundsSpent; - - if (change > 0) { - add_taddr_change_output_to_tx(0,change); - - LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", - getId(), - FormatMoney(change) - ); - } - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); - sign_send_raw_transaction(obj); - return true; - } - /** - * END SCENARIO #1 - */ - - - // Prepare raw transaction to handle JoinSplits - CMutableTransaction mtx(tx_); - crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); - mtx.joinSplitPubKey = joinSplitPubKey_; - //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) - if ( !hush_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) - mtx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 - else - mtx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); - - tx_ = CTransaction(mtx); - - // Copy zinputs and zoutputs to more flexible containers - std::deque zInputsDeque; // zInputsDeque stores minimum numbers of notes for target amount - CAmount tmp = 0; - for (auto o : z_sprout_inputs_) { - zInputsDeque.push_back(o); - tmp += std::get<2>(o); - if (tmp >= targetAmount) { - break; - } - } - std::deque zOutputsDeque; - for (auto o : z_outputs_) { - zOutputsDeque.push_back(o); - } - - // When spending notes, take a snapshot of note witnesses and anchors as the treestate will - // change upon arrival of new blocks which contain joinsplit transactions. This is likely - // to happen as creating a chained joinsplit transaction can take longer than the block interval. - if (z_sprout_inputs_.size() > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto t : z_sprout_inputs_) { - JSOutPoint jso = std::get<0>(t); - std::vector vOutPoints = { jso }; - uint256 inputAnchor; - std::vector> vInputWitnesses; - pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); - jsopWitnessAnchorMap[ jso.ToString() ] = WitnessAnchorData{ vInputWitnesses[0], inputAnchor }; - } - } - - - /** - * SCENARIO #2 - * - * taddr -> taddrs - * -> zaddrs - * - * Note: Consensus rule states that coinbase utxos can only be sent to a zaddr. TODO: Do they? - * Local wallet rule does not allow any change when sending coinbase utxos - * since there is currently no way to specify a change address and we don't - * want users accidentally sending excess funds to a recipient. - */ - if (isfromtaddr_) { - add_taddr_outputs_to_tx(); - - CAmount funds = selectedUTXOAmount; - CAmount fundsSpent = t_outputs_total + minersFee + z_outputs_total; - CAmount change = funds - fundsSpent; - - if (change > 0) { - if (selectedUTXOCoinbase) { - assert(isSingleZaddrOutput); - throw JSONRPCError(RPC_WALLET_ERROR, strprintf( - "Change %s not allowed. When shielding coinbase funds, the wallet does not " - "allow any change as there is currently no way to specify a change address " - "in z_sendmany.", FormatMoney(change))); - } else { - CBitcoinAddress ba = CBitcoinAddress(fromtaddr_); - add_taddr_change_output_to_tx(&ba,change); - LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", - getId(), - FormatMoney(change) - ); - } - } - - // Create joinsplits, where each output represents a zaddr recipient. - UniValue obj(UniValue::VOBJ); - while (zOutputsDeque.size() > 0) { - AsyncJoinSplitInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - int n = 0; - while (n++ 0) { - SendManyRecipient smr = zOutputsDeque.front(); - std::string address = std::get<0>(smr); - CAmount value = std::get<1>(smr); - std::string hexMemo = std::get<2>(smr); - zOutputsDeque.pop_front(); - - PaymentAddress pa = DecodePaymentAddress(address); - JSOutput jso = JSOutput(boost::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); - } - info.vjsout.push_back(jso); - - // Funds are removed from the value pool and enter the private pool - info.vpub_old += value; - } - obj = perform_joinsplit(info); - } - sign_send_raw_transaction(obj); - return true; - } - /** - * END SCENARIO #2 - */ - - - - /** - * SCENARIO #3 - * - * zaddr -> taddrs - * -> zaddrs - * - * Send to zaddrs by chaining JoinSplits together and immediately consuming any change - * Send to taddrs by creating dummy z outputs and accumulating value in a change note - * which is used to set vpub_new in the last chained joinsplit. - */ - UniValue obj(UniValue::VOBJ); - CAmount jsChange = 0; // this is updated after each joinsplit - int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 - bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit - CAmount vpubNewTarget = minersFee; - if (t_outputs_total > 0) { - add_taddr_outputs_to_tx(); - vpubNewTarget += t_outputs_total; - } - - // Keep track of treestate within this transaction - boost::unordered_map intermediates; - std::vector previousCommitments; - - while (!vpubNewProcessed) { - AsyncJoinSplitInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - - CAmount jsInputValue = 0; - uint256 jsAnchor; - std::vector> witnesses; - - JSDescription prevJoinSplit; - - // Keep track of previous JoinSplit and its commitments - if (tx_.vjoinsplit.size() > 0) { - prevJoinSplit = tx_.vjoinsplit.back(); - } - - // If there is no change, the chain has terminated so we can reset the tracked treestate. - if (jsChange==0 && tx_.vjoinsplit.size() > 0) { - intermediates.clear(); - previousCommitments.clear(); - } - - // - // Consume change as the first input of the JoinSplit. - // - if (jsChange > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Update tree state with previous joinsplit - SproutMerkleTree tree; - auto it = intermediates.find(prevJoinSplit.anchor); - if (it != intermediates.end()) { - tree = it->second; - } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); - } - - assert(changeOutputIndex != -1); - boost::optional changeWitness; - int n = 0; - for (const uint256& commitment : prevJoinSplit.commitments) { - tree.append(commitment); - previousCommitments.push_back(commitment); - if (!changeWitness && changeOutputIndex == n++) { - changeWitness = tree.witness(); - } else if (changeWitness) { - changeWitness.get().append(commitment); - } - } - if (changeWitness) { - witnesses.push_back(changeWitness); - } - jsAnchor = tree.root(); - intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) - - // Decrypt the change note's ciphertext to retrieve some data we need - ZCNoteDecryption decryptor(boost::get(spendingkey_).receiving_key()); - auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); - try { - SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( - decryptor, - prevJoinSplit.ciphertexts[changeOutputIndex], - prevJoinSplit.ephemeralKey, - hSig, - (unsigned char) changeOutputIndex); - - SproutNote note = plaintext.note(boost::get(frompaymentaddress_)); - info.notes.push_back(note); - - jsInputValue += plaintext.value(); - - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", - getId(), - FormatMoney(plaintext.value()) - ); - - } catch (const std::exception& e) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); - } - } - - - // - // Consume spendable non-change notes - // - std::vector vInputNotes; - std::vector vOutPoints; - std::vector> vInputWitnesses; - uint256 inputAnchor; - int numInputsNeeded = (jsChange>0) ? 1 : 0; - while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { - SendManyInputJSOP t = zInputsDeque.front(); - JSOutPoint jso = std::get<0>(t); - SproutNote note = std::get<1>(t); - CAmount noteFunds = std::get<2>(t); - zInputsDeque.pop_front(); - - WitnessAnchorData wad = jsopWitnessAnchorMap[ jso.ToString() ]; - vInputWitnesses.push_back(wad.witness); - if (inputAnchor.IsNull()) { - inputAnchor = wad.anchor; - } else if (inputAnchor != wad.anchor) { - throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); - } - - vOutPoints.push_back(jso); - vInputNotes.push_back(note); - - jsInputValue += noteFunds; - - int wtxHeight = -1; - int wtxDepth = -1; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; - // Zero-confirmation notes belong to transactions which have not yet been mined - if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); - } - wtxHeight = komodo_blockheight(wtx.hashBlock); - wtxDepth = wtx.GetDepthInMainChain(); - } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", - getId(), - jso.hash.ToString().substr(0, 10), - jso.js, - int(jso.n), // uint8_t - FormatMoney(noteFunds), - wtxHeight, - wtxDepth - ); - } - - // Add history of previous commitments to witness - if (vInputNotes.size() > 0) { - - if (vInputWitnesses.size()==0) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); - } - - for (auto & optionalWitness : vInputWitnesses) { - if (!optionalWitness) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); - } - SproutWitness w = *optionalWitness; // could use .get(); - if (jsChange > 0) { - for (const uint256& commitment : previousCommitments) { - w.append(commitment); - } - if (jsAnchor != w.root()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); - } - } - witnesses.push_back(w); - } - - // The jsAnchor is null if this JoinSplit is at the start of a new chain - if (jsAnchor.IsNull()) { - jsAnchor = inputAnchor; - } - - // Add spendable notes as inputs - std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); - } - - // Find recipient to transfer funds to - std::string address, hexMemo; - CAmount value = 0; - if (zOutputsDeque.size() > 0) { - SendManyRecipient smr = zOutputsDeque.front(); - address = std::get<0>(smr); - value = std::get<1>(smr); - hexMemo = std::get<2>(smr); - zOutputsDeque.pop_front(); - } - - // Reset change - jsChange = 0; - CAmount outAmount = value; - - // Set vpub_new in the last joinsplit (when there are no more notes to spend or zaddr outputs to satisfy) - if (zOutputsDeque.size() == 0 && zInputsDeque.size() == 0) { - assert(!vpubNewProcessed); - if (jsInputValue < vpubNewTarget) { - throw JSONRPCError(RPC_WALLET_ERROR, - strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr outputs %s)", - FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_outputs_total))); - } - outAmount += vpubNewTarget; - info.vpub_new += vpubNewTarget; // funds flowing back to public pool - vpubNewProcessed = true; - jsChange = jsInputValue - outAmount; - assert(jsChange >= 0); - } - else { - // This is not the last joinsplit, so compute change and any amount still due to the recipient - if (jsInputValue > outAmount) { - jsChange = jsInputValue - outAmount; - } else if (outAmount > jsInputValue) { - // Any amount due is owed to the recipient. Let the miners fee get paid first. - CAmount due = outAmount - jsInputValue; - SendManyRecipient r = SendManyRecipient(address, due, hexMemo); - zOutputsDeque.push_front(r); - - // reduce the amount being sent right now to the value of all inputs - value = jsInputValue; - } - } - - // create output for recipient - if (address.empty()) { - assert(value==0); - info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new - } else { - PaymentAddress pa = DecodePaymentAddress(address); - // If we are here, we know we have no Sapling outputs. - JSOutput jso = JSOutput(boost::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); - } - info.vjsout.push_back(jso); - } - - // create output for any change - if (jsChange>0) { - info.vjsout.push_back(JSOutput(boost::get(frompaymentaddress_), jsChange)); - - LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", - getId(), - FormatMoney(jsChange) - ); - } - - obj = perform_joinsplit(info, witnesses, jsAnchor); - - if (jsChange > 0) { - changeOutputIndex = find_output(obj, 1); - } - } - - // Sanity check in case changes to code block above exits loop by invoking 'break' - assert(zInputsDeque.size() == 0); - assert(zOutputsDeque.size() == 0); - assert(vpubNewProcessed); - - sign_send_raw_transaction(obj); - return true; + return false; } @@ -1127,33 +678,10 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { bool AsyncRPCOperation_sendmany::find_unspent_notes() { - std::vector sproutEntries; std::vector saplingEntries; { LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress_, mindepth_); - } - - // If using the TransactionBuilder, we only want Sapling notes. - // If not using it, we only want Sprout notes. - // TODO: Refactor `GetFilteredNotes()` so we only fetch what we need. - if (isUsingBuilder_) { - sproutEntries.clear(); - } else { - saplingEntries.clear(); - } - - for (CSproutNotePlaintextEntry & entry : sproutEntries) { - z_sprout_inputs_.push_back(SendManyInputJSOP(entry.jsop, entry.plaintext.note(boost::get(frompaymentaddress_)), CAmount(entry.plaintext.value()))); - std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end()); - LogPrint("zrpcunsafe", "%s: found unspent Sprout note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\n", - getId(), - entry.jsop.hash.ToString().substr(0, 10), - entry.jsop.js, - int(entry.jsop.n), // uint8_t - FormatMoney(entry.plaintext.value()), - HexStr(data).substr(0, 10) - ); + pwalletMain->GetFilteredNotes(saplingEntries, fromaddress_, mindepth_); } for (auto entry : saplingEntries) { @@ -1167,15 +695,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { HexStr(data).substr(0, 10)); } - if (z_sprout_inputs_.empty() && z_sapling_inputs_.empty()) { - return false; - } - // sort in descending order, so big notes appear first - std::sort(z_sprout_inputs_.begin(), z_sprout_inputs_.end(), - [](SendManyInputJSOP i, SendManyInputJSOP j) -> bool { - return std::get<2>(i) > std::get<2>(j); - }); std::sort(z_sapling_inputs_.begin(), z_sapling_inputs_.end(), [](SaplingNoteEntry i, SaplingNoteEntry j) -> bool { return i.note.value() > j.note.value(); @@ -1184,189 +704,6 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { return true; } -UniValue AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info) { - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - anchor = pcoinsTip->GetBestAnchor(SPROUT); // As there are no inputs, ask the wallet for the best anchor - } - return perform_joinsplit(info, witnesses, anchor); -} - - -UniValue AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info, std::vector & outPoints) { - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - pwalletMain->GetSproutNoteWitnesses(outPoints, witnesses, anchor); - } - return perform_joinsplit(info, witnesses, anchor); -} - -UniValue AsyncRPCOperation_sendmany::perform_joinsplit( - AsyncJoinSplitInfo & info, - std::vector> witnesses, - uint256 anchor) -{ - if (anchor.IsNull()) { - throw std::runtime_error("anchor is null"); - } - - if (!(witnesses.size() == info.notes.size())) { - throw runtime_error("number of notes and witnesses do not match"); - } - - for (size_t i = 0; i < witnesses.size(); i++) { - if (!witnesses[i]) { - throw runtime_error("joinsplit input could not be found in tree"); - } - info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], boost::get(spendingkey_))); - } - - // Make sure there are two inputs and two outputs - while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { - info.vjsin.push_back(JSInput()); - } - - while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { - info.vjsout.push_back(JSOutput()); - } - - if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { - throw runtime_error("unsupported joinsplit input/output counts"); - } - - CMutableTransaction mtx(tx_); - - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", - getId(), - tx_.vjoinsplit.size(), - FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), - FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), - FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value) - ); - - // Generate the proof, this can take over a minute. - std::array inputs - {info.vjsin[0], info.vjsin[1]}; - std::array outputs - {info.vjsout[0], info.vjsout[1]}; - std::array inputMap; - std::array outputMap; - uint256 esk; // payment disclosure - secret - - JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, - joinSplitPubKey_, - anchor, - inputs, - outputs, - inputMap, - outputMap, - info.vpub_old, - info.vpub_new, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address - { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); - } - } - - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); - - // Add the signature - if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey_ - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - mtx.joinSplitPubKey.begin() - ) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - CTransaction rawTx(mtx); - tx_ = rawTx; - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - - std::string encryptedNote1; - std::string encryptedNote2; - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x00); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote1 = HexStr(ss2.begin(), ss2.end()); - } - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x01); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote2 = HexStr(ss2.begin(), ss2.end()); - } - - UniValue arrInputMap(UniValue::VARR); - UniValue arrOutputMap(UniValue::VARR); - for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { - arrInputMap.push_back(static_cast(inputMap[i])); - } - for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - arrOutputMap.push_back(static_cast(outputMap[i])); - } - - - // !!! Payment disclosure START - unsigned char buffer[32] = {0}; - memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer - std::vector vch(&buffer[0], &buffer[0] + 32); - uint256 joinSplitPrivKey = uint256(vch); - size_t js_index = tx_.vjoinsplit.size() - 1; - uint256 placeholder; - for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - uint8_t mapped_index = outputMap[i]; - // placeholder for txid will be filled in later when tx has been finalized and signed. - PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; - JSOutput output = outputs[mapped_index]; - libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output - PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; - paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); - } - // !!! Payment disclosure END - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("encryptednote1", encryptedNote1)); - obj.push_back(Pair("encryptednote2", encryptedNote2)); - obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); - obj.push_back(Pair("inputmap", arrInputMap)); - obj.push_back(Pair("outputmap", arrOutputMap)); - return obj; -} - void AsyncRPCOperation_sendmany::add_taddr_outputs_to_tx() { CMutableTransaction rawTx(tx_); diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index 8e39f341a..86e6ef775 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -1,4 +1,5 @@ // Copyright (c) 2016 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -27,7 +28,6 @@ #include "zcash/JoinSplit.hpp" #include "zcash/Address.hpp" #include "wallet.h" -#include "paymentdisclosure.h" #include #include @@ -46,19 +46,6 @@ typedef std::tuple SendManyRecipient; // Input UTXO is a tuple (quadruple) of txid, vout, amount, coinbase) typedef std::tuple SendManyInputUTXO; -// Input JSOP is a tuple of JSOutpoint, note and amount -typedef std::tuple SendManyInputJSOP; - -// Package of info which is passed to perform_joinsplit methods. -struct AsyncJoinSplitInfo -{ - std::vector vjsin; - std::vector vjsout; - std::vector notes; - CAmount vpub_old = 0; - CAmount vpub_new = 0; -}; - // A struct to help us track the witness and anchor for a given JSOutPoint struct WitnessAnchorData { boost::optional witness; @@ -90,8 +77,6 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs - bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. - private: friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing @@ -117,7 +102,7 @@ private: std::vector t_outputs_; std::vector z_outputs_; std::vector t_inputs_; - std::vector z_sprout_inputs_; + //std::vector z_sprout_inputs_; std::vector z_sapling_inputs_; TransactionBuilder builder_; @@ -130,22 +115,7 @@ private: std::array get_memo_from_hex_string(std::string s); bool main_impl(); - // JoinSplit without any input notes to spend - UniValue perform_joinsplit(AsyncJoinSplitInfo &); - - // JoinSplit with input notes to spend (JSOutPoints)) - UniValue perform_joinsplit(AsyncJoinSplitInfo &, std::vector & ); - - // JoinSplit where you have the witnesses and anchor - UniValue perform_joinsplit( - AsyncJoinSplitInfo & info, - std::vector> witnesses, - uint256 anchor); - void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error - - // payment disclosure! - std::vector paymentDisclosureData_; }; @@ -190,22 +160,6 @@ public: return delegate->main_impl(); } - UniValue perform_joinsplit(AsyncJoinSplitInfo &info) { - return delegate->perform_joinsplit(info); - } - - UniValue perform_joinsplit(AsyncJoinSplitInfo &info, std::vector &v ) { - return delegate->perform_joinsplit(info, v); - } - - UniValue perform_joinsplit( - AsyncJoinSplitInfo & info, - std::vector> witnesses, - uint256 anchor) - { - return delegate->perform_joinsplit(info, witnesses, anchor); - } - void sign_send_raw_transaction(UniValue obj) { delegate->sign_send_raw_transaction(obj); } diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 55feb2cbf..de56ba3bc 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2017 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -47,9 +48,6 @@ #include "asyncrpcoperation_shieldcoinbase.h" -#include "paymentdisclosure.h" -#include "paymentdisclosuredb.h" - using namespace libzcash; extern uint64_t ASSETCHAINS_TIMELOCKGTE; @@ -107,8 +105,6 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( // Lock UTXOs lock_utxos(); - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() { @@ -180,20 +176,6 @@ void AsyncRPCOperation_shieldcoinbase::main() { unlock_utxos(); // clean up - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } bool AsyncRPCOperation_shieldcoinbase::main_impl() { @@ -234,37 +216,6 @@ bool AsyncRPCOperation_shieldcoinbase::main_impl() { return boost::apply_visitor(ShieldToAddress(this, sendAmount), tozaddr_); } -bool ShieldToAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { - // update the transaction with these inputs - CMutableTransaction rawTx(m_op->tx_); - for (ShieldCoinbaseUTXO & t : m_op->inputs_) { - CTxIn in(COutPoint(t.txid, t.vout)); - if (t.amount >= ASSETCHAINS_TIMELOCKGTE) - in.nSequence = 0xfffffffe; - rawTx.vin.push_back(in); - } - m_op->tx_ = CTransaction(rawTx); - - // Prepare raw transaction to handle JoinSplits - CMutableTransaction mtx(m_op->tx_); - crypto_sign_keypair(m_op->joinSplitPubKey_.begin(), m_op->joinSplitPrivKey_); - mtx.joinSplitPubKey = m_op->joinSplitPubKey_; - m_op->tx_ = CTransaction(mtx); - - // Create joinsplit - UniValue obj(UniValue::VOBJ); - ShieldCoinbaseJSInfo info; - info.vpub_old = sendAmount; - info.vpub_new = 0; - JSOutput jso = JSOutput(zaddr, sendAmount); - info.vjsout.push_back(jso); - obj = m_op->perform_joinsplit(info); - - m_op->sign_send_raw_transaction(obj); - return true; -} - - extern UniValue signrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue sendrawtransaction(const UniValue& params, bool fHelp, const CPubKey& mypk); @@ -406,163 +357,6 @@ void AsyncRPCOperation_shieldcoinbase::sign_send_raw_transaction(UniValue obj) } -UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) { - uint32_t consensusBranchId; - uint256 anchor; - { - LOCK(cs_main); - consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - anchor = pcoinsTip->GetBestAnchor(SPROUT); - } - - - if (anchor.IsNull()) { - throw std::runtime_error("anchor is null"); - } - - // Make sure there are two inputs and two outputs - while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { - info.vjsin.push_back(JSInput()); - } - - while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { - info.vjsout.push_back(JSOutput()); - } - - if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { - throw runtime_error("unsupported joinsplit input/output counts"); - } - - CMutableTransaction mtx(tx_); - - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", - getId(), - tx_.vjoinsplit.size(), - FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), - FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), - FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value) - ); - - // Generate the proof, this can take over a minute. - std::array inputs - {info.vjsin[0], info.vjsin[1]}; - std::array outputs - {info.vjsout[0], info.vjsout[1]}; - - std::array inputMap; - std::array outputMap; - - uint256 esk; // payment disclosure - secret - - JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, - joinSplitPubKey_, - anchor, - inputs, - outputs, - inputMap, - outputMap, - info.vpub_old, - info.vpub_new, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address - { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); - } - } - - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); - - // Add the signature - if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey_ - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - mtx.joinSplitPubKey.begin() - ) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - CTransaction rawTx(mtx); - tx_ = rawTx; - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - - std::string encryptedNote1; - std::string encryptedNote2; - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x00); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote1 = HexStr(ss2.begin(), ss2.end()); - } - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x01); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote2 = HexStr(ss2.begin(), ss2.end()); - } - - UniValue arrInputMap(UniValue::VARR); - UniValue arrOutputMap(UniValue::VARR); - for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { - arrInputMap.push_back(static_cast(inputMap[i])); - } - for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - arrOutputMap.push_back(static_cast(outputMap[i])); - } - - // !!! Payment disclosure START - unsigned char buffer[32] = {0}; - memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer - std::vector vch(&buffer[0], &buffer[0] + 32); - uint256 joinSplitPrivKey = uint256(vch); - size_t js_index = tx_.vjoinsplit.size() - 1; - uint256 placeholder; - for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - uint8_t mapped_index = outputMap[i]; - // placeholder for txid will be filled in later when tx has been finalized and signed. - PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; - JSOutput output = outputs[mapped_index]; - libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output - PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; - paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); - } - // !!! Payment disclosure END - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("encryptednote1", encryptedNote1)); - obj.push_back(Pair("encryptednote2", encryptedNote2)); - obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); - obj.push_back(Pair("inputmap", arrInputMap)); - obj.push_back(Pair("outputmap", arrOutputMap)); - return obj; -} - /** * Override getStatus() to append the operation's context object to the default status object. */ diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index b3fef0fc1..3eb1a6cfc 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -1,4 +1,5 @@ // Copyright (c) 2017 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -33,8 +34,6 @@ #include -#include "paymentdisclosure.h" - // Default transaction fee if caller does not specify one. #define SHIELD_COINBASE_DEFAULT_MINERS_FEE 10000 @@ -80,8 +79,6 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs bool cheatSpend = false; // set when this is shielding a cheating coinbase - bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. - private: friend class ShieldToAddress; friend class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase; // class for unit testing @@ -109,9 +106,6 @@ private: void lock_utxos(); void unlock_utxos(); - - // payment disclosure! - std::vector paymentDisclosureData_; }; class ShieldToAddress : public boost::static_visitor @@ -123,7 +117,6 @@ public: ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) : m_op(op), sendAmount(sendAmount) {} - bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 38aff1a04..9d11a55fe 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2009-2013 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://www.opensource.org/licenses/mit-license.php /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * @@ -168,22 +169,6 @@ static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, - const libzcash::SproutPaymentAddress& address, - libzcash::SproutSpendingKey& sk) -{ - CKeyingMaterial vchSecret; - if (!DecryptSecret(vMasterKey, vchCryptedSecret, address.GetHash(), vchSecret)) - return false; - - if (vchSecret.size() != libzcash::SerializedSproutSpendingKeySize) - return false; - - CSecureDataStream ss(vchSecret, SER_NETWORK, PROTOCOL_VERSION); - ss >> sk; - return sk.address() == address; -} static bool DecryptSaplingSpendingKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, @@ -207,7 +192,7 @@ bool CCryptoKeyStore::SetCrypted() LOCK2(cs_KeyStore, cs_SpendingKeyStore); if (fUseCrypto) return true; - if (!(mapKeys.empty() && mapSproutSpendingKeys.empty() && mapSaplingSpendingKeys.empty())) + if (!(mapKeys.empty() && mapSaplingSpendingKeys.empty())) return false; fUseCrypto = true; return true; @@ -260,21 +245,6 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) if (fDecryptionThoroughlyChecked) break; } - CryptedSproutSpendingKeyMap::const_iterator miSprout = mapCryptedSproutSpendingKeys.begin(); - for (; miSprout != mapCryptedSproutSpendingKeys.end(); ++miSprout) - { - const libzcash::SproutPaymentAddress &address = (*miSprout).first; - const std::vector &vchCryptedSecret = (*miSprout).second; - libzcash::SproutSpendingKey sk; - if (!DecryptSproutSpendingKey(vMasterKeyIn, vchCryptedSecret, address, sk)) - { - keyFail = true; - break; - } - keyPass = true; - if (fDecryptionThoroughlyChecked) - break; - } CryptedSaplingSpendingKeyMap::const_iterator miSapling = mapCryptedSaplingSpendingKeys.begin(); for (; miSapling != mapCryptedSaplingSpendingKeys.end(); ++miSapling) { @@ -292,7 +262,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) } if (keyPass && keyFail) { - LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); + LogPrintf("Oh shit! The wallet is probably corrupted: Some keys decrypt but not all.\n"); assert(false); } if (keyFail || !keyPass) @@ -440,30 +410,6 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co return false; } -bool CCryptoKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) -{ - { - LOCK(cs_SpendingKeyStore); - if (!IsCrypted()) - return CBasicKeyStore::AddSproutSpendingKey(sk); - - if (IsLocked()) - return false; - - std::vector vchCryptedSecret; - CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << sk; - CKeyingMaterial vchSecret(ss.begin(), ss.end()); - auto address = sk.address(); - if (!EncryptSecret(vMasterKey, vchSecret, address.GetHash(), vchCryptedSecret)) - return false; - - if (!AddCryptedSproutSpendingKey(address, sk.receiving_key(), vchCryptedSecret)) - return false; - } - return true; -} - bool CCryptoKeyStore::AddSaplingSpendingKey( const libzcash::SaplingExtendedSpendingKey &sk, const libzcash::SaplingPaymentAddress &defaultAddr) @@ -494,22 +440,6 @@ bool CCryptoKeyStore::AddSaplingSpendingKey( return true; } -bool CCryptoKeyStore::AddCryptedSproutSpendingKey( - const libzcash::SproutPaymentAddress &address, - const libzcash::ReceivingKey &rk, - const std::vector &vchCryptedSecret) -{ - { - LOCK(cs_SpendingKeyStore); - if (!SetCrypted()) - return false; - - mapCryptedSproutSpendingKeys[address] = vchCryptedSecret; - mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(rk))); - } - return true; -} - bool CCryptoKeyStore::AddCryptedSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector &vchCryptedSecret, @@ -531,23 +461,6 @@ bool CCryptoKeyStore::AddCryptedSaplingSpendingKey( return true; } -bool CCryptoKeyStore::GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey &skOut) const -{ - { - LOCK(cs_SpendingKeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetSproutSpendingKey(address, skOut); - - CryptedSproutSpendingKeyMap::const_iterator mi = mapCryptedSproutSpendingKeys.find(address); - if (mi != mapCryptedSproutSpendingKeys.end()) - { - const std::vector &vchCryptedSecret = (*mi).second; - return DecryptSproutSpendingKey(vMasterKey, vchCryptedSecret, address, skOut); - } - } - return false; -} - bool CCryptoKeyStore::GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey &skOut) const { { @@ -603,22 +516,6 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) } } mapKeys.clear(); - BOOST_FOREACH(SproutSpendingKeyMap::value_type& mSproutSpendingKey, mapSproutSpendingKeys) - { - const libzcash::SproutSpendingKey &sk = mSproutSpendingKey.second; - CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << sk; - CKeyingMaterial vchSecret(ss.begin(), ss.end()); - libzcash::SproutPaymentAddress address = sk.address(); - std::vector vchCryptedSecret; - if (!EncryptSecret(vMasterKeyIn, vchSecret, address.GetHash(), vchCryptedSecret)) { - return false; - } - if (!AddCryptedSproutSpendingKey(address, sk.receiving_key(), vchCryptedSecret)) { - return false; - } - } - mapSproutSpendingKeys.clear(); //! Sapling key support BOOST_FOREACH(SaplingSpendingKeyMap::value_type& mSaplingSpendingKey, mapSaplingSpendingKeys) { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index f42a762af..8a7de32b4 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -1,4 +1,5 @@ // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -145,7 +146,7 @@ class CCryptoKeyStore : public CBasicKeyStore private: std::pair> cryptedHDSeed; CryptedKeyMap mapCryptedKeys; - CryptedSproutSpendingKeyMap mapCryptedSproutSpendingKeys; + //CryptedSproutSpendingKeyMap mapCryptedSproutSpendingKeys; CryptedSaplingSpendingKeyMap mapCryptedSaplingSpendingKeys; CKeyingMaterial vMasterKey; @@ -223,37 +224,6 @@ public: mi++; } } - virtual bool AddCryptedSproutSpendingKey( - const libzcash::SproutPaymentAddress &address, - const libzcash::ReceivingKey &rk, - const std::vector &vchCryptedSecret); - bool AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk); - bool HaveSproutSpendingKey(const libzcash::SproutPaymentAddress &address) const - { - { - LOCK(cs_SpendingKeyStore); - if (!IsCrypted()) - return CBasicKeyStore::HaveSproutSpendingKey(address); - return mapCryptedSproutSpendingKeys.count(address) > 0; - } - return false; - } - bool GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey &skOut) const; - void GetSproutPaymentAddresses(std::set &setAddress) const - { - if (!IsCrypted()) - { - CBasicKeyStore::GetSproutPaymentAddresses(setAddress); - return; - } - setAddress.clear(); - CryptedSproutSpendingKeyMap::const_iterator mi = mapCryptedSproutSpendingKeys.begin(); - while (mi != mapCryptedSproutSpendingKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } - } //! Sapling virtual bool AddCryptedSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index dcc7fdfdd..6373b6e9f 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -75,11 +75,6 @@ CWalletTx GetValidReceive(const libzcash::SproutSpendingKey& sk, CAmount value, return GetValidReceive(*params, sk, value, randomInputs, version); } -libzcash::SproutNote GetNote(const libzcash::SproutSpendingKey& sk, - const CTransaction& tx, size_t js, size_t n) { - return GetNote(*params, sk, tx, js, n); -} - CWalletTx GetValidSpend(const libzcash::SproutSpendingKey& sk, const libzcash::SproutNote& note, CAmount value) { return GetValidSpend(*params, sk, note, value); @@ -139,341 +134,8 @@ TEST(WalletTests, SetupDatadirLocationRunAsFirstTest) { boost::filesystem::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); } +/* -TEST(WalletTests, SproutNoteDataSerialisation) { - auto sk = libzcash::SproutSpendingKey::random(); - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - SproutMerkleTree tree; - nd.witnesses.push_front(tree.witness()); - noteData[jsoutpt] = nd; - - CDataStream ss(SER_DISK, CLIENT_VERSION); - ss << noteData; - - mapSproutNoteData_t noteData2; - ss >> noteData2; - - EXPECT_EQ(noteData, noteData2); - EXPECT_EQ(noteData[jsoutpt].witnesses, noteData2[jsoutpt].witnesses); -} - - -TEST(WalletTests, FindUnspentSproutNotes) { - SelectParams(CBaseChainParams::TESTNET); - CWallet wallet; - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - - wtx.SetSproutNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - EXPECT_FALSE(wallet.IsSproutSpent(nullifier)); - - // We currently have an unspent and unconfirmed note in the wallet (depth of -1) - std::vector sproutEntries; - std::vector saplingEntries; - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); - EXPECT_EQ(0, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - - // Fake-mine the transaction - EXPECT_EQ(-1, chainActive.Height()); - CBlock block; - block.vtx.push_back(wtx); - block.hashMerkleRoot = block.BuildMerkleTree(); - auto blockHash = block.GetHash(); - CBlockIndex fakeIndex {block}; - mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); - chainActive.SetTip(&fakeIndex); - EXPECT_TRUE(chainActive.Contains(&fakeIndex)); - EXPECT_EQ(0, chainActive.Height()); - - wtx.SetMerkleBranch(block); - wallet.AddToWallet(wtx, true, NULL); - EXPECT_FALSE(wallet.IsSproutSpent(nullifier)); - - - // We now have an unspent and confirmed note in the wallet (depth of 1) - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2); - EXPECT_EQ(0, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - - - // Let's spend the note. - auto wtx2 = GetValidSpend(sk, note, 5); - wallet.AddToWallet(wtx2, true, NULL); - EXPECT_FALSE(wallet.IsSproutSpent(nullifier)); - - // Fake-mine a spend transaction - EXPECT_EQ(0, chainActive.Height()); - CBlock block2; - block2.vtx.push_back(wtx2); - block2.hashMerkleRoot = block2.BuildMerkleTree(); - block2.hashPrevBlock = blockHash; - auto blockHash2 = block2.GetHash(); - CBlockIndex fakeIndex2 {block2}; - mapBlockIndex.insert(std::make_pair(blockHash2, &fakeIndex2)); - fakeIndex2.SetHeight(1); - chainActive.SetTip(&fakeIndex2); - EXPECT_TRUE(chainActive.Contains(&fakeIndex2)); - EXPECT_EQ(1, chainActive.Height()); - - wtx2.SetMerkleBranch(block2); - wallet.AddToWallet(wtx2, true, NULL); - EXPECT_TRUE(wallet.IsSproutSpent(nullifier)); - - // The note has been spent. By default, GetFilteredNotes() ignores spent notes. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); - EXPECT_EQ(0, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - // Let's include spent notes to retrieve it. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - // The spent note has two confirmations. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - // It does not have 3 confirmations. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false); - EXPECT_EQ(0, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - - - // Let's receive a new note - CWalletTx wtx3; - { - auto wtx = GetValidReceive(sk, 20, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - - wtx.SetSproutNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - EXPECT_FALSE(wallet.IsSproutSpent(nullifier)); - - wtx3 = wtx; - } - - // Fake-mine the new transaction - EXPECT_EQ(1, chainActive.Height()); - CBlock block3; - block3.vtx.push_back(wtx3); - block3.hashMerkleRoot = block3.BuildMerkleTree(); - block3.hashPrevBlock = blockHash2; - auto blockHash3 = block3.GetHash(); - CBlockIndex fakeIndex3 {block3}; - mapBlockIndex.insert(std::make_pair(blockHash3, &fakeIndex3)); - fakeIndex3.SetHeight(2); - chainActive.SetTip(&fakeIndex3); - EXPECT_TRUE(chainActive.Contains(&fakeIndex3)); - EXPECT_EQ(2, chainActive.Height()); - - wtx3.SetMerkleBranch(block3); - wallet.AddToWallet(wtx3, true, NULL); - - // We now have an unspent note which has one confirmation, in addition to our spent note. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - // Let's return the spent note too. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false); - EXPECT_EQ(2, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - // Increasing number of confirmations will exclude our new unspent note. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false); - EXPECT_EQ(1, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - // If we also ignore spent notes at this depth, we won't find any notes. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true); - EXPECT_EQ(0, sproutEntries.size()); - sproutEntries.clear(); - saplingEntries.clear(); - - // Tear down - chainActive.SetTip(NULL); - mapBlockIndex.erase(blockHash); - mapBlockIndex.erase(blockHash2); - mapBlockIndex.erase(blockHash3); -} - - -TEST(WalletTests, SetSproutNoteAddrsInCWalletTx) { - auto sk = libzcash::SproutSpendingKey::random(); - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - EXPECT_EQ(0, wtx.mapSproutNoteData.size()); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - - wtx.SetSproutNoteData(noteData); - EXPECT_EQ(noteData, wtx.mapSproutNoteData); -} - -TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) { - SelectParams(CBaseChainParams::REGTEST); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); - auto consensusParams = Params().GetConsensus(); - - TestWallet wallet; - - std::vector> rawSeed(32); - HDSeed seed(rawSeed); - auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed); - auto expsk = sk.expsk; - auto fvk = expsk.full_viewing_key(); - auto ivk = fvk.in_viewing_key(); - auto pk = sk.DefaultAddress(); - - libzcash::SaplingNote note(pk, 50000); - auto cm = note.cm().get(); - SaplingMerkleTree tree; - tree.append(cm); - auto anchor = tree.root(); - auto witness = tree.witness(); - - auto nf = note.nullifier(fvk, witness.position()); - ASSERT_TRUE(nf); - uint256 nullifier = nf.get(); - - auto builder = TransactionBuilder(consensusParams, 1); - ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness)); - builder.AddSaplingOutput(fvk.ovk, pk, 50000, {}); - builder.SetFee(0); - auto maybe_tx = builder.Build(); - ASSERT_EQ(static_cast(maybe_tx), true); - auto tx = maybe_tx.get(); - - CWalletTx wtx {&wallet, tx}; - - EXPECT_EQ(0, wtx.mapSaplingNoteData.size()); - mapSaplingNoteData_t noteData; - - SaplingOutPoint op {wtx.GetHash(), 0}; - SaplingNoteData nd; - nd.nullifier = nullifier; - nd.ivk = ivk; - nd.witnesses.push_front(witness); - nd.witnessHeight = 123; - noteData.insert(std::make_pair(op, nd)); - - wtx.SetSaplingNoteData(noteData); - EXPECT_EQ(noteData, wtx.mapSaplingNoteData); - - // Test individual fields in case equality operator is defined/changed. - EXPECT_EQ(ivk, wtx.mapSaplingNoteData[op].ivk); - EXPECT_EQ(nullifier, wtx.mapSaplingNoteData[op].nullifier); - EXPECT_EQ(nd.witnessHeight, wtx.mapSaplingNoteData[op].witnessHeight); - EXPECT_TRUE(witness == wtx.mapSaplingNoteData[op].witnesses.front()); - - // Revert to default - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); -} - -TEST(WalletTests, SetSproutInvalidNoteAddrsInCWalletTx) { - CWalletTx wtx; - EXPECT_EQ(0, wtx.mapSproutNoteData.size()); - - mapSproutNoteData_t noteData; - auto sk = libzcash::SproutSpendingKey::random(); - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), uint256()}; - noteData[jsoutpt] = nd; - - EXPECT_THROW(wtx.SetSproutNoteData(noteData), std::logic_error); -} - -// The following test is the same as SetInvalidSaplingNoteDataInCWalletTx -// TEST(WalletTests, SetSaplingInvalidNoteAddrsInCWalletTx) - -// Cannot add note data for an index which does not exist in tx.vShieldedOutput -TEST(WalletTests, SetInvalidSaplingNoteDataInCWalletTx) { - CWalletTx wtx; - EXPECT_EQ(0, wtx.mapSaplingNoteData.size()); - - mapSaplingNoteData_t noteData; - SaplingOutPoint op {uint256(), 1}; - SaplingNoteData nd; - noteData.insert(std::make_pair(op, nd)); - - EXPECT_THROW(wtx.SetSaplingNoteData(noteData), std::logic_error); -} - -TEST(WalletTests, GetSproutNoteNullifier) { - CWallet wallet; - - auto sk = libzcash::SproutSpendingKey::random(); - auto address = sk.address(); - auto dec = ZCNoteDecryption(sk.receiving_key()); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - auto hSig = wtx.vjoinsplit[0].h_sig( - *params, wtx.joinSplitPubKey); - - auto ret = wallet.GetSproutNoteNullifier( - wtx.vjoinsplit[0], - address, - dec, - hSig, 1); - EXPECT_NE(nullifier, ret); - - wallet.AddSproutSpendingKey(sk); - - ret = wallet.GetSproutNoteNullifier( - wtx.vjoinsplit[0], - address, - dec, - hSig, 1); - EXPECT_EQ(nullifier, ret); -} TEST(WalletTests, FindMySaplingNotes) { SelectParams(CBaseChainParams::REGTEST); @@ -577,92 +239,6 @@ TEST(WalletTests, FindMySaplingNotesWithIvkOnly) { UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); } -TEST(WalletTests, FindMySproutNotes) { - CWallet wallet; - - auto sk = libzcash::SproutSpendingKey::random(); - auto sk2 = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk2); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - auto noteMap = wallet.FindMySproutNotes(wtx); - EXPECT_EQ(0, noteMap.size()); - - wallet.AddSproutSpendingKey(sk); - - noteMap = wallet.FindMySproutNotes(wtx); - EXPECT_EQ(2, noteMap.size()); - - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - EXPECT_EQ(1, noteMap.count(jsoutpt)); - EXPECT_EQ(nd, noteMap[jsoutpt]); -} - -TEST(WalletTests, FindMySproutNotesInEncryptedWallet) { - TestWallet wallet; - uint256 r {GetRandHash()}; - CKeyingMaterial vMasterKey (r.begin(), r.end()); - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - ASSERT_TRUE(wallet.EncryptKeys(vMasterKey)); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - auto noteMap = wallet.FindMySproutNotes(wtx); - EXPECT_EQ(2, noteMap.size()); - - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - EXPECT_EQ(1, noteMap.count(jsoutpt)); - EXPECT_NE(nd, noteMap[jsoutpt]); - - ASSERT_TRUE(wallet.Unlock(vMasterKey)); - - noteMap = wallet.FindMySproutNotes(wtx); - EXPECT_EQ(2, noteMap.size()); - EXPECT_EQ(1, noteMap.count(jsoutpt)); - EXPECT_EQ(nd, noteMap[jsoutpt]); -} - -TEST(WalletTests, GetConflictedSproutNotes) { - CWallet wallet; - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - auto wtx2 = GetValidSpend(sk, note, 5); - auto wtx3 = GetValidSpend(sk, note, 10); - auto hash2 = wtx2.GetHash(); - auto hash3 = wtx3.GetHash(); - - // No conflicts for no spends - EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); - wallet.AddToWallet(wtx, true, NULL); - EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); - - // No conflicts for one spend - wallet.AddToWallet(wtx2, true, NULL); - EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); - - // Conflicts for two spends - wallet.AddToWallet(wtx3, true, NULL); - auto c3 = wallet.GetConflicts(hash2); - EXPECT_EQ(2, c3.size()); - EXPECT_EQ(std::set({hash2, hash3}), c3); -} - // Generate note A and spend to create note B, from which we spend to create two conflicting transactions TEST(WalletTests, GetConflictedSaplingNotes) { SelectParams(CBaseChainParams::REGTEST); @@ -1735,88 +1311,6 @@ TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) { wallet.SetBestChain(walletdb, loc); } -TEST(WalletTests, UpdateSproutNullifierNoteMap) { - TestWallet wallet; - uint256 r {GetRandHash()}; - CKeyingMaterial vMasterKey (r.begin(), r.end()); - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - ASSERT_TRUE(wallet.EncryptKeys(vMasterKey)); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - // Pretend that we called FindMySproutNotes while the wallet was locked - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address()}; - noteData[jsoutpt] = nd; - wtx.SetSproutNoteData(noteData); - - wallet.AddToWallet(wtx, true, NULL); - EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes.count(nullifier)); - - EXPECT_FALSE(wallet.UpdateNullifierNoteMap()); - - ASSERT_TRUE(wallet.Unlock(vMasterKey)); - - EXPECT_TRUE(wallet.UpdateNullifierNoteMap()); - EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes.count(nullifier)); - EXPECT_EQ(wtx.GetHash(), wallet.mapSproutNullifiersToNotes[nullifier].hash); - EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes[nullifier].js); - EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes[nullifier].n); -} - -TEST(WalletTests, UpdatedSproutNoteData) { - TestWallet wallet; - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 0); - auto note2 = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - auto nullifier2 = note2.nullifier(sk); - auto wtx2 = wtx; - - // First pretend we added the tx to the wallet and - // we don't have the key for the second note - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 0}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - wtx.SetSproutNoteData(noteData); - - // Pretend we mined the tx by adding a fake witness - SproutMerkleTree tree; - wtx.mapSproutNoteData[jsoutpt].witnesses.push_front(tree.witness()); - wtx.mapSproutNoteData[jsoutpt].witnessHeight = 100; - - // Now pretend we added the key for the second note, and - // the tx was "added" to the wallet again to update it. - // This happens via the 'z_importkey' RPC method. - JSOutPoint jsoutpt2 {wtx2.GetHash(), 0, 1}; - SproutNoteData nd2 {sk.address(), nullifier2}; - noteData[jsoutpt2] = nd2; - wtx2.SetSproutNoteData(noteData); - - // The txs should initially be different - EXPECT_NE(wtx.mapSproutNoteData, wtx2.mapSproutNoteData); - EXPECT_EQ(1, wtx.mapSproutNoteData[jsoutpt].witnesses.size()); - EXPECT_EQ(100, wtx.mapSproutNoteData[jsoutpt].witnessHeight); - - // After updating, they should be the same - EXPECT_TRUE(wallet.UpdatedNoteData(wtx2, wtx)); - EXPECT_EQ(wtx.mapSproutNoteData, wtx2.mapSproutNoteData); - EXPECT_EQ(1, wtx.mapSproutNoteData[jsoutpt].witnesses.size()); - EXPECT_EQ(100, wtx.mapSproutNoteData[jsoutpt].witnessHeight); - // TODO: The new note should get witnessed (but maybe not here) (#1350) -} - TEST(WalletTests, UpdatedSaplingNoteData) { SelectParams(CBaseChainParams::REGTEST); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); @@ -1941,37 +1435,6 @@ TEST(WalletTests, UpdatedSaplingNoteData) { UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); } -TEST(WalletTests, MarkAffectedSproutTransactionsDirty) { - TestWallet wallet; - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - auto wtx = GetValidReceive(sk, 10, true); - auto hash = wtx.GetHash(); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - auto wtx2 = GetValidSpend(sk, note, 5); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {hash, 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - - wtx.SetSproutNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - wallet.MarkAffectedTransactionsDirty(wtx); - - // After getting a cached value, the first tx should be clean - wallet.mapWallet[hash].GetDebit(ISMINE_ALL); - EXPECT_TRUE(wallet.mapWallet[hash].fDebitCached); - - // After adding the note spend, the first tx should be dirty - wallet.AddToWallet(wtx2, true, NULL); - wallet.MarkAffectedTransactionsDirty(wtx2); - EXPECT_FALSE(wallet.mapWallet[hash].fDebitCached); -} - TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { SelectParams(CBaseChainParams::REGTEST); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); @@ -1999,7 +1462,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID()); // Generate shielding tx from transparent to Sapling - // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee + // 0.0005 t-HUSH in, 0.0004 z-HUSH out, 0.0001 t-HUSH fee auto builder = TransactionBuilder(consensusParams, 1, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000); builder.AddSaplingOutput(fvk.ovk, pk, 40000, {}); @@ -2093,39 +1556,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); } - -TEST(WalletTests, SproutNoteLocking) { - TestWallet wallet; - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - auto wtx = GetValidReceive(sk, 10, true); - auto wtx2 = GetValidReceive(sk, 10, true); - - JSOutPoint jsoutpt {wtx.GetHash(), 0, 0}; - JSOutPoint jsoutpt2 {wtx2.GetHash(),0, 0}; - - // Test selective locking - wallet.LockNote(jsoutpt); - EXPECT_TRUE(wallet.IsLockedNote(jsoutpt)); - EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2)); - - // Test selective unlocking - wallet.UnlockNote(jsoutpt); - EXPECT_FALSE(wallet.IsLockedNote(jsoutpt)); - - // Test multiple locking - wallet.LockNote(jsoutpt); - wallet.LockNote(jsoutpt2); - EXPECT_TRUE(wallet.IsLockedNote(jsoutpt)); - EXPECT_TRUE(wallet.IsLockedNote(jsoutpt2)); - - // Test unlock all - wallet.UnlockAllSproutNotes(); - EXPECT_FALSE(wallet.IsLockedNote(jsoutpt)); - EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2)); -} +*/ TEST(WalletTests, SaplingNoteLocking) { TestWallet wallet; diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 365533b6c..7811c9c8a 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -106,303 +106,6 @@ TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) { EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa2)); } -/** - * This test covers methods on CWallet - * GenerateNewSproutZKey() - * AddSproutZKey() - * LoadZKey() - * LoadZKeyMetadata() - */ -TEST(wallet_zkeys_tests, store_and_load_zkeys) { - SelectParams(CBaseChainParams::MAIN); - - CWallet wallet; - - // wallet should be empty - std::set addrs; - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(0, addrs.size()); - - // wallet should have one key - auto addr = wallet.GenerateNewSproutZKey(); - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(1, addrs.size()); - - // verify wallet has spending key for the address - ASSERT_TRUE(wallet.HaveSproutSpendingKey(addr)); - - // manually add new spending key to wallet - auto sk = libzcash::SproutSpendingKey::random(); - ASSERT_TRUE(wallet.AddSproutZKey(sk)); - - // verify wallet did add it - addr = sk.address(); - ASSERT_TRUE(wallet.HaveSproutSpendingKey(addr)); - - // verify spending key stored correctly - libzcash::SproutSpendingKey keyOut; - wallet.GetSproutSpendingKey(addr, keyOut); - ASSERT_EQ(sk, keyOut); - - // verify there are two keys - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(2, addrs.size()); - ASSERT_EQ(1, addrs.count(addr)); - - // Load a third key into the wallet - sk = libzcash::SproutSpendingKey::random(); - ASSERT_TRUE(wallet.LoadZKey(sk)); - - // attach metadata to this third key - addr = sk.address(); - int64_t now = GetTime(); - CKeyMetadata meta(now); - ASSERT_TRUE(wallet.LoadZKeyMetadata(addr, meta)); - - // check metadata is the same - CKeyMetadata m= wallet.mapSproutZKeyMetadata[addr]; - ASSERT_EQ(m.nCreateTime, now); -} - -/** - * This test covers methods on CWallet - * AddSproutViewingKey() - * RemoveSproutViewingKey() - * LoadSproutViewingKey() - */ -TEST(wallet_zkeys_tests, StoreAndLoadViewingKeys) { - SelectParams(CBaseChainParams::MAIN); - - CWallet wallet; - - // wallet should be empty - std::set addrs; - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(0, addrs.size()); - - // manually add new viewing key to wallet - auto sk = libzcash::SproutSpendingKey::random(); - auto vk = sk.viewing_key(); - ASSERT_TRUE(wallet.AddSproutViewingKey(vk)); - - // verify wallet did add it - auto addr = sk.address(); - ASSERT_TRUE(wallet.HaveSproutViewingKey(addr)); - // and that we don't have the corresponding spending key - ASSERT_FALSE(wallet.HaveSproutSpendingKey(addr)); - - // verify viewing key stored correctly - libzcash::SproutViewingKey vkOut; - wallet.GetSproutViewingKey(addr, vkOut); - ASSERT_EQ(vk, vkOut); - - // Load a second viewing key into the wallet - auto sk2 = libzcash::SproutSpendingKey::random(); - ASSERT_TRUE(wallet.LoadSproutViewingKey(sk2.viewing_key())); - - // verify wallet did add it - auto addr2 = sk2.address(); - ASSERT_TRUE(wallet.HaveSproutViewingKey(addr2)); - ASSERT_FALSE(wallet.HaveSproutSpendingKey(addr2)); - - // Remove the first viewing key - ASSERT_TRUE(wallet.RemoveSproutViewingKey(vk)); - ASSERT_FALSE(wallet.HaveSproutViewingKey(addr)); - ASSERT_TRUE(wallet.HaveSproutViewingKey(addr2)); -} - -/** - * This test covers methods on CWalletDB - * WriteZKey() - */ -TEST(wallet_zkeys_tests, write_zkey_direct_to_db) { - SelectParams(CBaseChainParams::TESTNET); - - // Get temporary and unique path for file. - // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); - mapArgs["-datadir"] = pathTemp.string(); - - bool fFirstRun; - CWallet wallet("wallet.dat"); - ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); - - // No default CPubKey set - ASSERT_TRUE(fFirstRun); - - // wallet should be empty - std::set addrs; - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(0, addrs.size()); - - // Add random key to the wallet - auto paymentAddress = wallet.GenerateNewSproutZKey(); - - // wallet should have one key - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(1, addrs.size()); - - // create random key and add it to database directly, bypassing wallet - auto sk = libzcash::SproutSpendingKey::random(); - auto addr = sk.address(); - int64_t now = GetTime(); - CKeyMetadata meta(now); - CWalletDB db("wallet.dat"); - db.WriteZKey(addr, sk, meta); - - // wallet should not be aware of key - ASSERT_FALSE(wallet.HaveSproutSpendingKey(addr)); - - // wallet sees one key - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(1, addrs.size()); - - // wallet should have default metadata for addr with null createtime - CKeyMetadata m = wallet.mapSproutZKeyMetadata[addr]; - ASSERT_EQ(m.nCreateTime, 0); - ASSERT_NE(m.nCreateTime, now); - - // load the wallet again - ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); - - // wallet can now see the spending key - ASSERT_TRUE(wallet.HaveSproutSpendingKey(addr)); - - // check key is the same - libzcash::SproutSpendingKey keyOut; - wallet.GetSproutSpendingKey(addr, keyOut); - ASSERT_EQ(sk, keyOut); - - // wallet should have two keys - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(2, addrs.size()); - - // check metadata is now the same - m = wallet.mapSproutZKeyMetadata[addr]; - ASSERT_EQ(m.nCreateTime, now); -} - -/** - * This test covers methods on CWalletDB - * WriteSproutViewingKey() - */ -TEST(wallet_zkeys_tests, WriteViewingKeyDirectToDB) { - SelectParams(CBaseChainParams::TESTNET); - - // Get temporary and unique path for file. - // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); - mapArgs["-datadir"] = pathTemp.string(); - - bool fFirstRun; - CWallet wallet("wallet-vkey.dat"); - ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); - - // No default CPubKey set - ASSERT_TRUE(fFirstRun); - - // create random viewing key and add it to database directly, bypassing wallet - auto sk = libzcash::SproutSpendingKey::random(); - auto vk = sk.viewing_key(); - auto addr = sk.address(); - int64_t now = GetTime(); - CKeyMetadata meta(now); - CWalletDB db("wallet-vkey.dat"); - db.WriteSproutViewingKey(vk); - - // wallet should not be aware of viewing key - ASSERT_FALSE(wallet.HaveSproutViewingKey(addr)); - - // load the wallet again - ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); - - // wallet can now see the viewing key - ASSERT_TRUE(wallet.HaveSproutViewingKey(addr)); - - // check key is the same - libzcash::SproutViewingKey vkOut; - wallet.GetSproutViewingKey(addr, vkOut); - ASSERT_EQ(vk, vkOut); -} - - - -/** - * This test covers methods on CWalletDB to load/save crypted z keys. - */ -TEST(wallet_zkeys_tests, write_cryptedzkey_direct_to_db) { - SelectParams(CBaseChainParams::TESTNET); - - // Get temporary and unique path for file. - // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); - mapArgs["-datadir"] = pathTemp.string(); - - bool fFirstRun; - CWallet wallet("wallet_crypted.dat"); - ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); - - // No default CPubKey set - ASSERT_TRUE(fFirstRun); - - // wallet should be empty - std::set addrs; - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(0, addrs.size()); - - // Add random key to the wallet - auto paymentAddress = wallet.GenerateNewSproutZKey(); - - // wallet should have one key - wallet.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(1, addrs.size()); - - // encrypt wallet - SecureString strWalletPass; - strWalletPass.reserve(100); - strWalletPass = "hello"; - ASSERT_TRUE(wallet.EncryptWallet(strWalletPass)); - - // adding a new key will fail as the wallet is locked - EXPECT_ANY_THROW(wallet.GenerateNewSproutZKey()); - - // unlock wallet and then add - wallet.Unlock(strWalletPass); - auto paymentAddress2 = wallet.GenerateNewSproutZKey(); - - // Create a new wallet from the existing wallet path - CWallet wallet2("wallet_crypted.dat"); - ASSERT_EQ(DB_LOAD_OK, wallet2.LoadWallet(fFirstRun)); - - // Confirm it's not the same as the other wallet - ASSERT_TRUE(&wallet != &wallet2); - - // wallet should have two keys - wallet2.GetSproutPaymentAddresses(addrs); - ASSERT_EQ(2, addrs.size()); - - // check we have entries for our payment addresses - ASSERT_TRUE(addrs.count(paymentAddress)); - ASSERT_TRUE(addrs.count(paymentAddress2)); - - // spending key is crypted, so we can't extract valid payment address - libzcash::SproutSpendingKey keyOut; - wallet2.GetSproutSpendingKey(paymentAddress, keyOut); - ASSERT_FALSE(paymentAddress == keyOut.address()); - - // unlock wallet to get spending keys and verify payment addresses - wallet2.Unlock(strWalletPass); - - wallet2.GetSproutSpendingKey(paymentAddress, keyOut); - ASSERT_EQ(paymentAddress, keyOut.address()); - - wallet2.GetSproutSpendingKey(paymentAddress2, keyOut); - ASSERT_EQ(paymentAddress2, keyOut.address()); -} - /** * This test covers methods on CWalletDB to load/save crypted sapling z keys. */ diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp deleted file mode 100644 index cd0cc42a6..000000000 --- a/src/wallet/rpcdisclosure.cpp +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "rpc/server.h" -#include "init.h" -#include "key_io.h" -#include "main.h" -#include "script/script.h" -#include "script/standard.h" -#include "sync.h" -#include "util.h" -#include "utiltime.h" -#include "wallet.h" - -#include -#include - -#include -#include - -#include - -#include "paymentdisclosure.h" -#include "paymentdisclosuredb.h" - -#include "zcash/Note.hpp" -#include "zcash/NoteEncryption.hpp" - -using namespace std; -using namespace libzcash; - -// Function declaration for function implemented in wallet/rpcwallet.cpp -bool EnsureWalletIsAvailable(bool avoidException); - -/** - * RPC call to generate a payment disclosure - */ -UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - string enableArg = "paymentdisclosure"; - auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true); - string strPaymentDisclosureDisabledMsg = ""; - if (!fEnablePaymentDisclosure) { - strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_getpaymentdisclosure", enableArg); - } - - if (fHelp || params.size() < 3 || params.size() > 4 ) - throw runtime_error( - "z_getpaymentdisclosure \"txid\" \"js_index\" \"output_index\" (\"message\") \n" - "\nGenerate a payment disclosure for a given joinsplit output.\n" - "\nEXPERIMENTAL FEATURE\n" - + strPaymentDisclosureDisabledMsg + - "\nArguments:\n" - "1. \"txid\" (string, required) \n" - "2. \"js_index\" (string, required) \n" - "3. \"output_index\" (string, required) \n" - "4. \"message\" (string, optional) \n" - "\nResult:\n" - "\"paymentdisclosure\" (string) Hex data string, with \"zpd:\" prefix.\n" - "\nExamples:\n" - + HelpExampleCli("z_getpaymentdisclosure", "96f12882450429324d5f3b48630e3168220e49ab7b0f066e5c2935a6b88bb0f2 0 0 \"refund\"") - + HelpExampleRpc("z_getpaymentdisclosure", "\"96f12882450429324d5f3b48630e3168220e49ab7b0f066e5c2935a6b88bb0f2\", 0, 0, \"refund\"") - ); - - if (!fEnablePaymentDisclosure) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosure is disabled."); - } - - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); - - // Check wallet knows about txid - string txid = params[0].get_str(); - uint256 hash; - hash.SetHex(txid); - - CTransaction tx; - uint256 hashBlock; - - // Check txid has been seen - if (!GetTransaction(hash, tx, hashBlock, true)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); - } - - // Check tx has been confirmed - if (hashBlock.IsNull()) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction has not been confirmed yet"); - } - - // Check is mine - if (!pwalletMain->mapWallet.count(hash)) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not belong to the wallet"); - } - const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - - // Check if shielded tx - if (wtx.vjoinsplit.empty()) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); - } - - // Check js_index - int js_index = params[1].get_int(); - if (js_index < 0 || js_index >= wtx.vjoinsplit.size()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid js_index"); - } - - // Check output_index - int output_index = params[2].get_int(); - if (output_index < 0 || output_index >= ZC_NUM_JS_OUTPUTS) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid output_index"); - } - - // Get message if it exists - string msg; - if (params.size() == 4) { - msg = params[3].get_str(); - } - - // Create PaymentDisclosureKey - PaymentDisclosureKey key = {hash, (size_t)js_index, (uint8_t)output_index }; - - // TODO: In future, perhaps init the DB in init.cpp - shared_ptr db = PaymentDisclosureDB::sharedInstance(); - PaymentDisclosureInfo info; - if (!db->Get(key, info)) { - throw JSONRPCError(RPC_DATABASE_ERROR, "Could not find payment disclosure info for the given joinsplit output"); - } - - PaymentDisclosure pd( wtx.joinSplitPubKey, key, info, msg ); - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << pd; - string strHex = HexStr(ss.begin(), ss.end()); - return PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX + strHex; -} - - - -/** - * RPC call to validate a payment disclosure data blob. - */ -UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - string enableArg = "paymentdisclosure"; - auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true); - string strPaymentDisclosureDisabledMsg = ""; - if (!fEnablePaymentDisclosure) { - strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_validatepaymentdisclosure", enableArg); - } - - if (fHelp || params.size() != 1) - throw runtime_error( - "z_validatepaymentdisclosure \"paymentdisclosure\"\n" - "\nValidates a payment disclosure.\n" - "\nEXPERIMENTAL FEATURE\n" - + strPaymentDisclosureDisabledMsg + - "\nArguments:\n" - "1. \"paymentdisclosure\" (string, required) Hex data string, with \"zpd:\" prefix.\n" - "\nExamples:\n" - + HelpExampleCli("z_validatepaymentdisclosure", "\"zpd:706462ff004c561a0447ba2ec51184e6c204...\"") - + HelpExampleRpc("z_validatepaymentdisclosure", "\"zpd:706462ff004c561a0447ba2ec51184e6c204...\"") - ); - - if (!fEnablePaymentDisclosure) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosure is disabled."); - } - - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); - - // Verify the payment disclosure input begins with "zpd:" prefix. - string strInput = params[0].get_str(); - size_t pos = strInput.find(PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX); - if (pos != 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure prefix not found."); - } - string hexInput = strInput.substr(strlen(PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX)); - if (!IsHex(hexInput)) - { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected payment disclosure data in hexadecimal format."); - } - - // Unserialize the payment disclosure data into an object - PaymentDisclosure pd; - CDataStream ss(ParseHex(hexInput), SER_NETWORK, PROTOCOL_VERSION); - try { - ss >> pd; - // too much data is ignored, but if not enough data, exception of type ios_base::failure is thrown, - // CBaseDataStream::read(): end of data: iostream error - } catch (const std::exception &e) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed."); - } - - if (pd.payload.marker != PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure marker not found."); - } - - if (pd.payload.version != PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Payment disclosure version is unsupported."); - } - - uint256 hash = pd.payload.txid; - CTransaction tx; - uint256 hashBlock; - // Check if we have seen the transaction - if (!GetTransaction(hash, tx, hashBlock, true)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); - } - - // Check if the transaction has been confirmed - if (hashBlock.IsNull()) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction has not been confirmed yet"); - } - - // Check if shielded tx - if (tx.vjoinsplit.empty()) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); - } - - UniValue errs(UniValue::VARR); - UniValue o(UniValue::VOBJ); - o.push_back(Pair("txid", pd.payload.txid.ToString())); - - // Check js_index - if (pd.payload.js >= tx.vjoinsplit.size()) { - errs.push_back("Payment disclosure refers to an invalid joinsplit index"); - } - o.push_back(Pair("jsIndex", pd.payload.js)); - - if (pd.payload.n < 0 || pd.payload.n >= ZC_NUM_JS_OUTPUTS) { - errs.push_back("Payment disclosure refers to an invalid output index"); - } - o.push_back(Pair("outputIndex", pd.payload.n)); - o.push_back(Pair("version", pd.payload.version)); - o.push_back(Pair("onetimePrivKey", pd.payload.esk.ToString())); - o.push_back(Pair("message", pd.payload.message)); - o.push_back(Pair("joinSplitPubKey", tx.joinSplitPubKey.ToString())); - - // Verify the payment disclosure was signed using the same key as the transaction i.e. the joinSplitPrivKey. - uint256 dataToBeSigned = SerializeHash(pd.payload, SER_GETHASH, 0); - bool sigVerified = (crypto_sign_verify_detached(pd.payloadSig.data(), - dataToBeSigned.begin(), 32, - tx.joinSplitPubKey.begin()) == 0); - o.push_back(Pair("signatureVerified", sigVerified)); - if (!sigVerified) { - errs.push_back("Payment disclosure signature does not match transaction signature"); - } - - // Check the payment address is valid - SproutPaymentAddress zaddr = pd.payload.zaddr; - { - o.push_back(Pair("paymentAddress", EncodePaymentAddress(zaddr))); - - try { - // Decrypt the note to get value and memo field - JSDescription jsdesc = tx.vjoinsplit[pd.payload.js]; - uint256 h_sig = jsdesc.h_sig(*pzcashParams, tx.joinSplitPubKey); - - ZCPaymentDisclosureNoteDecryption decrypter; - - ZCNoteEncryption::Ciphertext ciphertext = jsdesc.ciphertexts[pd.payload.n]; - - uint256 pk_enc = zaddr.pk_enc; - auto plaintext = decrypter.decryptWithEsk(ciphertext, pk_enc, pd.payload.esk, h_sig, pd.payload.n); - - CDataStream ssPlain(SER_NETWORK, PROTOCOL_VERSION); - ssPlain << plaintext; - SproutNotePlaintext npt; - ssPlain >> npt; - - string memoHexString = HexStr(npt.memo().data(), npt.memo().data() + npt.memo().size()); - o.push_back(Pair("memo", memoHexString)); - o.push_back(Pair("value", ValueFromAmount(npt.value()))); - - // Check the blockchain commitment matches decrypted note commitment - uint256 cm_blockchain = jsdesc.commitments[pd.payload.n]; - SproutNote note = npt.note(zaddr); - uint256 cm_decrypted = note.cm(); - bool cm_match = (cm_decrypted == cm_blockchain); - o.push_back(Pair("commitmentMatch", cm_match)); - if (!cm_match) { - errs.push_back("Commitment derived from payment disclosure does not match blockchain commitment"); - } - } catch (const std::exception &e) { - errs.push_back(string("Error while decrypting payment disclosure note: ") + string(e.what()) ); - } - } - - bool isValid = errs.empty(); - o.push_back(Pair("valid", isValid)); - if (!isValid) { - o.push_back(Pair("errors", errs)); - } - - return o; -} diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 44639f663..72af5d930 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -149,6 +149,43 @@ UniValue convertpassphrase(const UniValue& params, bool fHelp, const CPubKey& my return ret; } +UniValue rescan(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + //LOCK2(cs_main, pwalletMain->cs_wallet); + + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 1) + throw runtime_error( + "rescan \"height\"\n" + "\nRescan all transactions from genesis or given block height.\n" + "\nArguments:\n" + "1. \"height\" (integer, optional) The block height to rescan from\n" + "\nExamples:\n" + "\nRescan from block height 555\n" + + HelpExampleCli("rescan", "\"555\"") + + "\nRescan from genesis block\n" + + HelpExampleCli("rescan","") + ); + + // Height to rescan from + int nRescanHeight = 0; + if (params.size() > 0) + nRescanHeight = params[0].get_int(); + if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + } + + LogPrintf("Rescanning from height=%d\n", nRescanHeight); + //pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight],true); + bool update = false; + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(),update); + //TODO: can we return something more useful? + return NullUniValue; +} + + UniValue importprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (!EnsureWalletIsAvailable(fHelp)) @@ -856,48 +893,27 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp, const CPubKey& m throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); } - if (boost::get(&viewingkey) == nullptr) { - if (params.size() < 4) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Missing zaddr for Sapling viewing key."); - } - string strAddress = params[3].get_str(); - auto address = DecodePaymentAddress(strAddress); - if (!IsValidPaymentAddress(address)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); - } + if (params.size() < 4) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Missing zaddr for Sapling viewing key."); + } + string strAddress = params[3].get_str(); + auto address = DecodePaymentAddress(strAddress); + if (!IsValidPaymentAddress(address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); + } - auto addr = boost::get(address); - auto ivk = boost::get(viewingkey); + auto addr = boost::get(address); + auto ivk = boost::get(viewingkey); - if (pwalletMain->HaveSaplingIncomingViewingKey(addr)) { - if (fIgnoreExistingKey) { - return NullUniValue; - } - } else { - pwalletMain->MarkDirty(); - - if (!pwalletMain->AddSaplingIncomingViewingKey(ivk, addr)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); - } + if (pwalletMain->HaveSaplingIncomingViewingKey(addr)) { + if (fIgnoreExistingKey) { + return NullUniValue; } } else { - auto vkey = boost::get(viewingkey); - auto addr = vkey.address(); - if (pwalletMain->HaveSproutSpendingKey(addr)) { - throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this viewing key"); - } + pwalletMain->MarkDirty(); - // Don't throw error in case a viewing key is already there - if (pwalletMain->HaveSproutViewingKey(addr)) { - if (fIgnoreExistingKey) { - return NullUniValue; - } - } else { - pwalletMain->MarkDirty(); - - if (!pwalletMain->AddSproutViewingKey(vkey)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); - } + if (!pwalletMain->AddSaplingIncomingViewingKey(ivk, addr)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); } } @@ -977,26 +993,12 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& m throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } - if (boost::get(&address) == nullptr) { - auto addr = boost::get(address); - libzcash::SaplingIncomingViewingKey ivk; - if(!pwalletMain->GetSaplingIncomingViewingKey(addr, ivk)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold viewing key for this zaddr"); - } - return EncodeViewingKey(ivk); + auto addr = boost::get(address); + libzcash::SaplingIncomingViewingKey ivk; + if(!pwalletMain->GetSaplingIncomingViewingKey(addr, ivk)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold viewing key for this zaddr"); } - - auto addr = boost::get(address); - libzcash::SproutViewingKey vk; - if (!pwalletMain->GetSproutViewingKey(addr, vk)) { - libzcash::SproutSpendingKey k; - if (!pwalletMain->GetSproutSpendingKey(addr, k)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this zaddr"); - } - vk = k.viewing_key(); - } - - return EncodeViewingKey(vk); + return EncodeViewingKey(ivk); } extern int32_t KOMODO_NSPV; diff --git a/src/wallet/rpchushwallet.cpp b/src/wallet/rpchushwallet.cpp index 62b7a4fc9..64cd0e4a5 100644 --- a/src/wallet/rpchushwallet.cpp +++ b/src/wallet/rpchushwallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Copyright (c) 2019 Cryptoforge // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b8115767a..867de51a7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -38,6 +38,7 @@ #include "zcbenchmarks.h" #include "script/interpreter.h" #include "zcash/zip32.h" +#include "zcash/Note.hpp" #include "notaries_staked.h" #include "utiltime.h" @@ -70,7 +71,6 @@ using namespace libzcash; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern std::string ASSETCHAINS_OVERRIDE_PUBKEY; -const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; extern UniValue TxJoinSplitToJSON(const CTransaction& tx); extern int32_t KOMODO_INSYNC; @@ -3123,7 +3123,7 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&) if (wtx.GetDepthInMainChain() < 0) continue; - if (wtx.mapSaplingNoteData.size() == 0 && wtx.mapSproutNoteData.size() == 0 && !wtx.IsTrusted()) + if (wtx.mapSaplingNoteData.size() == 0 && !wtx.IsTrusted()) continue; //Assign Immature @@ -3309,7 +3309,7 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&) if (!CheckFinalTx(wtx)) continue; - if (wtx.mapSaplingNoteData.size() == 0 && wtx.mapSproutNoteData.size() == 0 && !wtx.IsTrusted()) + if (wtx.mapSaplingNoteData.size() == 0 && !wtx.IsTrusted()) continue; //Excude transactions with less confirmations than required @@ -3787,9 +3787,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue results(UniValue::VARR); if (zaddrs.size() > 0) { - std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); + pwalletMain->GetFilteredNotes(saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); std::set> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs); for (auto & entry : saplingEntries) { @@ -4047,10 +4046,9 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) { CAmount balance = 0; - std::vector sproutEntries; std::vector saplingEntries; LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, address, minDepth, true, ignoreUnspendable); + pwalletMain->GetFilteredNotes(saplingEntries, address, minDepth, true, ignoreUnspendable); for (auto & entry : saplingEntries) { balance += CAmount(entry.note.value()); } @@ -4102,16 +4100,14 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubK throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); } - // Visitor to support Sprout and Sapling addrs if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), zaddr) && !boost::apply_visitor(IncomingViewingKeyBelongsToWallet(pwalletMain), zaddr)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); } UniValue result(UniValue::VARR); - std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false); + pwalletMain->GetFilteredNotes(saplingEntries, fromaddress, nMinDepth, false, false); std::set> nullifierSet; auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); @@ -4213,6 +4209,42 @@ UniValue z_getbalance(const UniValue& params, bool fHelp, const CPubKey& mypk) return ValueFromAmount(nBalance); } +UniValue z_getnotescount(const UniValue& params, bool fHelp,const CPubKey& mypk) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 1) + throw runtime_error( + "z_getnotescount\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) Only include notes in transactions confirmed at least this many times.\n" + "\nReturns the number of sapling notes available in the wallet.\n" + "\nResult:\n" + "{\n" + " \"sapling\" (numeric) the number of sapling notes in the wallet\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_getnotescount", "0") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + int sapling = 0; + for (auto& wtx : pwalletMain->mapWallet) { + if (wtx.second.GetDepthInMainChain() >= nMinDepth) { + sapling += wtx.second.mapSaplingNoteData.size(); + } + } + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("sapling", sapling)); + + return ret; +} UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPubKey& mypk) { @@ -4232,7 +4264,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPubKey& my "\nResult:\n" "{\n" " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" - " \"private\": xxxxx, (numeric) the total balance of private funds (in both Sprout and Sapling addresses)\n" + " \"private\": xxxxx, (numeric) the total balance of private funds\n" " \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n" "}\n" "\nExamples:\n" @@ -4352,7 +4384,9 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp, const CPubKey& my // Fetch the note that is being spent auto res = pwalletMain->mapSaplingNullifiersToNotes.find(spend.nullifier); if (res == pwalletMain->mapSaplingNullifiersToNotes.end()) { - fprintf(stderr,"Could not find spending note %s", uint256_str(str, spend.nullifier)); + if(fZdebug) { + fprintf(stderr,"Could not find spending note %s\n", uint256_str(str, spend.nullifier)); + } continue; } auto op = res->second; @@ -4406,8 +4440,10 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp, const CPubKey& my pa = recovered->second; isOutgoing = true; } else { - // Unreadable - fprintf(stderr,"Could not recover Sapling note!"); + // Unreadable or unconfirmed? + if(fZdebug) { + fprintf(stderr,"Could not recover Sapling note!\n"); + } continue; } } @@ -4610,14 +4646,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); } - // Remember whether this is a Sprout or Sapling address + // Remember whether this is a Sapling address fromSapling = boost::get(&res) != nullptr; } - // This logic will need to be updated if we add a new shielded pool - bool fromSprout = !(fromTaddr || fromSapling); - - if (fromSprout) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from a Sprout zaddr, only Sapling zaddrs supported."); UniValue outputs = params[1].get_array(); @@ -4627,15 +4658,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk) // Keep track of addresses to spot duplicates set setAddress; - // Track whether we see any Sprout addresses - bool noSproutAddrs = !fromSprout; - // Recipients std::vector taddrRecipients; std::vector zaddrRecipients; CAmount nTotalOut = 0; - bool containsSproutOutput = false; bool containsSaplingOutput = false; for (const UniValue& o : outputs.getValues()) { @@ -4656,35 +4683,6 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk) auto res = DecodePaymentAddress(address); if (IsValidPaymentAddress(res, branchId)) { isZaddr = true; - - bool toSapling = boost::get(&res) != nullptr; - bool toSprout = !toSapling; - noSproutAddrs = noSproutAddrs && toSapling; - - containsSproutOutput |= toSprout; - containsSaplingOutput |= toSapling; - - // Sending to both Sprout and Sapling is currently unsupported using z_sendmany - if (containsSproutOutput && containsSaplingOutput) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Cannot send to both Sprout and Sapling addresses using z_sendmany"); - } - if ( GetTime() > KOMODO_SAPLING_DEADLINE ) - { - if ( fromSprout || toSprout ) - throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage has expired"); - } - if ( toSapling && ASSETCHAINS_SYMBOL[0] == 0 ) - throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage will expire soon"); - - // If we are sending from a shielded address, all recipient - // shielded addresses must be of the same type. - if ((fromSprout && toSapling) || (fromSapling && toSprout)) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Cannot send between Sprout and Sapling addresses using z_sendmany"); - } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); } @@ -4875,9 +4873,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk) // Builder (used if Sapling addresses are involved) boost::optional builder; - if (noSproutAddrs) { - builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); - } + builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); // Contextual transaction we will build on // (used if no Sapling addresses are involved) @@ -5140,7 +5136,6 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& myp #define MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT 10 #define MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT 90 -#define JOINSPLIT_SIZE GetSerializeSize(JSDescription(), SER_NETWORK, PROTOCOL_VERSION) #define OUTPUTDESCRIPTION_SIZE GetSerializeSize(OutputDescription(), SER_NETWORK, PROTOCOL_VERSION) #define SPENDDESCRIPTION_SIZE GetSerializeSize(SpendDescription(), SER_NETWORK, PROTOCOL_VERSION) @@ -5150,16 +5145,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp return NullUniValue; string enableArg = "zmergetoaddress"; - auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true); - std::string strDisabledMsg = ""; - if (!fEnableMergeToAddress) { - strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg); - } + //auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true); + //std::string strDisabledMsg = ""; + //if (!fEnableMergeToAddress) { + // strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg); + //} if (fHelp || params.size() < 2 || params.size() > 7) throw runtime_error( "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" - + strDisabledMsg + "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" "\nto combine those into a single note." "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" @@ -5203,20 +5197,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" "}\n" "\nExamples:\n" - + HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") - + HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + + HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' zs1aW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") + + HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"zs1aW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") ); - if (!fEnableMergeToAddress) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled."); - } - LOCK2(cs_main, pwalletMain->cs_wallet); THROW_IF_SYNCING(KOMODO_INSYNC); bool useAnyUTXO = false; - bool useAnySprout = false; bool useAnySapling = false; std::set taddrs = {}; std::set zaddrs = {}; @@ -5237,8 +5226,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp if (address == "ANY_TADDR") { useAnyUTXO = true; - } else if (address == "ANY_SPROUT") { - useAnySprout = true; } else if (address == "ANY_SAPLING") { useAnySapling = true; } else { @@ -5263,8 +5250,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp if (useAnyUTXO && taddrs.size() > 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific t-addrs when using \"ANY_TADDR\""); } - if ((useAnySprout || useAnySapling) && zaddrs.size() > 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\""); + if ((useAnySapling) && zaddrs.size() > 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SAPLING\""); } const int nextBlockHeight = chainActive.Height() + 1; @@ -5273,7 +5260,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp // Validate the destination address auto destaddress = params[1].get_str(); - bool isToSproutZaddr = false; bool isToSaplingZaddr = false; CTxDestination taddr = DecodeDestination(destaddress); if (!IsValidDestination(taddr)) { @@ -5286,7 +5272,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); } } else { - isToSproutZaddr = true; + throw JSONRPCError(RPC_INVALID_PARAMETER, "Only Sapling zaddrs allowed!"); } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); @@ -5311,14 +5297,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp } } - int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT; int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT; if (params.size() > 4) { int nNoteLimit = params[4].get_int(); if (nNoteLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); } - sproutNoteLimit = nNoteLimit; saplingNoteLimit = nNoteLimit; } @@ -5335,7 +5319,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp std::string memo; if (params.size() > 6) { memo = params[6].get_str(); - if (!(isToSproutZaddr || isToSaplingZaddr)) { + if (!isToSaplingZaddr) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); } else if (!IsHex(memo)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); @@ -5349,7 +5333,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp // Prepare to get UTXOs and notes std::vector utxoInputs; - std::vector sproutNoteInputs; std::vector saplingNoteInputs; CAmount mergedUTXOValue = 0; CAmount mergedNoteValue = 0; @@ -5363,9 +5346,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING; size_t estimatedTxSize = 200; // tx overhead + wiggle room - if (isToSproutZaddr) { - estimatedTxSize += JOINSPLIT_SIZE; - } else if (isToSaplingZaddr) { + + if (isToSaplingZaddr) { estimatedTxSize += OUTPUTDESCRIPTION_SIZE; } @@ -5423,57 +5405,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp } } - if (useAnySprout || useAnySapling || zaddrs.size() > 0) { + if (useAnySapling || zaddrs.size() > 0) { // Get available notes - std::vector sproutEntries; - //std::vector saplingEntries; - //pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs); - std::vector saplingEntries,skipsapling; - pwalletMain->GetFilteredNotes(sproutEntries, useAnySprout == 0 ? saplingEntries : skipsapling, zaddrs); - // If Sapling is not active, do not allow sending from a sapling addresses. - if (!saplingActive && saplingEntries.size() > 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); - } - // Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress - if (sproutEntries.size() > 0 && saplingEntries.size() > 0) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); - } - // If sending between shielded addresses, they must be the same type - if ((saplingEntries.size() > 0 && isToSproutZaddr) || (sproutEntries.size() > 0 && isToSaplingZaddr)) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Cannot send between Sprout and Sapling addresses using z_mergetoaddress"); - } - - // Find unspent notes and update estimated size - for (const CSproutNotePlaintextEntry& entry : sproutEntries) { - noteCounter++; - CAmount nValue = entry.plaintext.value(); - - if (!maxedOutNotesFlag) { - // If we haven't added any notes yet and the merge is to a - // z-address, we have already accounted for the first JoinSplit. - size_t increase = (sproutNoteInputs.empty() && !isToSproutZaddr) || (sproutNoteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; - if (estimatedTxSize + increase >= max_tx_size || - (sproutNoteLimit > 0 && noteCounter > sproutNoteLimit)) - { - maxedOutNotesFlag = true; - } else { - estimatedTxSize += increase; - auto zaddr = entry.address; - SproutSpendingKey zkey; - pwalletMain->GetSproutSpendingKey(zaddr, zkey); - sproutNoteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey); - mergedNoteValue += nValue; - } - } - - if (maxedOutNotesFlag) { - remainingNoteValue += nValue; - } - } + std::vector saplingEntries; + pwalletMain->GetFilteredNotes(saplingEntries, zaddrs); for (const SaplingNoteEntry& entry : saplingEntries) { noteCounter++; @@ -5502,7 +5437,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp } size_t numUtxos = utxoInputs.size(); - size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size(); + size_t numNotes = saplingNoteInputs.size(); //fprintf(stderr, "num utxos.%li\n", numUtxos); if (numUtxos < 2 && numNotes == 0) { @@ -5540,22 +5475,19 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( Params().GetConsensus(), nextBlockHeight); - bool isSproutShielded = sproutNoteInputs.size() > 0 || isToSproutZaddr; - if (contextualTx.nVersion == 1 && isSproutShielded) { - contextualTx.nVersion = 2; // Tx format should support vjoinsplit - } // Builder (used if Sapling addresses are involved) boost::optional builder; if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); - } else + } else { contextualTx.nExpiryHeight = 0; // set non z-tx to have no expiry height. + } // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr operation( - new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, sproutNoteInputs, saplingNoteInputs, recipient, nFee, contextInfo) ); + new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, saplingNoteInputs, recipient, nFee, contextInfo) ); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); @@ -8296,9 +8228,8 @@ extern UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPu extern UniValue z_importviewingkey(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue z_exportwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue z_importwallet(const UniValue& params, bool fHelp, const CPubKey& mypk); +extern UniValue rescan(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcdisclosure.cpp -extern UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp, const CPubKey& mypk); static const CRPCCommand commands[] = { // category name actor (function) okSafeMode @@ -8337,6 +8268,7 @@ static const CRPCCommand commands[] = { "wallet", "listunspent", &listunspent, false }, { "wallet", "lockunspent", &lockunspent, true }, { "wallet", "move", &movecmd, false }, + { "wallet", "rescan", &rescan, false }, { "wallet", "sendfrom", &sendfrom, false }, { "wallet", "sendmany", &sendmany, false }, { "wallet", "sendtoaddress", &sendtoaddress, false }, @@ -8368,9 +8300,7 @@ static const CRPCCommand commands[] = { "wallet", "z_getinfo", &z_getinfo, true }, { "wallet", "z_listsentbyaddress", &z_listsentbyaddress, true }, { "wallet", "z_listreceivedbyaddress", &z_listreceivedbyaddress, true }, - // TODO: rearrange into another category - { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, - { "disclosure", "z_validatepaymentdisclosure", &z_validatepaymentdisclosure, true } + { "wallet", "z_getnotescount", &z_getnotescount, false } }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 53b3af6a6..ae845cb21 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -114,26 +114,6 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } -// Generate a new spending key and return its public payment address -libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() -{ - AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata - - auto k = SproutSpendingKey::random(); - auto addr = k.address(); - - // Check for collision, even though it is unlikely to ever occur - if (CCryptoKeyStore::HaveSproutSpendingKey(addr)) - throw std::runtime_error("CWallet::GenerateNewSproutZKey(): Collision detected"); - - // Create new metadata - int64_t nCreationTime = GetTime(); - mapSproutZKeyMetadata[addr] = CKeyMetadata(nCreationTime); - - if (!AddSproutZKey(k)) - throw std::runtime_error("CWallet::GenerateNewSproutZKey(): AddSproutZKey failed"); - return addr; -} // Generate a new Sapling spending key and return its public payment address SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() @@ -231,30 +211,6 @@ bool CWallet::AddSaplingIncomingViewingKey( } -// Add spending key to keystore and persist to disk -bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key) -{ - AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata - auto addr = key.address(); - - if (!CCryptoKeyStore::AddSproutSpendingKey(key)) - return false; - - // check if we need to remove from viewing keys - if (HaveSproutViewingKey(addr)) - RemoveSproutViewingKey(key.viewing_key()); - - if (!fFileBacked) - return true; - - if (!IsCrypted()) { - return CWalletDB(strWalletFile).WriteZKey(addr, - key, - mapSproutZKeyMetadata[addr]); - } - return true; -} - CPubKey CWallet::GenerateNewKey() { AssertLockHeld(cs_wallet); // mapKeyMetadata @@ -325,33 +281,6 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, return false; } - -bool CWallet::AddCryptedSproutSpendingKey( - const libzcash::SproutPaymentAddress &address, - const libzcash::ReceivingKey &rk, - const std::vector &vchCryptedSecret) -{ - if (!CCryptoKeyStore::AddCryptedSproutSpendingKey(address, rk, vchCryptedSecret)) - return false; - if (!fFileBacked) - return true; - { - LOCK(cs_wallet); - if (pwalletdbEncryption) { - return pwalletdbEncryption->WriteCryptedZKey(address, - rk, - vchCryptedSecret, - mapSproutZKeyMetadata[address]); - } else { - return CWalletDB(strWalletFile).WriteCryptedZKey(address, - rk, - vchCryptedSecret, - mapSproutZKeyMetadata[address]); - } - } - return false; -} - bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector &vchCryptedSecret, const libzcash::SaplingPaymentAddress &defaultAddr) @@ -385,22 +314,11 @@ bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) return true; } -bool CWallet::LoadZKeyMetadata(const SproutPaymentAddress &addr, const CKeyMetadata &meta) -{ - AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata - mapSproutZKeyMetadata[addr] = meta; - return true; -} - bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } -bool CWallet::LoadCryptedZKey(const libzcash::SproutPaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret) -{ - return CCryptoKeyStore::AddCryptedSproutSpendingKey(addr, rk, vchCryptedSecret); -} bool CWallet::LoadCryptedSaplingZKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, @@ -428,43 +346,6 @@ bool CWallet::LoadSaplingPaymentAddress( return CCryptoKeyStore::AddSaplingIncomingViewingKey(ivk, addr); } -bool CWallet::LoadZKey(const libzcash::SproutSpendingKey &key) -{ - return CCryptoKeyStore::AddSproutSpendingKey(key); -} - -bool CWallet::AddSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - if (!CCryptoKeyStore::AddSproutViewingKey(vk)) { - return false; - } - nTimeFirstKey = 1; // No birthday information for viewing keys. - if (!fFileBacked) { - return true; - } - return CWalletDB(strWalletFile).WriteSproutViewingKey(vk); -} - -bool CWallet::RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - AssertLockHeld(cs_wallet); - if (!CCryptoKeyStore::RemoveSproutViewingKey(vk)) { - return false; - } - if (fFileBacked) { - if (!CWalletDB(strWalletFile).EraseSproutViewingKey(vk)) { - return false; - } - } - - return true; -} - -bool CWallet::LoadSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - return CCryptoKeyStore::AddSproutViewingKey(vk); -} - bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) @@ -681,29 +562,6 @@ std::set> CWallet::GetNullifiersFor return nullifierSet; } -bool CWallet::IsNoteSproutChange( - const std::set> & nullifierSet, - const PaymentAddress & address, - const JSOutPoint & jsop) -{ - // A Note is marked as "change" if the address that received it - // also spent Notes in the same transaction. This will catch, - // for instance: - // - Change created by spending fractions of Notes (because - // z_sendmany sends change to the originating z-address). - // - "Chaining Notes" used to connect JoinSplits together. - // - Notes created by consolidation transactions (e.g. using - // z_mergetoaddress). - // - Notes sent from one address to itself. - for (const JSDescription & jsd : mapWallet[jsop.hash].vjoinsplit) { - for (const uint256 & nullifier : jsd.nullifiers) { - if (nullifierSet.count(std::make_pair(address, nullifier))) { - return true; - } - } - } - return false; -} bool CWallet::IsNoteSaplingChange(const std::set> & nullifierSet, const libzcash::PaymentAddress & address, @@ -785,20 +643,6 @@ set CWallet::GetConflicts(const uint256& txid) const result.insert(it->second); } - std::pair range_n; - - for (const JSDescription& jsdesc : wtx.vjoinsplit) { - for (const uint256& nullifier : jsdesc.nullifiers) { - if (mapTxSproutNullifiers.count(nullifier) <= 1) { - continue; // No conflict if zero or one spends - } - range_n = mapTxSproutNullifiers.equal_range(nullifier); - for (TxNullifiers::const_iterator it = range_n.first; it != range_n.second; ++it) { - result.insert(it->second); - } - } - } - std::pair range_o; for (const SpendDescription &spend : wtx.vShieldedSpend) { @@ -941,38 +785,6 @@ unsigned int CWallet::GetSpendDepth(const uint256& hash, unsigned int n) const return 0; } -/** - * Note is spent if any non-conflicted transaction - * spends it: - */ -bool CWallet::IsSproutSpent(const uint256& nullifier) const { - pair range; - range = mapTxSproutNullifiers.equal_range(nullifier); - - for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) { - const uint256& wtxid = it->second; - std::map::const_iterator mit = mapWallet.find(wtxid); - if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) { - return true; // Spent - } - } - return false; -} - -unsigned int CWallet::GetSproutSpendDepth(const uint256& nullifier) const { - pair range; - range = mapTxSproutNullifiers.equal_range(nullifier); - - for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) { - const uint256& wtxid = it->second; - std::map::const_iterator mit = mapWallet.find(wtxid); - if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) { - return mit->second.GetDepthInMainChain(); // Spent - } - } - return 0; -} - bool CWallet::IsSaplingSpent(const uint256& nullifier) const { pair range; range = mapTxSaplingNullifiers.equal_range(nullifier); @@ -1010,15 +822,6 @@ void CWallet::AddToTransparentSpends(const COutPoint& outpoint, const uint256& w SyncMetaData(range); } -void CWallet::AddToSproutSpends(const uint256& nullifier, const uint256& wtxid) -{ - mapTxSproutNullifiers.insert(make_pair(nullifier, wtxid)); - - pair range; - range = mapTxSproutNullifiers.equal_range(nullifier); - SyncMetaData(range); -} - void CWallet::AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid) { mapTxSaplingNullifiers.insert(make_pair(nullifier, wtxid)); @@ -1038,11 +841,6 @@ void CWallet::AddToSpends(const uint256& wtxid) for (const CTxIn& txin : thisTx.vin) { AddToTransparentSpends(txin.prevout, wtxid); } - for (const JSDescription& jsdesc : thisTx.vjoinsplit) { - for (const uint256& nullifier : jsdesc.nullifiers) { - AddToSproutSpends(nullifier, wtxid); - } - } for (const SpendDescription &spend : thisTx.vShieldedSpend) { AddToSaplingSpends(spend.nullifier, wtxid); } @@ -1098,21 +896,6 @@ void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex) extern int32_t KOMODO_REWIND; for (std::pair& wtxItem : mapWallet) { - //Sprout - for (auto& item : wtxItem.second.mapSproutNoteData) { - auto* nd = &(item.second); - if (nd->nullifier && pwalletMain->GetSproutSpendDepth(*item.second.nullifier) <= WITNESS_CACHE_SIZE) { - // Only decrement witnesses that are not above the current height - if (nd->witnessHeight <= pindex->GetHeight()) { - if (nd->witnesses.size() > 1) { - // indexHeight is the height of the block being removed, so - // the new witness cache height is one below it. - nd->witnesses.pop_front(); - nd->witnessHeight = pindex->GetHeight() - 1; - } - } - } - } //Sapling for (auto& item : wtxItem.second.mapSaplingNoteData) { auto* nd = &(item.second); @@ -1140,14 +923,6 @@ void ClearSingleNoteWitnessCache(NoteData* nd) nd->witnessRootValidated = false; } -int CWallet::SproutWitnessMinimumHeight(const uint256& nullifier, int nWitnessHeight, int nMinimumHeight) -{ - if (GetSproutSpendDepth(nullifier) <= WITNESS_CACHE_SIZE) { - nMinimumHeight = min(nWitnessHeight, nMinimumHeight); - } - return nMinimumHeight; -} - int CWallet::SaplingWitnessMinimumHeight(const uint256& nullifier, int nWitnessHeight, int nMinimumHeight) { if (GetSaplingSpendDepth(nullifier) <= WITNESS_CACHE_SIZE) { @@ -1167,7 +942,7 @@ int CWallet::VerifyAndSetInitialWitness(const CBlockIndex* pindex, bool witnessO for (std::pair& wtxItem : mapWallet) { nWitnessTxIncrement += 1; - if (wtxItem.second.mapSproutNoteData.empty() && wtxItem.second.mapSaplingNoteData.empty()) + if (wtxItem.second.mapSaplingNoteData.empty()) continue; if (wtxItem.second.GetDepthInMainChain() > 0) { @@ -1288,10 +1063,6 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly) LogPrintf("Building Witnesses for block %i %.4f complete\n", pblockindex->GetHeight(), pblockindex->GetHeight() / double(height)); } - SproutMerkleTree sproutTree; - sproutRoot = pblockindex->pprev->hashFinalSproutRoot; - pcoinsTip->GetSproutAnchorAt(sproutRoot, sproutTree); - SaplingMerkleTree saplingTree; saplingRoot = pblockindex->pprev->hashFinalSaplingRoot; pcoinsTip->GetSaplingAnchorAt(saplingRoot, saplingTree); @@ -1302,7 +1073,7 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly) for (std::pair& wtxItem : mapWallet) { - if (wtxItem.second.mapSproutNoteData.empty() && wtxItem.second.mapSaplingNoteData.empty()) + if (wtxItem.second.mapSaplingNoteData.empty()) continue; if (wtxItem.second.GetDepthInMainChain() > 0) { @@ -1492,22 +1263,6 @@ bool CWallet::UpdateNullifierNoteMap() ZCNoteDecryption dec; for (std::pair& wtxItem : mapWallet) { - for (mapSproutNoteData_t::value_type& item : wtxItem.second.mapSproutNoteData) { - if (!item.second.nullifier) { - if (GetNoteDecryptor(item.second.address, dec)) { - auto i = item.first.js; - auto hSig = wtxItem.second.vjoinsplit[i].h_sig( - *pzcashParams, wtxItem.second.joinSplitPubKey); - item.second.nullifier = GetSproutNoteNullifier( - wtxItem.second.vjoinsplit[i], - item.second.address, - dec, - hSig, - item.first.n); - } - } - } - // TODO: Sapling. This method is only called from RPC walletpassphrase, which is currently unsupported // as RPC encryptwallet is hidden behind two flags: -developerencryptwallet -experimentalfeatures @@ -1518,7 +1273,7 @@ bool CWallet::UpdateNullifierNoteMap() } /** - * Update mapSproutNullifiersToNotes and mapSaplingNullifiersToNotes + * Update mapSaplingNullifiersToNotes * with the cached nullifiers in this tx. */ void CWallet::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx) @@ -1534,48 +1289,6 @@ void CWallet::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx) } } -/** - * Update mapSproutNullifiersToNotes, computing the nullifier from a cached witness if necessary. - */ -void CWallet::UpdateSproutNullifierNoteMapWithTx(CWalletTx& wtx) { - LOCK(cs_wallet); - - ZCNoteDecryption dec; - for (mapSproutNoteData_t::value_type& item : wtx.mapSproutNoteData) { - SproutNoteData nd = item.second; - - if (nd.witnesses.empty()) { - // If there are no witnesses, erase the nullifier and associated mapping. - if (nd.nullifier) { - mapSproutNullifiersToNotes.erase(nd.nullifier.get()); - } - nd.nullifier = boost::none; - } - else { - if (GetNoteDecryptor(nd.address, dec)) { - auto i = item.first.js; - auto hSig = wtx.vjoinsplit[i].h_sig( - *pzcashParams, wtx.joinSplitPubKey); - auto optNullifier = GetSproutNoteNullifier( - wtx.vjoinsplit[i], - item.second.address, - dec, - hSig, - item.first.n); - - if (!optNullifier) { - // This should not happen. If it does, maybe the position has been corrupted or miscalculated? - assert(false); - } - - uint256 nullifier = optNullifier.get(); - mapSproutNullifiersToNotes[nullifier] = item.first; - item.second.nullifier = nullifier; - } - } - } -} - /** * Update mapSaplingNullifiersToNotes, computing the nullifier from a cached witness if necessary. */ @@ -1633,7 +1346,6 @@ void CWallet::UpdateNullifierNoteMapForBlock(const CBlock *pblock) { auto hash = tx.GetHash(); bool txIsOurs = mapWallet.count(hash); if (txIsOurs) { - UpdateSproutNullifierNoteMapWithTx(mapWallet[hash]); UpdateSaplingNullifierNoteMapWithTx(mapWallet[hash]); } } @@ -1673,6 +1385,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD int64_t latestEntry = 0; { // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future + // TODO: this is 2 blocktimes, which will become 150? int64_t latestTolerated = latestNow + 300; std::list acentries; TxItems txOrdered = OrderedTxItems(acentries); @@ -1766,21 +1479,6 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) { - bool unchangedSproutFlag = (wtxIn.mapSproutNoteData.empty() || wtxIn.mapSproutNoteData == wtx.mapSproutNoteData); - if (!unchangedSproutFlag) { - auto tmp = wtxIn.mapSproutNoteData; - // Ensure we keep any cached witnesses we may already have - for (const std::pair nd : wtx.mapSproutNoteData) { - if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) { - tmp.at(nd.first).witnesses.assign( - nd.second.witnesses.cbegin(), nd.second.witnesses.cend()); - } - tmp.at(nd.first).witnessHeight = nd.second.witnessHeight; - } - // Now copy over the updated note data - wtx.mapSproutNoteData = tmp; - } - bool unchangedSaplingFlag = (wtxIn.mapSaplingNoteData.empty() || wtxIn.mapSaplingNoteData == wtx.mapSaplingNoteData); if (!unchangedSaplingFlag) { auto tmp = wtxIn.mapSaplingNoteData; @@ -1798,7 +1496,7 @@ bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) wtx.mapSaplingNoteData = tmp; } - return !unchangedSproutFlag || !unchangedSaplingFlag; + return !unchangedSaplingFlag; } /** @@ -1815,7 +1513,6 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl return false; bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; - auto sproutNoteData = FindMySproutNotes(tx); auto saplingNoteDataAndAddressesToAdd = FindMySaplingNotes(tx); auto saplingNoteData = saplingNoteDataAndAddressesToAdd.first; auto addressesToAdd = saplingNoteDataAndAddressesToAdd.second; @@ -1839,7 +1536,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl fprintf(stderr, " %s\n", wladdr.c_str()); } } - if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) + if (fExisted || IsMine(tx) || IsFromMe(tx) || saplingNoteData.size() > 0) { // wallet filter for notary nodes. Enables by setting -whitelistaddress= as startup param or in conf file (works same as -addnode byut with R-address's) if ( !tx.IsCoinBase() && !vWhiteListAddress.empty() && !NotaryAddress.empty() ) @@ -1871,10 +1568,6 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl CWalletTx wtx(this,tx); - if (sproutNoteData.size() > 0) { - wtx.SetSproutNoteData(sproutNoteData); - } - if (saplingNoteData.size() > 0) { wtx.SetSaplingNoteData(saplingNoteData); } @@ -1946,82 +1639,6 @@ void CWallet::RescanWallet() } -/** - * Returns a nullifier if the SpendingKey is available - * Throws std::runtime_error if the decryptor doesn't match this note - */ -boost::optional CWallet::GetSproutNoteNullifier(const JSDescription &jsdesc, - const libzcash::SproutPaymentAddress &address, - const ZCNoteDecryption &dec, - const uint256 &hSig, - uint8_t n) const -{ - boost::optional ret; - auto note_pt = libzcash::SproutNotePlaintext::decrypt( - dec, - jsdesc.ciphertexts[n], - jsdesc.ephemeralKey, - hSig, - (unsigned char) n); - auto note = note_pt.note(address); - // SpendingKeys are only available if: - // - We have them (this isn't a viewing key) - // - The wallet is unlocked - libzcash::SproutSpendingKey key; - if (GetSproutSpendingKey(address, key)) { - ret = note.nullifier(key); - } - return ret; -} - -/** - * Finds all output notes in the given transaction that have been sent to - * PaymentAddresses in this wallet. - * - * It should never be necessary to call this method with a CWalletTx, because - * the result of FindMySproutNotes (for the addresses available at the time) will - * already have been cached in CWalletTx.mapSproutNoteData. - */ -mapSproutNoteData_t CWallet::FindMySproutNotes(const CTransaction &tx) const -{ - LOCK(cs_SpendingKeyStore); - uint256 hash = tx.GetHash(); - - mapSproutNoteData_t noteData; - for (size_t i = 0; i < tx.vjoinsplit.size(); i++) { - auto hSig = tx.vjoinsplit[i].h_sig(*pzcashParams, tx.joinSplitPubKey); - for (uint8_t j = 0; j < tx.vjoinsplit[i].ciphertexts.size(); j++) { - for (const NoteDecryptorMap::value_type& item : mapNoteDecryptors) { - try { - auto address = item.first; - JSOutPoint jsoutpt {hash, i, j}; - auto nullifier = GetSproutNoteNullifier( - tx.vjoinsplit[i], - address, - item.second, - hSig, j); - if (nullifier) { - SproutNoteData nd {address, *nullifier}; - noteData.insert(std::make_pair(jsoutpt, nd)); - } else { - SproutNoteData nd {address}; - noteData.insert(std::make_pair(jsoutpt, nd)); - } - break; - } catch (const note_decryption_failed &err) { - // Couldn't decrypt with this decryptor - } catch (const std::exception &exc) { - // Unexpected failure - LogPrintf("FindMySproutNotes(): Unexpected error while testing decrypt:\n"); - LogPrintf("%s\n", exc.what()); - } - } - } - } - return noteData; -} - - /** * Finds all output notes in the given transaction that have been sent to * SaplingPaymentAddresses in this wallet. @@ -2081,18 +1698,6 @@ std::pair CWallet::FindMySap return std::make_pair(noteData, viewingKeysToAdd); } -bool CWallet::IsSproutNullifierFromMe(const uint256& nullifier) const -{ - { - LOCK(cs_wallet); - if (mapSproutNullifiersToNotes.count(nullifier) && - mapWallet.count(mapSproutNullifiersToNotes.at(nullifier).hash)) { - return true; - } - } - return false; -} - bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const { { @@ -2105,33 +1710,6 @@ bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const return false; } -void CWallet::GetSproutNoteWitnesses(std::vector notes, - std::vector>& witnesses, - uint256 &final_anchor) -{ - LOCK(cs_wallet); - witnesses.resize(notes.size()); - boost::optional rt; - int i = 0; - for (JSOutPoint note : notes) { - if (mapWallet.count(note.hash) && - mapWallet[note.hash].mapSproutNoteData.count(note) && - mapWallet[note.hash].mapSproutNoteData[note].witnesses.size() > 0) { - witnesses[i] = mapWallet[note.hash].mapSproutNoteData[note].witnesses.front(); - if (!rt) { - rt = witnesses[i]->root(); - } else { - assert(*rt == witnesses[i]->root()); - } - } - i++; - } - // All returned witnesses have the same anchor - if (rt) { - final_anchor = *rt; - } -} - void CWallet::GetSaplingNoteWitnesses(std::vector notes, std::vector>& witnesses, uint256 &final_anchor) @@ -2141,14 +1719,26 @@ void CWallet::GetSaplingNoteWitnesses(std::vector notes, boost::optional rt; int i = 0; for (SaplingOutPoint note : notes) { - if (mapWallet.count(note.hash) && - mapWallet[note.hash].mapSaplingNoteData.count(note) && - mapWallet[note.hash].mapSaplingNoteData[note].witnesses.size() > 0) { - witnesses[i] = mapWallet[note.hash].mapSaplingNoteData[note].witnesses.front(); + //fprintf(stderr,"%s: i=%d\n", __func__,i); + auto noteData = mapWallet[note.hash].mapSaplingNoteData; + auto nWitnesses = noteData[note].witnesses.size(); + if (mapWallet.count(note.hash) && noteData.count(note) && nWitnesses > 0) { + fprintf(stderr,"%s: Found %lu witnesses for note %s\n", __func__, nWitnesses, note.hash.ToString().c_str() ); + witnesses[i] = noteData[note].witnesses.front(); if (!rt) { + //fprintf(stderr,"%s: Setting witness root\n",__func__); rt = witnesses[i]->root(); } else { - assert(*rt == witnesses[i]->root()); + if(*rt == witnesses[i]->root()) { + //fprintf(stderr,"%s: rt=%s\n",__func__,rt.GetHash().ToString().c_str()); + //fprintf(stderr,"%s: witnesses[%d]->root()=%s\n",__func__,i,witnesses[i]->root().GetHash().ToString().c_str()); + // Something is fucky + std::string err = string("CWallet::GetSaplingNoteWitnesses: Invalid witness root! rt=") + rt.get().ToString(); + err += string("\n!= witness[i]->root()=") + witnesses[i]->root().ToString(); + //throw std::logic_error(err); + fprintf(stderr,"%s: IGNORING %s\n", __func__,err.c_str()); + } + } } i++; @@ -2156,6 +1746,7 @@ void CWallet::GetSaplingNoteWitnesses(std::vector notes, // All returned witnesses have the same anchor if (rt) { final_anchor = *rt; + //fprintf(stderr,"%s: final_anchor=%s\n", __func__, rt.get().ToString().c_str() ); } } @@ -2364,13 +1955,6 @@ bool CWallet::IsFromMe(const CTransaction& tx) const if (GetDebit(tx, ISMINE_ALL) > 0) { return true; } - for (const JSDescription& jsdesc : tx.vjoinsplit) { - for (const uint256& nullifier : jsdesc.nullifiers) { - if (IsSproutNullifierFromMe(nullifier)) { - return true; - } - } - } for (const SpendDescription &spend : tx.vShieldedSpend) { if (IsSaplingNullifierFromMe(spend.nullifier)) { return true; @@ -2506,22 +2090,6 @@ bool CWallet::LoadCryptedHDSeed(const uint256& seedFp, const std::vector nd : noteData) { - if (nd.first.js < vjoinsplit.size() && - nd.first.n < vjoinsplit[nd.first.js].ciphertexts.size()) { - // Store the address and nullifier for the Note - mapSproutNoteData[nd.first] = nd.second; - } else { - // If FindMySproutNotes() was used to obtain noteData, - // this should never happen - throw std::logic_error("CWalletTx::SetSproutNoteData(): Invalid note"); - } - } -} - void CWalletTx::SetSaplingNoteData(mapSaplingNoteData_t ¬eData) { mapSaplingNoteData.clear(); @@ -2665,36 +2233,6 @@ void CWalletTx::GetAmounts(list& listReceived, if (isFromMyTaddr) { CAmount myVpubOld = 0; CAmount myVpubNew = 0; - for (const JSDescription& js : vjoinsplit) { - bool fMyJSDesc = false; - - // Check input side - for (const uint256& nullifier : js.nullifiers) { - if (pwallet->IsSproutNullifierFromMe(nullifier)) { - fMyJSDesc = true; - break; - } - } - - // Check output side - if (!fMyJSDesc) { - for (const std::pair nd : this->mapSproutNoteData) { - if (nd.first.js < vjoinsplit.size() && nd.first.n < vjoinsplit[nd.first.js].ciphertexts.size()) { - fMyJSDesc = true; - break; - } - } - } - - if (fMyJSDesc) { - myVpubOld += js.vpub_old; - myVpubNew += js.vpub_new; - } - - if (!MoneyRange(js.vpub_old) || !MoneyRange(js.vpub_new) || !MoneyRange(myVpubOld) || !MoneyRange(myVpubNew)) { - throw std::runtime_error("CWalletTx::GetAmounts: value out of range"); - } - } // Create an output for the value taken from or added to the transparent value pool by JoinSplits if (myVpubOld > myVpubNew) { @@ -2812,57 +2350,6 @@ void CWallet::WitnessNoteCommitment(std::vector commitments, std::vector>& witnesses, uint256 &final_anchor) { - witnesses.resize(commitments.size()); - CBlockIndex* pindex = chainActive.Genesis(); - SproutMerkleTree tree; - - while (pindex) { - CBlock block; - ReadBlockFromDisk(block, pindex,1); - - BOOST_FOREACH(const CTransaction& tx, block.vtx) - { - BOOST_FOREACH(const JSDescription& jsdesc, tx.vjoinsplit) - { - BOOST_FOREACH(const uint256 ¬e_commitment, jsdesc.commitments) - { - tree.append(note_commitment); - - BOOST_FOREACH(boost::optional& wit, witnesses) { - if (wit) { - wit->append(note_commitment); - } - } - - size_t i = 0; - BOOST_FOREACH(uint256& commitment, commitments) { - if (note_commitment == commitment) { - witnesses.at(i) = tree.witness(); - } - i++; - } - } - } - } - - uint256 current_anchor = tree.root(); - - // Consistency check: we should be able to find the current tree - // in our CCoins view. - SproutMerkleTree dummy_tree; - assert(pcoinsTip->GetSproutAnchorAt(current_anchor, dummy_tree)); - - pindex = chainActive.Next(pindex); - } - - // TODO: #93; Select a root via some heuristic. - final_anchor = tree.root(); - - BOOST_FOREACH(boost::optional& wit, witnesses) { - if (wit) { - assert(final_anchor == wit->root()); - } - } } /** @@ -3066,38 +2553,11 @@ void CWallet::DeleteWalletTransactions(const CBlockIndex* pindex) { continue; } - //Check for unspent inputs or spend less than N Blocks ago. (Sprout) - for (auto & pair : pwtx->mapSproutNoteData) { - SproutNoteData nd = pair.second; - if (!nd.nullifier || pwalletMain->GetSproutSpendDepth(*nd.nullifier) <= fDeleteTransactionsAfterNBlocks) { - LogPrint("deletetx","DeleteTx - Unspent sprout input tx %s\n", pwtx->GetHash().ToString()); - deleteTx = false; - continue; - } - } - if (!deleteTx) { txSaveCount++; continue; } - //Check for outputs that no longer have parents in the wallet. Exclude parents that are in the same transaction. (Sprout) - for (int i = 0; i < pwtx->vjoinsplit.size(); i++) { - const JSDescription& jsdesc = pwtx->vjoinsplit[i]; - for (const uint256 &nullifier : jsdesc.nullifiers) { - // JSOutPoint op = pwalletMain->mapSproutNullifiersToNotes[nullifier]; - if (pwalletMain->IsSproutNullifierFromMe(nullifier)) { - const uint256& parentHash = pwalletMain->mapSproutNullifiersToNotes[nullifier].hash; - const CWalletTx* parent = pwalletMain->GetWalletTx(parentHash); - if (parent != NULL && parentHash != wtxid) { - LogPrint("deletetx","DeleteTx - Parent of sprout tx %s found\n", pwtx->GetHash().ToString()); - deleteTx = false; - continue; - } - } - } - } - if (!deleteTx) { txSaveCount++; continue; @@ -3202,11 +2662,11 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) } } - SproutMerkleTree sproutTree; + //SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; // This should never fail: we should always be able to get the tree // state on the path to the tip of our chain - assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, sproutTree)); + //assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, sproutTree)); if (pindex->pprev) { if (NetworkUpgradeActive(pindex->pprev->GetHeight(), Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { assert(pcoinsTip->GetSaplingAnchorAt(pindex->pprev->hashFinalSaplingRoot, saplingTree)); @@ -5021,38 +4481,6 @@ void CWallet::ListLockedCoins(std::vector& vOutpts) // Note Locking Operations -void CWallet::LockNote(const JSOutPoint& output) -{ - AssertLockHeld(cs_wallet); // setLockedSproutNotes - setLockedSproutNotes.insert(output); -} - -void CWallet::UnlockNote(const JSOutPoint& output) -{ - AssertLockHeld(cs_wallet); // setLockedSproutNotes - setLockedSproutNotes.erase(output); -} - -void CWallet::UnlockAllSproutNotes() -{ - AssertLockHeld(cs_wallet); // setLockedSproutNotes - setLockedSproutNotes.clear(); -} - -bool CWallet::IsLockedNote(const JSOutPoint& outpt) const -{ - AssertLockHeld(cs_wallet); // setLockedSproutNotes - - return (setLockedSproutNotes.count(outpt) > 0); -} - -std::vector CWallet::ListLockedSproutNotes() -{ - AssertLockHeld(cs_wallet); // setLockedSproutNotes - std::vector vOutpts(setLockedSproutNotes.begin(), setLockedSproutNotes.end()); - return vOutpts; -} - void CWallet::LockNote(const SaplingOutPoint& output) { AssertLockHeld(cs_wallet); @@ -5318,7 +4746,6 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) * These notes are decrypted and added to the output parameter vector, outEntries. */ void CWallet::GetFilteredNotes( - std::vector& sproutEntries, std::vector& saplingEntries, std::string address, int minDepth, @@ -5331,7 +4758,7 @@ void CWallet::GetFilteredNotes( filterAddresses.insert(DecodePaymentAddress(address)); } - GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey); + GetFilteredNotes(saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey); } /** @@ -5340,7 +4767,6 @@ void CWallet::GetFilteredNotes( * These notes are decrypted and added to the output parameter vector, outEntries. */ void CWallet::GetFilteredNotes( - std::vector& sproutEntries, std::vector& saplingEntries, std::set& filterAddresses, int minDepth, @@ -5429,11 +4855,6 @@ void CWallet::GetFilteredNotes( // Shielded key and address generalizations // -bool IncomingViewingKeyBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const -{ - return m_wallet->HaveSproutViewingKey(zaddr); -} - bool IncomingViewingKeyBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingIncomingViewingKey ivk; @@ -5445,11 +4866,6 @@ bool IncomingViewingKeyBelongsToWallet::operator()(const libzcash::InvalidEncodi return false; } -bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const -{ - return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr); -} - bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingIncomingViewingKey ivk; @@ -5465,11 +4881,6 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& return false; } -bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const -{ - return m_wallet->HaveSproutSpendingKey(zaddr); -} - bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingIncomingViewingKey ivk; @@ -5485,17 +4896,6 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::InvalidEncodin return false; } -boost::optional GetSpendingKeyForPaymentAddress::operator()( - const libzcash::SproutPaymentAddress &zaddr) const -{ - libzcash::SproutSpendingKey k; - if (m_wallet->GetSproutSpendingKey(zaddr, k)) { - return libzcash::SpendingKey(k); - } else { - return boost::none; - } -} - boost::optional GetSpendingKeyForPaymentAddress::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { @@ -5514,20 +4914,6 @@ boost::optional GetSpendingKeyForPaymentAddress::operator return libzcash::SpendingKey(); } -SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { - auto addr = sk.address(); - if (log){ - LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr)); - } - if (m_wallet->HaveSproutSpendingKey(addr)) { - return KeyAlreadyExists; - } else if (m_wallet-> AddSproutZKey(sk)) { - m_wallet->mapSproutZKeyMetadata[addr].nCreateTime = nTime; - return KeyAdded; - } else { - return KeyNotAdded; - } -} SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const { auto fvk = sk.expsk.full_viewing_key(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 205a49008..f6e6274d1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -236,9 +236,10 @@ public: std::string ToString() const; }; +// NOTE: wallet.dat format depends on this data structure :( class SproutNoteData -{ -public: + { + public: libzcash::SproutPaymentAddress address; /** @@ -264,7 +265,7 @@ public: /** * Block height corresponding to the most current witness. * - * When we first create a SproutNoteData in CWallet::FindMySproutNotes, this is set to + * When we first create a SaplingNoteData in CWallet::FindMySaplingNotes, this is set to * -1 as a placeholder. The next time CWallet::ChainTip is called, we can * determine what height the witness cache for this note is valid for (even * if no witnesses were cached), and so can set the correct value in @@ -272,15 +273,6 @@ public: */ int witnessHeight; - //In Memory Only - bool witnessRootValidated; - - SproutNoteData() : address(), nullifier(), witnessHeight {-1}, witnessRootValidated {false} { } - SproutNoteData(libzcash::SproutPaymentAddress a) : - address {a}, nullifier(), witnessHeight {-1}, witnessRootValidated {false} { } - SproutNoteData(libzcash::SproutPaymentAddress a, uint256 n) : - address {a}, nullifier {n}, witnessHeight {-1}, witnessRootValidated {false} { } - ADD_SERIALIZE_METHODS; template @@ -291,27 +283,22 @@ public: READWRITE(witnessHeight); } - friend bool operator<(const SproutNoteData& a, const SproutNoteData& b) { - return (a.address < b.address || - (a.address == b.address && a.nullifier < b.nullifier)); - } - - friend bool operator==(const SproutNoteData& a, const SproutNoteData& b) { - return (a.address == b.address && a.nullifier == b.nullifier); - } - - friend bool operator!=(const SproutNoteData& a, const SproutNoteData& b) { - return !(a == b); - } }; + class SaplingNoteData { public: /** - * We initialize the height to -1 for the same reason as we do in SproutNoteData. - * See the comment in that class for a full description. + * Block height corresponding to the most current witness. + * + * When we first create a SaplingNoteData in CWallet::FindMySaplingNotes, this is set to + * -1 as a placeholder. The next time CWallet::ChainTip is called, we can + * determine what height the witness cache for this note is valid for (even + * if no witnesses were cached), and so can set the correct value in + * CWallet::BuildWitnessCache and CWallet::DecrementNoteWitnesses. */ + SaplingNoteData() : witnessHeight {-1}, nullifier() { } SaplingNoteData(libzcash::SaplingIncomingViewingKey ivk) : ivk {ivk}, witnessHeight {-1}, nullifier() { } SaplingNoteData(libzcash::SaplingIncomingViewingKey ivk, uint256 n) : ivk {ivk}, witnessHeight {-1}, nullifier(n) { } @@ -347,18 +334,11 @@ public: } }; + +// NOTE: this sprout structure is serialized into wallet.dat, removing it would change wallet.dat format on disk :( typedef std::map mapSproutNoteData_t; typedef std::map mapSaplingNoteData_t; -/** Decrypted note, its location in a transaction, and number of confirmations. */ -struct CSproutNotePlaintextEntry -{ - JSOutPoint jsop; - libzcash::SproutPaymentAddress address; - libzcash::SproutNotePlaintext plaintext; - int confirmations; -}; - /** Sapling note, its location in a transaction, and number of confirmations. */ struct SaplingNoteEntry { @@ -589,11 +569,8 @@ public: MarkDirty(); } - void SetSproutNoteData(mapSproutNoteData_t ¬eData); void SetSaplingNoteData(mapSaplingNoteData_t ¬eData); - std::pair DecryptSproutNote( - JSOutPoint jsop) const; boost::optional> DecryptSaplingNote(SaplingOutPoint op) const; @@ -801,14 +778,12 @@ private: * detect and report conflicts (double-spends). */ typedef TxSpendMap TxNullifiers; - TxNullifiers mapTxSproutNullifiers; TxNullifiers mapTxSaplingNullifiers; std::vector pendingSaplingConsolidationTxs; AsyncRPCOperationId saplingConsolidationOperationId; void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid); - void AddToSproutSpends(const uint256& nullifier, const uint256& wtxid); void AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid); void AddToSpends(const uint256& wtxid); @@ -829,7 +804,6 @@ public: protected: - int SproutWitnessMinimumHeight(const uint256& nullifier, int nWitnessHeight, int nMinimumHeight); int SaplingWitnessMinimumHeight(const uint256& nullifier, int nWitnessHeight, int nMinimumHeight); /** @@ -853,11 +827,11 @@ protected: try { for (std::pair& wtxItem : mapWallet) { auto wtx = wtxItem.second; - // We skip transactions for which mapSproutNoteData and mapSaplingNoteData - // are empty. This covers transactions that have no Sprout or Sapling data + // We skip transactions for which mapSaplingNoteData + // is empty. This covers transactions that have no Sapling data // (i.e. are purely transparent), as well as shielding and unshielding // transactions in which we only have transparent addresses involved. - if (!(wtx.mapSproutNoteData.empty() && wtx.mapSaplingNoteData.empty())) { + if (!(wtx.mapSaplingNoteData.empty())) { if (!walletdb.WriteTx(wtxItem.first, wtx)) { LogPrintf("SetBestChain(): Failed to write CWalletTx, aborting atomic write\n"); walletdb.TxnAbort(); @@ -915,7 +889,6 @@ public: std::set setKeyPool; std::map mapKeyMetadata; - std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; typedef std::map MasterKeyMap; @@ -1033,8 +1006,6 @@ public: bool IsSpent(const uint256& hash, unsigned int n) const; unsigned int GetSpendDepth(const uint256& hash, unsigned int n) const; - bool IsSproutSpent(const uint256& nullifier) const; - unsigned int GetSproutSpendDepth(const uint256& nullifier) const; bool IsSaplingSpent(const uint256& nullifier) const; unsigned int GetSaplingSpendDepth(const uint256& nullifier) const; @@ -1047,8 +1018,6 @@ public: bool IsLockedNote(const JSOutPoint& outpt) const; void LockNote(const JSOutPoint& output); void UnlockNote(const JSOutPoint& output); - void UnlockAllSproutNotes(); - std::vector ListLockedSproutNotes(); bool IsLockedNote(const SaplingOutPoint& output) const; void LockNote(const SaplingOutPoint& output); @@ -1098,31 +1067,6 @@ public: void GetKeyBirthTimes(std::map &mapKeyBirth) const; - /** - * Sprout ZKeys - */ - //! Generates a new Sprout zaddr - libzcash::SproutPaymentAddress GenerateNewSproutZKey(); - //! Adds spending key to the store, and saves it to disk - bool AddSproutZKey(const libzcash::SproutSpendingKey &key); - //! Adds spending key to the store, without saving it to disk (used by LoadWallet) - bool LoadZKey(const libzcash::SproutSpendingKey &key); - //! Load spending key metadata (used by LoadWallet) - bool LoadZKeyMetadata(const libzcash::SproutPaymentAddress &addr, const CKeyMetadata &meta); - //! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet) - bool LoadCryptedZKey(const libzcash::SproutPaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret); - //! Adds an encrypted spending key to the store, and saves it to disk (virtual method, declared in crypter.h) - bool AddCryptedSproutSpendingKey( - const libzcash::SproutPaymentAddress &address, - const libzcash::ReceivingKey &rk, - const std::vector &vchCryptedSecret); - - //! Adds a Sprout viewing key to the store, and saves it to disk. - bool AddSproutViewingKey(const libzcash::SproutViewingKey &vk); - bool RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk); - //! Adds a Sprout viewing key to the store, without saving it to disk (used by LoadWallet) - bool LoadSproutViewingKey(const libzcash::SproutViewingKey &dest); - /** * Sapling ZKeys */ @@ -1171,7 +1115,6 @@ public: void MarkDirty(); bool UpdateNullifierNoteMap(); void UpdateNullifierNoteMapWithTx(const CWalletTx& wtx); - void UpdateSproutNullifierNoteMapWithTx(CWalletTx& wtx); void UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx); void UpdateNullifierNoteMapForBlock(const CBlock* pblock); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); @@ -1219,21 +1162,9 @@ public: std::set GetAccountAddresses(const std::string& strAccount) const; - boost::optional GetSproutNoteNullifier( - const JSDescription& jsdesc, - const libzcash::SproutPaymentAddress& address, - const ZCNoteDecryption& dec, - const uint256& hSig, - uint8_t n) const; - mapSproutNoteData_t FindMySproutNotes(const CTransaction& tx) const; std::pair FindMySaplingNotes(const CTransaction& tx) const; - bool IsSproutNullifierFromMe(const uint256& nullifier) const; bool IsSaplingNullifierFromMe(const uint256& nullifier) const; - void GetSproutNoteWitnesses( - std::vector notes, - std::vector>& witnesses, - uint256 &final_anchor); void GetSaplingNoteWitnesses( std::vector notes, std::vector>& witnesses, @@ -1367,8 +1298,7 @@ public: bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); /* Find notes filtered by payment address, min depth, ability to spend */ - void GetFilteredNotes(std::vector& sproutEntries, - std::vector& saplingEntries, + void GetFilteredNotes(std::vector& saplingEntries, std::string address, int minDepth=1, bool ignoreSpent=true, @@ -1376,8 +1306,7 @@ public: /* Find notes filtered by payment addresses, min depth, max depth, if they are spent, if a spending key is required, and if they are locked */ - void GetFilteredNotes(std::vector& sproutEntries, - std::vector& saplingEntries, + void GetFilteredNotes(std::vector& saplingEntries, std::set& filterAddresses, int minDepth=1, int maxDepth=INT_MAX, @@ -1456,7 +1385,6 @@ private: public: PaymentAddressBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {} - bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::InvalidEncoding& no) const; }; @@ -1469,7 +1397,6 @@ private: public: IncomingViewingKeyBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {} - bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::InvalidEncoding& no) const; }; @@ -1481,7 +1408,6 @@ private: public: HaveSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} - bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::InvalidEncoding& no) const; }; @@ -1493,7 +1419,6 @@ private: public: GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} - boost::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; boost::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; boost::optional operator()(const libzcash::InvalidEncoding& no) const; }; @@ -1565,7 +1490,6 @@ public: ) : m_wallet(wallet), params(params), nTime(_nTime), hdKeypath(_hdKeypath), seedFpStr(_seedFp), log(_log) {} - SpendingKeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const; SpendingKeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const; SpendingKeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 952a76735..71331ed93 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2019 The Hush developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -124,26 +124,6 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, return true; } -bool CWalletDB::WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, - const libzcash::ReceivingKey &rk, - const std::vector& vchCryptedSecret, - const CKeyMetadata &keyMeta) -{ - const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; - - if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta)) - return false; - - if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(rk, vchCryptedSecret), false)) - return false; - if (fEraseUnencryptedKey) - { - Erase(std::make_pair(std::string("zkey"), addr)); - } - return true; -} - bool CWalletDB::WriteCryptedSaplingZKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector& vchCryptedSecret, @@ -172,16 +152,6 @@ bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } -bool CWalletDB::WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta) -{ - nWalletDBUpdated++; - - if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta)) - return false; - - // pair is: tuple_key("zkey", paymentaddress) --> secretkey - return Write(std::make_pair(std::string("zkey"), addr), key, false); -} bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingExtendedSpendingKey &key, const CKeyMetadata &keyMeta) @@ -203,18 +173,6 @@ bool CWalletDB::WriteSaplingPaymentAddress( return Write(std::make_pair(std::string("sapzaddr"), addr), ivk, false); } -bool CWalletDB::WriteSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("vkey"), vk), '1'); -} - -bool CWalletDB::EraseSproutViewingKey(const libzcash::SproutViewingKey &vk) -{ - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("vkey"), vk)); -} - bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdated++; @@ -552,6 +510,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "vkey") { + /* libzcash::SproutViewingKey vk; ssKey >> vk; char fYes; @@ -559,24 +518,27 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, if (fYes == '1') pwallet->LoadSproutViewingKey(vk); - // Viewing keys have no birthday information for now, - // so set the wallet birthday to the beginning of time. + Viewing keys have no birthday information for now, + so set the wallet birthday to the beginning of time. pwallet->nTimeFirstKey = 1; + */ } else if (strType == "zkey") { + /* libzcash::SproutPaymentAddress addr; ssKey >> addr; libzcash::SproutSpendingKey key; ssValue >> key; - if (!pwallet->LoadZKey(key)) - { - strErr = "Error reading wallet database: LoadZKey failed"; - return false; - } + //if (!pwallet->LoadZKey(key)) + //{ + // strErr = "Error reading wallet database: LoadZKey failed"; + // return false; + //} wss.nZKeys++; + */ } else if (strType == "sapzkey") { @@ -691,6 +653,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "czkey") { + /* libzcash::SproutPaymentAddress addr; ssKey >> addr; // Deserialization of a pair is just one item after another @@ -707,6 +670,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return false; } wss.fIsEncrypted = true; + */ } else if (strType == "csapzkey") { @@ -742,13 +706,15 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "zkeymeta") { + /* libzcash::SproutPaymentAddress addr; ssKey >> addr; CKeyMetadata keyMeta; ssValue >> keyMeta; wss.nZKeyMeta++; + */ - pwallet->LoadZKeyMetadata(addr, keyMeta); + // pwallet->LoadZKeyMetadata(addr, keyMeta); // ignore earliest key creation time as taddr will exist before any zaddr } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 9069e3a08..64a4817d9 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -1,7 +1,8 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2013 The Bitcoin Core developers +// Copyright (c) 2009-2013 The Hush developers // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://www.opensource.org/licenses/mit-license.php /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * @@ -199,23 +200,15 @@ public: bool WriteHDChain(const CHDChain& chain); /// Write spending key to wallet database, where key is payment address and value is spending key. - bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta); bool WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingExtendedSpendingKey &key, const CKeyMetadata &keyMeta); bool WriteSaplingPaymentAddress(const libzcash::SaplingPaymentAddress &addr, const libzcash::SaplingIncomingViewingKey &ivk); - bool WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, - const libzcash::ReceivingKey & rk, - const std::vector& vchCryptedSecret, - const CKeyMetadata &keyMeta); bool WriteCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta); - bool WriteSproutViewingKey(const libzcash::SproutViewingKey &vk); - bool EraseSproutViewingKey(const libzcash::SproutViewingKey &vk); - private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 148cd321c..5ce66a82f 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2019-2020 The Hush developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php + #include "Address.hpp" #include "NoteEncryption.hpp" #include "hash.h" @@ -13,36 +17,6 @@ const uint32_t SAPLING_BRANCH_ID = 0x76b809bb; namespace libzcash { -uint256 SproutPaymentAddress::GetHash() const { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << *this; - return Hash(ss.begin(), ss.end()); -} - -uint256 ReceivingKey::pk_enc() const { - return ZCNoteEncryption::generate_pubkey(*this); -} - -SproutPaymentAddress SproutViewingKey::address() const { - return SproutPaymentAddress(a_pk, sk_enc.pk_enc()); -} - -ReceivingKey SproutSpendingKey::receiving_key() const { - return ReceivingKey(ZCNoteEncryption::generate_privkey(*this)); -} - -SproutViewingKey SproutSpendingKey::viewing_key() const { - return SproutViewingKey(PRF_addr_a_pk(*this), receiving_key()); -} - -SproutSpendingKey SproutSpendingKey::random() { - return SproutSpendingKey(random_uint252()); -} - -SproutPaymentAddress SproutSpendingKey::address() const { - return viewing_key().address(); -} - //! Sapling uint256 SaplingPaymentAddress::GetHash() const { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -119,10 +93,6 @@ class IsValidAddressForNetwork : public boost::static_visitor { public: IsValidAddressForNetwork(uint32_t consensusBranchId) : branchId(consensusBranchId) {} - bool operator()(const libzcash::SproutPaymentAddress &addr) const { - return true; - } - bool operator()(const libzcash::InvalidEncoding &addr) const { return false; } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 42ee0031b..ac90120a5 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #ifndef ZC_ADDRESS_H_ #define ZC_ADDRESS_H_ @@ -26,6 +28,16 @@ const size_t SerializedSaplingSpendingKeySize = 32; typedef std::array diversifier_t; +class ReceivingKey : public uint256 { +public: + ReceivingKey() { } + ReceivingKey(uint256 sk_enc) : uint256(sk_enc) { } + + uint256 pk_enc() const; +}; + + +// NOTE: wallet.dat format depends on this class SproutPaymentAddress { public: uint256 a_pk; @@ -54,52 +66,6 @@ public: } }; -class ReceivingKey : public uint256 { -public: - ReceivingKey() { } - ReceivingKey(uint256 sk_enc) : uint256(sk_enc) { } - - uint256 pk_enc() const; -}; - -class SproutViewingKey { -public: - uint256 a_pk; - ReceivingKey sk_enc; - - SproutViewingKey() : a_pk(), sk_enc() { } - SproutViewingKey(uint256 a_pk, ReceivingKey sk_enc) : a_pk(a_pk), sk_enc(sk_enc) { } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(a_pk); - READWRITE(sk_enc); - } - - SproutPaymentAddress address() const; - - friend inline bool operator==(const SproutViewingKey& a, const SproutViewingKey& b) { - return a.a_pk == b.a_pk && a.sk_enc == b.sk_enc; - } - friend inline bool operator<(const SproutViewingKey& a, const SproutViewingKey& b) { - return (a.a_pk < b.a_pk || - (a.a_pk == b.a_pk && a.sk_enc < b.sk_enc)); - } -}; - -class SproutSpendingKey : public uint252 { -public: - SproutSpendingKey() : uint252() { } - SproutSpendingKey(uint252 a_sk) : uint252(a_sk) { } - - static SproutSpendingKey random(); - - ReceivingKey receiving_key() const; - SproutViewingKey viewing_key() const; - SproutPaymentAddress address() const; -}; //! Sapling functions. class SaplingPaymentAddress { @@ -218,8 +184,8 @@ public: SaplingPaymentAddress default_address() const; }; -typedef boost::variant PaymentAddress; -typedef boost::variant ViewingKey; +typedef boost::variant PaymentAddress; +typedef boost::variant ViewingKey; } diff --git a/src/zcash/IncrementalMerkleTree.cpp b/src/zcash/IncrementalMerkleTree.cpp index b8b3cd183..d4411c11b 100644 --- a/src/zcash/IncrementalMerkleTree.cpp +++ b/src/zcash/IncrementalMerkleTree.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include #include @@ -1016,6 +1018,7 @@ size_t IncrementalMerkleTree::next_depth(size_t skip) const { template Hash IncrementalMerkleTree::root(size_t depth, std::deque filler_hashes) const { + //fprintf(stderr,"%s: depth=%d\n",__func__,depth); PathFiller filler(filler_hashes); Hash combine_left = left ? *left : filler.next(0); diff --git a/src/zcash/IncrementalMerkleTree.hpp b/src/zcash/IncrementalMerkleTree.hpp index 1ebb2b499..ab55e8ad4 100644 --- a/src/zcash/IncrementalMerkleTree.hpp +++ b/src/zcash/IncrementalMerkleTree.hpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #ifndef ZC_INCREMENTALMERKLETREE_H_ #define ZC_INCREMENTALMERKLETREE_H_ diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 0a052209a..5cd7627dc 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers #include "JoinSplit.hpp" #include "prf.h" #include "sodium.h" @@ -27,245 +28,5 @@ class JoinSplitCircuit : public JoinSplit { public: JoinSplitCircuit() {} ~JoinSplitCircuit() {} - - SproutProof prove( - const std::array& inputs, - const std::array& outputs, - std::array& out_notes, - std::array& out_ciphertexts, - uint256& out_ephemeralKey, - const uint256& joinSplitPubKey, - uint256& out_randomSeed, - std::array& out_macs, - std::array& out_nullifiers, - std::array& out_commitments, - uint64_t vpub_old, - uint64_t vpub_new, - const uint256& rt, - bool computeProof, - uint256 *out_esk // Payment disclosure - ) { - if (vpub_old > MAX_MONEY) { - throw std::invalid_argument("nonsensical vpub_old value"); - } - - if (vpub_new > MAX_MONEY) { - throw std::invalid_argument("nonsensical vpub_new value"); - } - - uint64_t lhs_value = vpub_old; - uint64_t rhs_value = vpub_new; - - for (size_t i = 0; i < NumInputs; i++) { - // Sanity checks of input - { - // If note has nonzero value - if (inputs[i].note.value() != 0) { - // The witness root must equal the input root. - if (inputs[i].witness.root() != rt) { - throw std::invalid_argument("joinsplit not anchored to the correct root"); - } - - // The tree must witness the correct element - if (inputs[i].note.cm() != inputs[i].witness.element()) { - throw std::invalid_argument("witness of wrong element for joinsplit input"); - } - } - - // Ensure we have the key to this note. - if (inputs[i].note.a_pk != inputs[i].key.address().a_pk) { - throw std::invalid_argument("input note not authorized to spend with given key"); - } - - // Balance must be sensical - if (inputs[i].note.value() > MAX_MONEY) { - throw std::invalid_argument("nonsensical input note value"); - } - - lhs_value += inputs[i].note.value(); - - if (lhs_value > MAX_MONEY) { - throw std::invalid_argument("nonsensical left hand size of joinsplit balance"); - } - } - - // Compute nullifier of input - out_nullifiers[i] = inputs[i].nullifier(); - } - - // Sample randomSeed - out_randomSeed = random_uint256(); - - // Compute h_sig - uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, joinSplitPubKey); - - // Sample phi - uint252 phi = random_uint252(); - - // Compute notes for outputs - for (size_t i = 0; i < NumOutputs; i++) { - // Sanity checks of output - { - if (outputs[i].value > MAX_MONEY) { - throw std::invalid_argument("nonsensical output value"); - } - - rhs_value += outputs[i].value; - - if (rhs_value > MAX_MONEY) { - throw std::invalid_argument("nonsensical right hand side of joinsplit balance"); - } - } - - // Sample r - uint256 r = random_uint256(); - - out_notes[i] = outputs[i].note(phi, r, i, h_sig); - } - - if (lhs_value != rhs_value) { - throw std::invalid_argument("invalid joinsplit balance"); - } - - // Compute the output commitments - for (size_t i = 0; i < NumOutputs; i++) { - out_commitments[i] = out_notes[i].cm(); - } - - // Encrypt the ciphertexts containing the note - // plaintexts to the recipients of the value. - { - ZCNoteEncryption encryptor(h_sig); - - for (size_t i = 0; i < NumOutputs; i++) { - SproutNotePlaintext pt(out_notes[i], outputs[i].memo); - - out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc); - } - - out_ephemeralKey = encryptor.get_epk(); - - // !!! Payment disclosure START - if (out_esk != nullptr) { - *out_esk = encryptor.get_esk(); - } - // !!! Payment disclosure END - } - - // Authenticate h_sig with each of the input - // spending keys, producing macs which protect - // against malleability. - for (size_t i = 0; i < NumInputs; i++) { - out_macs[i] = PRF_pk(inputs[i].key, i, h_sig); - } - - if (!computeProof) { - return GrothProof(); - } - - GrothProof proof; - - CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION); - ss1 << inputs[0].witness.path(); - std::vector auth1(ss1.begin(), ss1.end()); - - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << inputs[1].witness.path(); - std::vector auth2(ss2.begin(), ss2.end()); - - librustzcash_sprout_prove( - proof.begin(), - - phi.begin(), - rt.begin(), - h_sig.begin(), - - inputs[0].key.begin(), - inputs[0].note.value(), - inputs[0].note.rho.begin(), - inputs[0].note.r.begin(), - auth1.data(), - - inputs[1].key.begin(), - inputs[1].note.value(), - inputs[1].note.rho.begin(), - inputs[1].note.r.begin(), - auth2.data(), - - out_notes[0].a_pk.begin(), - out_notes[0].value(), - out_notes[0].r.begin(), - - out_notes[1].a_pk.begin(), - out_notes[1].value(), - out_notes[1].r.begin(), - - vpub_old, - vpub_new - ); - - return proof; - } }; - -template -JoinSplit* JoinSplit::Prepared() -{ - return new JoinSplitCircuit(); -} - -template -uint256 JoinSplit::h_sig( - const uint256& randomSeed, - const std::array& nullifiers, - const uint256& joinSplitPubKey -) { - const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] - = {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'}; - - std::vector block(randomSeed.begin(), randomSeed.end()); - - for (size_t i = 0; i < NumInputs; i++) { - block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end()); - } - - block.insert(block.end(), joinSplitPubKey.begin(), joinSplitPubKey.end()); - - uint256 output; - - if (crypto_generichash_blake2b_salt_personal(output.begin(), 32, - &block[0], block.size(), - NULL, 0, // No key. - NULL, // No salt. - personalization - ) != 0) - { - throw std::logic_error("hash function failure"); - } - - return output; -} - -SproutNote JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const { - uint256 rho = PRF_rho(phi, i, h_sig); - - return SproutNote(addr.a_pk, value, rho, r); -} - -JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) { - SproutSpendingKey a_sk = SproutSpendingKey::random(); - addr = a_sk.address(); -} - -JSInput::JSInput() : witness(SproutMerkleTree().witness()), - key(SproutSpendingKey::random()) { - note = SproutNote(key.address().a_pk, 0, random_uint256(), random_uint256()); - SproutMerkleTree dummy_tree; - dummy_tree.append(note.cm()); - witness = dummy_tree.witness(); -} - -template class JoinSplit; - } diff --git a/src/zcash/JoinSplit.hpp b/src/zcash/JoinSplit.hpp index 09e31570f..e7aded3c1 100644 --- a/src/zcash/JoinSplit.hpp +++ b/src/zcash/JoinSplit.hpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #ifndef ZC_JOINSPLIT_H_ #define ZC_JOINSPLIT_H_ @@ -24,69 +26,13 @@ typedef std::array GrothProof; typedef boost::variant SproutProof; class JSInput { -public: - SproutWitness witness; - SproutNote note; - SproutSpendingKey key; - - JSInput(); - JSInput(SproutWitness witness, - SproutNote note, - SproutSpendingKey key) : witness(witness), note(note), key(key) { } - - uint256 nullifier() const { - return note.nullifier(key); - } }; class JSOutput { -public: - SproutPaymentAddress addr; - uint64_t value; - std::array memo = {{0xF6}}; // 0xF6 is invalid UTF8 as per spec, rest of array is 0x00 - - JSOutput(); - JSOutput(SproutPaymentAddress addr, uint64_t value) : addr(addr), value(value) { } - - SproutNote note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const; }; template class JoinSplit { -public: - virtual ~JoinSplit() {} - - static JoinSplit* Prepared(); - - static uint256 h_sig(const uint256& randomSeed, - const std::array& nullifiers, - const uint256& joinSplitPubKey - ); - - // Compute nullifiers, macs, note commitments & encryptions, and SNARK proof - virtual SproutProof prove( - const std::array& inputs, - const std::array& outputs, - std::array& out_notes, - std::array& out_ciphertexts, - uint256& out_ephemeralKey, - const uint256& joinSplitPubKey, - uint256& out_randomSeed, - std::array& out_hmacs, - std::array& out_nullifiers, - std::array& out_commitments, - uint64_t vpub_old, - uint64_t vpub_new, - const uint256& rt, - bool computeProof = true, - // For paymentdisclosure, we need to retrieve the esk. - // Reference as non-const parameter with default value leads to compile error. - // So use pointer for simplicity. - uint256 *out_esk = nullptr - ) = 0; - -protected: - JoinSplit() {} }; } diff --git a/src/zcash/Note.cpp b/src/zcash/Note.cpp index 23210c784..e6d185a0e 100644 --- a/src/zcash/Note.cpp +++ b/src/zcash/Note.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include "Note.hpp" #include "prf.h" #include "crypto/sha256.h" @@ -11,35 +13,6 @@ using namespace libzcash; -SproutNote::SproutNote() { - a_pk = random_uint256(); - rho = random_uint256(); - r = random_uint256(); -} - -uint256 SproutNote::cm() const { - unsigned char discriminant = 0xb0; - - CSHA256 hasher; - hasher.Write(&discriminant, 1); - hasher.Write(a_pk.begin(), 32); - - auto value_vec = convertIntToVectorLE(value_); - - hasher.Write(&value_vec[0], value_vec.size()); - hasher.Write(rho.begin(), 32); - hasher.Write(r.begin(), 32); - - uint256 result; - hasher.Finalize(result.begin()); - - return result; -} - -uint256 SproutNote::nullifier(const SproutSpendingKey& a_sk) const { - return PRF_nf(a_sk, rho); -} - // Construct and populate Sapling note for a given payment address and value. SaplingNote::SaplingNote(const SaplingPaymentAddress& address, const uint64_t value) : BaseNote(value) { d = address.d; @@ -88,57 +61,6 @@ boost::optional SaplingNote::nullifier(const SaplingFullViewingKey& vk, return result; } -SproutNotePlaintext::SproutNotePlaintext( - const SproutNote& note, - std::array memo) : BaseNotePlaintext(note, memo) -{ - rho = note.rho; - r = note.r; -} - -SproutNote SproutNotePlaintext::note(const SproutPaymentAddress& addr) const -{ - return SproutNote(addr.a_pk, value_, rho, r); -} - -SproutNotePlaintext SproutNotePlaintext::decrypt(const ZCNoteDecryption& decryptor, - const ZCNoteDecryption::Ciphertext& ciphertext, - const uint256& ephemeralKey, - const uint256& h_sig, - unsigned char nonce - ) -{ - auto plaintext = decryptor.decrypt(ciphertext, ephemeralKey, h_sig, nonce); - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << plaintext; - - SproutNotePlaintext ret; - ss >> ret; - - assert(ss.size() == 0); - - return ret; -} - -ZCNoteEncryption::Ciphertext SproutNotePlaintext::encrypt(ZCNoteEncryption& encryptor, - const uint256& pk_enc - ) const -{ - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << (*this); - - ZCNoteEncryption::Plaintext pt; - - assert(pt.size() == ss.size()); - - memcpy(&pt[0], &ss[0], pt.size()); - - return encryptor.encrypt(pk_enc, pt); -} - - - // Construct and populate SaplingNotePlaintext for a given note and memo. SaplingNotePlaintext::SaplingNotePlaintext( const SaplingNote& note, diff --git a/src/zcash/Note.hpp b/src/zcash/Note.hpp index 7d3377306..5b16d95c5 100644 --- a/src/zcash/Note.hpp +++ b/src/zcash/Note.hpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #ifndef ZC_NOTE_H_ #define ZC_NOTE_H_ @@ -22,25 +24,6 @@ public: inline uint64_t value() const { return value_; }; }; -class SproutNote : public BaseNote { -public: - uint256 a_pk; - uint256 rho; - uint256 r; - - SproutNote(uint256 a_pk, uint64_t value, uint256 rho, uint256 r) - : BaseNote(value), a_pk(a_pk), rho(rho), r(r) {} - - SproutNote(); - - virtual ~SproutNote() {}; - - uint256 cm() const; - - uint256 nullifier(const SproutSpendingKey& a_sk) const; -}; - - class SaplingNote : public BaseNote { public: diversifier_t d; @@ -74,48 +57,6 @@ public: inline const std::array & memo() const { return memo_; } }; -class SproutNotePlaintext : public BaseNotePlaintext { -public: - uint256 rho; - uint256 r; - - SproutNotePlaintext() {} - - SproutNotePlaintext(const SproutNote& note, std::array memo); - - SproutNote note(const SproutPaymentAddress& addr) const; - - virtual ~SproutNotePlaintext() {} - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - unsigned char leadingByte = 0x00; - READWRITE(leadingByte); - - if (leadingByte != 0x00) { - throw std::ios_base::failure("lead byte of SproutNotePlaintext is not recognized"); - } - - READWRITE(value_); - READWRITE(rho); - READWRITE(r); - READWRITE(memo_); - } - - static SproutNotePlaintext decrypt(const ZCNoteDecryption& decryptor, - const ZCNoteDecryption::Ciphertext& ciphertext, - const uint256& ephemeralKey, - const uint256& h_sig, - unsigned char nonce - ); - - ZCNoteEncryption::Ciphertext encrypt(ZCNoteEncryption& encryptor, - const uint256& pk_enc - ) const; -}; - typedef std::pair SaplingNotePlaintextEncryptionResult; class SaplingNotePlaintext : public BaseNotePlaintext { diff --git a/src/zcash/NoteEncryption.cpp b/src/zcash/NoteEncryption.cpp index 63e073265..9f2d64563 100644 --- a/src/zcash/NoteEncryption.cpp +++ b/src/zcash/NoteEncryption.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include "NoteEncryption.hpp" #include #include "sodium.h" @@ -374,52 +376,6 @@ typename NoteDecryption::Plaintext NoteDecryption::decrypt return plaintext; } -// -// Payment disclosure - decrypt with esk -// -template -typename PaymentDisclosureNoteDecryption::Plaintext PaymentDisclosureNoteDecryption::decryptWithEsk - (const PaymentDisclosureNoteDecryption::Ciphertext &ciphertext, - const uint256 &pk_enc, - const uint256 &esk, - const uint256 &hSig, - unsigned char nonce - ) const -{ - uint256 dhsecret; - - if (crypto_scalarmult(dhsecret.begin(), esk.begin(), pk_enc.begin()) != 0) { - throw std::logic_error("Could not create DH secret"); - } - - // Regenerate keypair - uint256 epk = NoteEncryption::generate_pubkey(esk); - - unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; - KDF(K, dhsecret, epk, pk_enc, hSig, nonce); - - // The nonce is zero because we never reuse keys - unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; - - PaymentDisclosureNoteDecryption::Plaintext plaintext; - - // Message length is always NOTEENCRYPTION_AUTH_BYTES less than - // the ciphertext length. - if (crypto_aead_chacha20poly1305_ietf_decrypt(plaintext.begin(), NULL, - NULL, - ciphertext.begin(), PaymentDisclosureNoteDecryption::CLEN, - NULL, - 0, - cipher_nonce, K) != 0) { - throw note_decryption_failed(); - } - - return plaintext; -} - - - - template uint256 NoteEncryption::generate_privkey(const uint252 &a_sk) { @@ -461,6 +417,4 @@ uint252 random_uint252() template class NoteEncryption; template class NoteDecryption; -template class PaymentDisclosureNoteDecryption; - } diff --git a/src/zcash/NoteEncryption.hpp b/src/zcash/NoteEncryption.hpp index f6e692028..9c726d5cf 100644 --- a/src/zcash/NoteEncryption.hpp +++ b/src/zcash/NoteEncryption.hpp @@ -1,7 +1,4 @@ -/* -See the Zcash protocol specification for more information. -https://github.com/zcash/zips/blob/master/protocol/protocol.pdf -*/ +// Copyright (c) 2019-2020 The Hush developers #ifndef ZC_NOTE_ENCRYPTION_H_ #define ZC_NOTE_ENCRYPTION_H_ @@ -169,33 +166,9 @@ public: }; - -// Subclass PaymentDisclosureNoteDecryption provides a method to decrypt a note with esk. -template -class PaymentDisclosureNoteDecryption : public NoteDecryption { -protected: -public: - enum { CLEN=MLEN+NOTEENCRYPTION_AUTH_BYTES }; - typedef std::array Ciphertext; - typedef std::array Plaintext; - - PaymentDisclosureNoteDecryption() : NoteDecryption() {} - PaymentDisclosureNoteDecryption(uint256 sk_enc) : NoteDecryption(sk_enc) {} - - Plaintext decryptWithEsk( - const Ciphertext &ciphertext, - const uint256 &pk_enc, - const uint256 &esk, - const uint256 &hSig, - unsigned char nonce - ) const; -}; - } typedef libzcash::NoteEncryption ZCNoteEncryption; typedef libzcash::NoteDecryption ZCNoteDecryption; -typedef libzcash::PaymentDisclosureNoteDecryption ZCPaymentDisclosureNoteDecryption; - #endif /* ZC_NOTE_ENCRYPTION_H_ */ diff --git a/src/zcash/Proof.cpp b/src/zcash/Proof.cpp index 7ab1298a8..1a0ebfe75 100644 --- a/src/zcash/Proof.cpp +++ b/src/zcash/Proof.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include "Proof.hpp" #include "crypto/common.h" diff --git a/src/zcash/Proof.hpp b/src/zcash/Proof.hpp index 7c9b7f7f5..0a3d0bb14 100644 --- a/src/zcash/Proof.hpp +++ b/src/zcash/Proof.hpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #ifndef ZC_PROOF_H_ #define ZC_PROOF_H_ diff --git a/src/zcash/Zcash.h b/src/zcash/Zcash.h index 84dfe9525..e45aa1d02 100644 --- a/src/zcash/Zcash.h +++ b/src/zcash/Zcash.h @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers #ifndef ZC_ZCASH_H_ #define ZC_ZCASH_H_ diff --git a/src/zcash/prf.cpp b/src/zcash/prf.cpp index 2491de83e..9ab3d0f10 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include "prf.h" #include "crypto/sha256.h" #include "hash.h" diff --git a/src/zcash/util.cpp b/src/zcash/util.cpp index 6f32bf79a..064e1cca9 100644 --- a/src/zcash/util.cpp +++ b/src/zcash/util.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include "zcash/util.h" #include #include diff --git a/src/zcash/util.h b/src/zcash/util.h index 10886e3ca..0c04a5aac 100644 --- a/src/zcash/util.h +++ b/src/zcash/util.h @@ -1,3 +1,4 @@ +// Copyright (c) 2019-2020 The Hush developers #ifndef ZC_UTIL_H_ #define ZC_UTIL_H_ diff --git a/src/zcash/zip32.cpp b/src/zcash/zip32.cpp index 15478843e..615c5896d 100644 --- a/src/zcash/zip32.cpp +++ b/src/zcash/zip32.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2018 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zcash/zip32.h b/src/zcash/zip32.h index 44bc58598..1521dddd1 100644 --- a/src/zcash/zip32.h +++ b/src/zcash/zip32.h @@ -1,4 +1,5 @@ // Copyright (c) 2018 The Zcash developers +// Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -134,7 +135,7 @@ struct SaplingExtendedSpendingKey { } }; -typedef boost::variant SpendingKey; +typedef boost::variant SpendingKey; } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 2f79e454b..152ad7295 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -25,7 +25,6 @@ #include "sodium.h" #include "streams.h" #include "txdb.h" -#include "utiltest.h" #include "wallet/wallet.h" #include "zcbenchmarks.h" @@ -41,7 +40,7 @@ void pre_wallet_load() { LogPrintf("%s: In progress...\n", __func__); if (ShutdownRequested()) - throw new std::runtime_error("The node is shutting down"); + throw new std::runtime_error("The Hush node is shutting down"); if (pwalletMain) pwalletMain->Flush(false); @@ -93,60 +92,6 @@ double benchmark_sleep() return timer_stop(tv_start); } -double benchmark_create_joinsplit() -{ - uint256 joinSplitPubKey; - - /* Get the anchor of an empty commitment tree. */ - uint256 anchor = SproutMerkleTree().root(); - - struct timeval tv_start; - timer_start(tv_start); - JSDescription jsdesc(*pzcashParams, - joinSplitPubKey, - anchor, - {JSInput(), JSInput()}, - {JSOutput(), JSOutput()}, - 0, - 0); - double ret = timer_stop(tv_start); - - auto verifier = libzcash::ProofVerifier::Strict(); - assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); - return ret; -} - -std::vector benchmark_create_joinsplit_threaded(int nThreads) -{ - std::vector ret; - std::vector> tasks; - std::vector threads; - for (int i = 0; i < nThreads; i++) { - std::packaged_task task(&benchmark_create_joinsplit); - tasks.emplace_back(task.get_future()); - threads.emplace_back(std::move(task)); - } - std::future_status status; - for (auto it = tasks.begin(); it != tasks.end(); it++) { - it->wait(); - ret.push_back(it->get()); - } - for (auto it = threads.begin(); it != threads.end(); it++) { - it->join(); - } - return ret; -} - -double benchmark_verify_joinsplit(const JSDescription &joinsplit) -{ - struct timeval tv_start; - timer_start(tv_start); - uint256 joinSplitPubKey; - auto verifier = libzcash::ProofVerifier::Strict(); - joinsplit.Verify(*pzcashParams, verifier, joinSplitPubKey); - return timer_stop(tv_start); -} - #ifdef ENABLE_MINING double benchmark_solve_equihash() { @@ -263,160 +208,6 @@ double benchmark_large_tx(size_t nInputs) return timer_stop(tv_start); } -double benchmark_try_decrypt_notes(size_t nAddrs) -{ - CWallet wallet; - for (int i = 0; i < nAddrs; i++) { - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - } - - auto sk = libzcash::SproutSpendingKey::random(); - auto tx = GetValidReceive(*pzcashParams, sk, 10, true); - - struct timeval tv_start; - timer_start(tv_start); - auto nd = wallet.FindMySproutNotes(tx); - return timer_stop(tv_start); -} - -double benchmark_increment_note_witnesses(size_t nTxs) -{ - CWallet wallet; - SproutMerkleTree sproutTree; - SaplingMerkleTree saplingTree; - - auto sk = libzcash::SproutSpendingKey::random(); - wallet.AddSproutSpendingKey(sk); - - // First block - CBlock block1; - for (int i = 0; i < nTxs; i++) { - auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); - auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - - wtx.SetSproutNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - block1.vtx.push_back(wtx); - } - CBlockIndex index1(block1); - index1.SetHeight(1); - - // Increment to get transactions witnessed - wallet.ChainTip(&index1, &block1, std::make_pair(sproutTree, saplingTree)); - - // Second block - CBlock block2; - block2.hashPrevBlock = block1.GetHash(); - { - auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); - auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapSproutNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - SproutNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - - wtx.SetSproutNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - block2.vtx.push_back(wtx); - } - CBlockIndex index2(block2); - index2.SetHeight(2); - - struct timeval tv_start; - timer_start(tv_start); - wallet.ChainTip(&index2, &block2, std::make_pair(sproutTree, saplingTree)); - return timer_stop(tv_start); -} - -// Fake the input of a given block -class FakeCoinsViewDB : public CCoinsViewDB { - uint256 hash; - SproutMerkleTree t; - -public: - FakeCoinsViewDB(std::string dbName, uint256& hash) : CCoinsViewDB(dbName, 100, false, false), hash(hash) {} - - bool GetAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { - if (rt == t.root()) { - tree = t; - return true; - } - return false; - } - - bool GetNullifier(const uint256 &nf, ShieldedType type) const { - return false; - } - - uint256 GetBestBlock() const { - return hash; - } - - uint256 GetBestAnchor() const { - return t.root(); - } - - bool BatchWrite(CCoinsMap &mapCoins, - const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsSproutMap &mapSproutAnchors, - CNullifiersMap &mapSproutNullifiers, - CNullifiersMap& mapSaplingNullifiers) { - return false; - } - - bool GetStats(CCoinsStats &stats) const { - return false; - } -}; - -double benchmark_connectblock_slow() -{ - // Test for issue 2017-05-01.a - SelectParams(CBaseChainParams::MAIN); - CBlock block; - FILE* fp = fopen((GetDataDir() / "benchmark/block-107134.dat").string().c_str(), "rb"); - if (!fp) throw new std::runtime_error("Failed to open block data file"); - CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION); - blkFile >> block; - blkFile.fclose(); - - // Fake its inputs - auto hashPrev = uint256S("00000000159a41f468e22135942a567781c3f3dc7ad62257993eb3c69c3f95ef"); - FakeCoinsViewDB fakeDB("benchmark/block-107134-inputs", hashPrev); - CCoinsViewCache view(&fakeDB); - - // Fake the chain - CBlockIndex index(block); - index.SetHeight(107134); - CBlockIndex indexPrev; - indexPrev.phashBlock = &hashPrev; - indexPrev.SetHeight(index.GetHeight() - 1); - index.pprev = &indexPrev; - mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev)); - - CValidationState state; - struct timeval tv_start; - timer_start(tv_start); - assert(ConnectBlock(block, state, &index, view, true)); - auto duration = timer_stop(tv_start); - - // Undo alterations to global state - mapBlockIndex.erase(hashPrev); - SelectParamsFromCommandLine(); - - return duration; -} - extern UniValue getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk);