@@ -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
|
||||
```
|
||||
|
||||
5
build.sh
Executable file
5
build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) 2019-2020 The Hush developers
|
||||
|
||||
set -eu -o pipefail
|
||||
./zcutil/build.sh $@
|
||||
@@ -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)))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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 <https://myhush.org/security/>.
|
||||
@@ -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 <https://myhush.org/security/>.
|
||||
|
||||
@@ -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] <hex\-tx> [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 <https://myhush.org/security/>.
|
||||
|
||||
@@ -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 <https://myhush.org/security/>.
|
||||
@@ -87,6 +87,11 @@ leave that many cores free, default: 0)
|
||||
.IP
|
||||
Specify pid file (default: komodod.pid)
|
||||
.HP
|
||||
\fB\-txexpirynotify=\fR<cmd>
|
||||
.IP
|
||||
Execute command when transaction expires (%s in cmd is replaced by
|
||||
transaction id)
|
||||
.HP
|
||||
\fB\-prune=\fR<n>
|
||||
.IP
|
||||
Reduce storage requirements by pruning (deleting) old blocks. This mode
|
||||
@@ -295,6 +300,11 @@ Keep the last <n> transactions (default: 200)
|
||||
.IP
|
||||
Keep transactions for at least <n> blocks (default: 10000)
|
||||
.HP
|
||||
\fB\-opretmintxfee=\fR<amt>
|
||||
.IP
|
||||
Minimum fee (in KMD/kB) to allow for OP_RETURN transactions (default:
|
||||
400000)
|
||||
.HP
|
||||
\fB\-paytxfee=\fR<amt>
|
||||
.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 <https://myhush.org/security/>.
|
||||
|
||||
@@ -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.
|
||||
@@ -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'
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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 \
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -193,9 +193,10 @@ unsigned int base_uint<BITS>::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;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright © 2019-2020 The Hush Developers
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
1599
src/cc/dapps/dappinc.h
Normal file
1599
src/cc/dapps/dappinc.h
Normal file
File diff suppressed because it is too large
Load Diff
1429
src/cc/dapps/subatomic.c
Normal file
1429
src/cc/dapps/subatomic.c
Normal file
File diff suppressed because it is too large
Load Diff
27
src/cc/dapps/subatomic.json
Normal file
27
src/cc/dapps/subatomic.json
Normal file
@@ -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" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uint256, SproutMerkleTree, CCoinsKeyHasher> 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());
|
||||
|
||||
11
src/coins.h
11
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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -36,4 +36,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright (c) 2019-2020 The Hush developers
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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; i<cond->size; 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,585 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/variant/get.hpp>
|
||||
|
||||
#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 <array>
|
||||
|
||||
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<JSInput, 2>& inputs,
|
||||
const std::array<JSOutput, 2>& 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<JSInput, 2> inputs = {
|
||||
JSInput(), // dummy input
|
||||
JSInput() // dummy input
|
||||
};
|
||||
|
||||
std::array<JSOutput, 2> outputs = {
|
||||
JSOutput(recipient_addr, 10),
|
||||
JSOutput() // dummy output
|
||||
};
|
||||
|
||||
std::array<SproutNote, 2> 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<JSInput, 2> inputs = {
|
||||
JSInput(), // dummy input
|
||||
JSInput(witness_recipient, decrypted_note, recipient_key)
|
||||
};
|
||||
|
||||
SproutSpendingKey second_recipient = SproutSpendingKey::random();
|
||||
SproutPaymentAddress second_addr = second_recipient.address();
|
||||
|
||||
std::array<JSOutput, 2> outputs = {
|
||||
JSOutput(second_addr, 9),
|
||||
JSOutput() // dummy output
|
||||
};
|
||||
|
||||
std::array<SproutNote, 2> 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<JSInput, 2>& inputs,
|
||||
const std::array<JSOutput, 2>& outputs,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt
|
||||
) {
|
||||
uint256 ephemeralKey;
|
||||
uint256 randomSeed;
|
||||
uint256 joinSplitPubKey = random_uint256();
|
||||
std::array<uint256, 2> macs;
|
||||
std::array<uint256, 2> nullifiers;
|
||||
std::array<uint256, 2> commitments;
|
||||
std::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
||||
|
||||
std::array<SproutNote, 2> 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<JSInput, 2>& inputs,
|
||||
const std::array<JSOutput, 2>& 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<std::vector<std::string>> 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<std::string>& 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<SproutWitness>& 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<SproutWitness> 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<unsigned char, ZC_MEMO_SIZE> 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);
|
||||
}
|
||||
@@ -95,101 +95,6 @@ TEST(keystore_tests, sapling_keys) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(keystore_tests, store_and_retrieve_spending_key) {
|
||||
CBasicKeyStore keyStore;
|
||||
libzcash::SproutSpendingKey skOut;
|
||||
|
||||
std::set<libzcash::SproutPaymentAddress> 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<libzcash::SproutPaymentAddress> 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<libzcash::SproutPaymentAddress> 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
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "utilmoneystr.h"
|
||||
#include "chainparams.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "wallet/wallet.h"
|
||||
#include "amount.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <iostream>
|
||||
#include "util.h"
|
||||
|
||||
#include "paymentdisclosure.h"
|
||||
#include "paymentdisclosuredb.h"
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
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<std::mutex> 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<NUM_TRIES; i++) {
|
||||
// Generate an ephemeral keypair for joinsplit sig.
|
||||
uint256 joinSplitPubKey;
|
||||
unsigned char buffer[crypto_sign_SECRETKEYBYTES] = {0};
|
||||
crypto_sign_keypair(joinSplitPubKey.begin(), &buffer[0]);
|
||||
|
||||
// First 32 bytes contain private key, second 32 bytes contain public key.
|
||||
ASSERT_EQ(0, memcmp(joinSplitPubKey.begin(), &buffer[0]+32, 32));
|
||||
std::vector<unsigned char> 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<size_t>::max();
|
||||
uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits<uint8_t>::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<unsigned char, 64> 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
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "primitives/transaction.h"
|
||||
#include "zcash/Note.hpp"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
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<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
|
||||
libzcash::JSInput(witness, note, k),
|
||||
libzcash::JSInput() // dummy input of zero value
|
||||
};
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
|
||||
libzcash::JSOutput(addr, 50),
|
||||
libzcash::JSOutput(addr, 50)
|
||||
};
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
|
||||
|
||||
{
|
||||
auto jsdesc = JSDescription::Randomized(
|
||||
false,
|
||||
*params, joinSplitPubKey, rt,
|
||||
inputs, outputs,
|
||||
inputMap, outputMap,
|
||||
0, 0, false);
|
||||
|
||||
std::set<size_t> inputSet(inputMap.begin(), inputMap.end());
|
||||
std::set<size_t> expectedInputSet {0, 1};
|
||||
EXPECT_EQ(expectedInputSet, inputSet);
|
||||
|
||||
std::set<size_t> outputSet(outputMap.begin(), outputMap.end());
|
||||
std::set<size_t> 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<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> 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<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
|
||||
EXPECT_EQ(expectedInputMap, inputMap);
|
||||
EXPECT_EQ(expectedOutputMap, outputMap);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "rpc/protocol.h" // For HTTP status codes
|
||||
#include "sync.h"
|
||||
#include "ui_interface.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -251,21 +252,25 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
|
||||
{
|
||||
std::unique_ptr<HTTPRequest> 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;
|
||||
|
||||
24
src/init.cpp
24
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=<file>", strprintf(_("Specify pid file (default: %s)"), "komodod.pid"));
|
||||
#endif
|
||||
strUsage += HelpMessageOpt("-txexpirynotify=<cmd>", _("Execute command when transaction expires (%s in cmd is replaced by transaction id)"));
|
||||
strUsage += HelpMessageOpt("-prune=<n>", 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..."));
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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<unsigned char> 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<std::string>
|
||||
@@ -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<unsigned char> 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<unsigned char> 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<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& 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<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& 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<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& 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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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<libzcash::SproutPaymentAddress> &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<libzcash::SaplingPaymentAddress> &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<CKeyID, CKey> KeyMap;
|
||||
typedef std::map<CScriptID, CScript > ScriptMap;
|
||||
typedef std::set<CScript> WatchOnlySet;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, libzcash::SproutSpendingKey> SproutSpendingKeyMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, libzcash::SproutViewingKey> SproutViewingKeyMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, ZCNoteDecryption> 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<libzcash::SproutPaymentAddress> &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<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
|
||||
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, std::vector<unsigned char> > CryptedSproutSpendingKeyMap;
|
||||
|
||||
//! Sapling
|
||||
typedef std::map<libzcash::SaplingExtendedFullViewingKey, std::vector<unsigned char> > CryptedSaplingSpendingKeyMap;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
#include "consensus/params.h"
|
||||
#include "primitives/nonce.h"
|
||||
//#include "primitives/nonce.h"
|
||||
#include "komodo_defs.h"
|
||||
#include "script/standard.h"
|
||||
#include "cc/CCinclude.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)))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 The Hush developers
|
||||
// Copyright (c) 2019-2020 The Hush developers
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
|
||||
106
src/main.cpp
106
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)
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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=<omitted>, 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);
|
||||
}
|
||||
@@ -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 <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
||||
// 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 <typename Stream, typename Operation>
|
||||
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 <typename Stream, typename Operation>
|
||||
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<unsigned char, 64> 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<unsigned char, 64> sig) : payload(payload), payloadSig(sig) {};
|
||||
PaymentDisclosure(const uint256& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
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<PaymentDisclosureKey, PaymentDisclosureInfo> PaymentDisclosureKeyInfo;
|
||||
|
||||
|
||||
#endif // ZCASH_PAYMENTDISCLOSURE_H
|
||||
@@ -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 <boost/filesystem.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static boost::filesystem::path emptyPath;
|
||||
|
||||
/**
|
||||
* Static method to return the shared/default payment disclosure database.
|
||||
*/
|
||||
shared_ptr<PaymentDisclosureDB> PaymentDisclosureDB::sharedInstance() {
|
||||
// Thread-safe in C++11 and gcc 4.3
|
||||
static shared_ptr<PaymentDisclosureDB> ptr = std::make_shared<PaymentDisclosureDB>();
|
||||
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<std::mutex> 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<std::mutex> 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;
|
||||
}
|
||||
@@ -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 <cstdint>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <leveldb/db.h>
|
||||
|
||||
|
||||
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<PaymentDisclosureDB> 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
|
||||
@@ -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"
|
||||
|
||||
@@ -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 <cstring>
|
||||
|
||||
@@ -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
|
||||
@@ -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<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& 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<libzcash::SproutNote, ZC_NUM_JS_OUTPUTS> 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<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof,
|
||||
uint256 *esk, // payment disclosure
|
||||
std::function<int(int)> 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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<uint64_t>(tree.size())));
|
||||
//SproutMerkleTree tree;
|
||||
//pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree);
|
||||
//obj.push_back(Pair("commitments", static_cast<uint64_t>(tree.size())));
|
||||
obj.push_back(Pair("commitments", 0));
|
||||
|
||||
CBlockIndex* tip = chainActive.LastTip();
|
||||
UniValue valuePools(UniValue::VARR);
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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<UniValue>
|
||||
{
|
||||
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<libzcash::SaplingPaymentAddress>(&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"
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
|
||||
@@ -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<uint256, SproutMerkleTree>::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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SproutSpendingKey>(&spendingkey2) != nullptr);
|
||||
auto sk2 = boost::get<SproutSpendingKey>(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<SproutPaymentAddress>(&paymentaddr2) != nullptr);
|
||||
auto addr2 = boost::get<SproutPaymentAddress>(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);
|
||||
|
||||
@@ -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<libzcash::SproutPaymentAddress> 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<libzcash::SproutPaymentAddress> 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<libzcash::SproutPaymentAddress> addrs;
|
||||
pwalletMain->GetSproutPaymentAddresses(addrs);
|
||||
std::set<libzcash::SaplingPaymentAddress> 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<libzcash::SproutPaymentAddress>(&address) != nullptr);
|
||||
auto addr = boost::get<libzcash::SproutPaymentAddress>(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<libzcash::SproutPaymentAddress>(&address) != nullptr);
|
||||
auto newAddr = boost::get<libzcash::SproutPaymentAddress>(address);
|
||||
BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(newAddr));
|
||||
|
||||
// Check if too many args
|
||||
BOOST_CHECK_THROW(CallRPC("z_getnewaddress toomanyargs"), runtime_error);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 <boost/variant.hpp>
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
#include <librustzcash.h>
|
||||
#include "zcash/Note.hpp"
|
||||
|
||||
SpendDescriptionInfo::SpendDescriptionInfo(
|
||||
libzcash::SaplingExpandedSpendingKey expsk,
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
12
src/txdb.cpp
12
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -513,7 +513,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
|
||||
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<uint256> 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<uint256> ids;
|
||||
for (const CTransaction& tx : transactionsToRemove) {
|
||||
list<CTransaction> 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<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates;
|
||||
|
||||
/*
|
||||
boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> 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;
|
||||
|
||||
|
||||
@@ -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<CTransaction>& removed);
|
||||
void removeExpired(unsigned int nBlockHeight);
|
||||
std::vector<uint256> removeExpired(unsigned int nBlockHeight);
|
||||
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
||||
std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
|
||||
void removeWithoutBranchId(uint32_t nMemPoolBranchId);
|
||||
|
||||
@@ -98,6 +98,9 @@ public:
|
||||
|
||||
/** New block has been accepted */
|
||||
boost::signals2::signal<void (const uint256& hash)> NotifyBlockTip;
|
||||
|
||||
/** Transaction expired */
|
||||
boost::signals2::signal<void (const uint256& txid)> NotifyTxExpiration;
|
||||
};
|
||||
|
||||
extern CClientUIInterface uiInterface;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
154
src/utiltest.cpp
154
src/utiltest.cpp
@@ -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 <array>
|
||||
|
||||
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<libzcash::JSInput, 2> inputs = {
|
||||
libzcash::JSInput(), // dummy input
|
||||
libzcash::JSInput() // dummy input
|
||||
};
|
||||
|
||||
std::array<libzcash::JSOutput, 2> 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<libzcash::JSInput, 2> inputs = {
|
||||
libzcash::JSInput(tree.witness(), note, sk),
|
||||
dummyin
|
||||
};
|
||||
|
||||
std::array<libzcash::JSOutput, 2> 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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "paymentdisclosuredb.h"
|
||||
int32_t komodo_blockheight(uint256 hash);
|
||||
|
||||
using namespace libzcash;
|
||||
@@ -75,19 +74,17 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
|
||||
boost::optional<TransactionBuilder> builder,
|
||||
CMutableTransaction contextualTx,
|
||||
std::vector<MergeToAddressInputUTXO> utxoInputs,
|
||||
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs,
|
||||
std::vector<MergeToAddressInputSaplingNote> 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<PaymentDisclosureDB> 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<libzcash::SproutPaymentAddress>(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<MergeToAddressInputSproutNote> 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<JSOutPoint> vOutPoints = {jso};
|
||||
uint256 inputAnchor;
|
||||
std::vector<boost::optional<SproutWitness>> 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<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates;
|
||||
std::vector<uint256> 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<boost::optional<SproutWitness>> 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<SproutWitness> 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<SproutNote> vInputNotes;
|
||||
std::vector<SproutSpendingKey> vInputZKeys;
|
||||
std::vector<JSOutPoint> vOutPoints;
|
||||
std::vector<boost::optional<SproutWitness>> 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<libzcash::SproutPaymentAddress>(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<boost::optional<SproutWitness>> 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<JSOutPoint>& outPoints)
|
||||
{
|
||||
std::vector<boost::optional<SproutWitness>> 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<boost::optional<SproutWitness>> 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<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs{info.vjsin[0], info.vjsin[1]};
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs{info.vjsout[0], info.vjsout[1]};
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> 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<uint64_t>(inputMap[i]));
|
||||
}
|
||||
for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
|
||||
arrOutputMap.push_back(static_cast<uint64_t>(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<unsigned char> 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<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s)
|
||||
{
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> 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));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user