From 796a9901c01162255675612e698958adc25fc44a Mon Sep 17 00:00:00 2001 From: FireMartZ Date: Sat, 3 Mar 2018 20:17:40 -0500 Subject: [PATCH] Merge non source files. --- README.md | 4 +- contrib/ci-workers/grind.yml | 1 - contrib/devtools/README.md | 2 +- depends/builders/darwin.mk | 2 +- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/README.md | 8 +- qa/rpc-tests/bip65-cltv-p2p.py | 1 - qa/rpc-tests/bipdersig-p2p.py | 1 - qa/rpc-tests/mempool_tx_input_limit.py | 33 +--- qa/rpc-tests/nodehandling.py | 17 -- qa/rpc-tests/paymentdisclosure.py | 47 ++---- qa/rpc-tests/prioritisetransaction.py | 28 ---- qa/rpc-tests/proxy_test.py | 1 + qa/rpc-tests/test_framework/blocktools.py | 1 - qa/rpc-tests/test_framework/equihash.py | 1 - qa/rpc-tests/test_framework/netutil.py | 1 - qa/rpc-tests/test_framework/util.py | 30 ++++ qa/rpc-tests/wallet_1941.py | 29 +--- qa/rpc-tests/wallet_nullifiers.py | 45 ++++++ qa/rpc-tests/wallet_protectcoinbase.py | 53 ++++-- qa/rpc-tests/wallet_shieldcoinbase.py | 96 ++++++----- qa/rpc-tests/wallet_treestate.py | 36 +---- qa/rpc-tests/walletbackup.py | 9 ++ qa/rpc-tests/zkey_import_export.py | 189 ++++++++++++++++++++++ share/ui.rc | 2 +- zcutil/build-debian-package.sh | 2 +- zcutil/cleanup-tags.sh | 2 +- zcutil/fetch-params.sh | 2 +- zcutil/make-release.py | 57 ++++++- zcutil/release-notes.py | 41 +++-- 30 files changed, 485 insertions(+), 257 deletions(-) create mode 100644 qa/rpc-tests/zkey_import_export.py diff --git a/README.md b/README.md index c0bd8394d..9dd93225f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HUSH 1.0.13 +# HUSH 1.0.14 ## What is HUSH? @@ -99,4 +99,4 @@ A [HUSH GUI Wallet](https://github.com/MyHush/hush-swing-wallet-ui/releases/tag/ License ------- -For license information see the file [COPYING](COPYING). \ No newline at end of file +For license information see the file [COPYING](COPYING). diff --git a/contrib/ci-workers/grind.yml b/contrib/ci-workers/grind.yml index 9381cf4a4..ef7e5758e 100644 --- a/contrib/ci-workers/grind.yml +++ b/contrib/ci-workers/grind.yml @@ -25,4 +25,3 @@ name: "{{ item }}" state: present with_items: "{{ grind_deps }}" - diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 51d90c4ed..bf84dd056 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -98,4 +98,4 @@ maintained: * for sec/leveldb: https://github.com/bitcoin/leveldb.git (branch bitcoin-fork) Usage: git-subtree-check.sh DIR COMMIT -COMMIT may be omitted, in which case HEAD is used. \ No newline at end of file +COMMIT may be omitted, in which case HEAD is used. diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index 855d3afcb..27f550ab0 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -19,4 +19,4 @@ darwin_LIBTOOL:=$(shell xcrun -f libtool) darwin_OTOOL:=$(shell xcrun -f otool) darwin_NM:=$(shell xcrun -f nm) darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) -darwin_native_toolchain= \ No newline at end of file +darwin_native_toolchain= diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 4846b56ef..b9a15ba00 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -46,6 +46,7 @@ testScripts=( 'disablewallet.py' 'zcjoinsplit.py' 'zcjoinsplitdoublespend.py' + 'zkey_import_export.py' 'getblocktemplate.py' 'bip65-cltv-p2p.py' 'bipdersig-p2p.py' diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index e13d1cbf1..b9ac19561 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -1,4 +1,10 @@ -# Regression tests of RPC interface +Regression tests of RPC interface +================================= + +### [python-bitcoinrpc](https://github.com/jgarzik/python-bitcoinrpc) +Git subtree of [https://github.com/jgarzik/python-bitcoinrpc](https://github.com/jgarzik/python-bitcoinrpc). +Changes to python-bitcoinrpc should be made upstream, and then +pulled here using git subtree. ### [test_framework/test_framework.py](test_framework/test_framework.py) Base class for new regression tests. diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 520516222..cfd2df01e 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -94,4 +94,3 @@ class BIP65Test(ComparisonTestFramework): if __name__ == '__main__': BIP65Test().main() - diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index 8ca0a6d51..f254843f1 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -100,4 +100,3 @@ class BIP66Test(ComparisonTestFramework): if __name__ == '__main__': BIP66Test().main() - diff --git a/qa/rpc-tests/mempool_tx_input_limit.py b/qa/rpc-tests/mempool_tx_input_limit.py index 538122671..c48d73be0 100755 --- a/qa/rpc-tests/mempool_tx_input_limit.py +++ b/qa/rpc-tests/mempool_tx_input_limit.py @@ -6,7 +6,7 @@ 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 + start_node, connect_nodes, wait_and_assert_operationid_status import time from decimal import Decimal @@ -33,34 +33,7 @@ class MempoolTxInputLimitTest(BitcoinTestFramework): recipients = [] recipients.append({"address": to_addr, "amount": amount}) myopid = self.nodes[0].z_sendmany(from_addr, recipients) - return self.wait_and_assert_operationid_status(myopid) - - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - txid = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - time.sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - elif status == "success": - txid = results[0]['result']['txid'] - break - print('...returned status: {}'.format(status)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - return txid + return wait_and_assert_operationid_status(self.nodes[0], myopid) def run_test(self): self.nodes[0].generate(100) @@ -126,7 +99,7 @@ class MempoolTxInputLimitTest(BitcoinTestFramework): recipients.append({"address":self.nodes[1].getnewaddress(), "amount": spend_taddr_amount - spend_taddr_output - spend_taddr_output}) myopid = self.nodes[0].z_sendmany(node0_zaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.nodes[1].generate(1) self.sync_all() diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index 1ce93b476..391a935d0 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -62,22 +62,5 @@ class NodeHandlingTest (BitcoinTestFramework): found = True assert(found) - ########################### - # Connection to self (takes approx 5 min) - # Stress test for network layer. Trying to connect to self every 0.5 sec. - # Helps to discover different multi-threading problems. - ########################### - url = urlparse.urlparse(self.nodes[0].url) - - print "Connection to self stress test. " \ - "Constantly trying to connect to self every 0.5 sec. " \ - "The whole test takes approx 5 mins" - for x in xrange(600): - connect_nodes(self.nodes[0], 0) - time.sleep(0.5) - # self-connection should be disconnected during the version checking - for node in self.nodes[0].getpeerinfo(): - assert(node['addr'] != url.hostname+":"+str(p2p_port(0))) - if __name__ == '__main__': NodeHandlingTest ().main () diff --git a/qa/rpc-tests/paymentdisclosure.py b/qa/rpc-tests/paymentdisclosure.py index 0e21ca98f..48d4712a9 100755 --- a/qa/rpc-tests/paymentdisclosure.py +++ b/qa/rpc-tests/paymentdisclosure.py @@ -6,9 +6,8 @@ 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 + start_node, connect_nodes_bi, wait_and_assert_operationid_status -import time from decimal import Decimal class PaymentDisclosureTest (BitcoinTestFramework): @@ -31,34 +30,6 @@ class PaymentDisclosureTest (BitcoinTestFramework): self.is_network_split=False self.sync_all() - # Returns txid if operation was a success or None - def wait_and_assert_operationid_status(self, nodeid, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - txid = None - for x in xrange(1, timeout): - results = self.nodes[nodeid].z_getoperationresult(opids) - if len(results)==0: - time.sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - elif status == "success": - txid = results[0]['result']['txid'] - break - print('...returned status: {}'.format(status)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert(in_errormsg in errormsg) - print('...returned error: {}'.format(errormsg)) - return txid - def run_test (self): print "Mining blocks..." @@ -97,7 +68,7 @@ class PaymentDisclosureTest (BitcoinTestFramework): # 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 = self.wait_and_assert_operationid_status(0, myopid) + 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 ) @@ -175,6 +146,17 @@ class PaymentDisclosureTest (BitcoinTestFramework): 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) @@ -185,7 +167,7 @@ class PaymentDisclosureTest (BitcoinTestFramework): node1zaddr = self.nodes[1].z_getnewaddress() recipients = [{"address":node1zaddr, "amount":Decimal('1')}] myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - txid = self.wait_and_assert_operationid_status(0, myopid) + txid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[0].generate(1) self.sync_all() @@ -231,4 +213,3 @@ class PaymentDisclosureTest (BitcoinTestFramework): if __name__ == '__main__': PaymentDisclosureTest().main() - diff --git a/qa/rpc-tests/prioritisetransaction.py b/qa/rpc-tests/prioritisetransaction.py index 9eeb72e8c..134b9b160 100755 --- a/qa/rpc-tests/prioritisetransaction.py +++ b/qa/rpc-tests/prioritisetransaction.py @@ -26,34 +26,6 @@ class PrioritiseTransactionTest (BitcoinTestFramework): self.is_network_split=False self.sync_all() - # Returns txid if operation was a success or None - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - txid = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - time.sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - elif status == "success": - txid = results[0]['result']['txid'] - break - print('...returned status: {}'.format(status)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - return txid - def run_test (self): # tx priority is calculated: priority = sum(input_value_in_base_units * input_age)/size_in_bytes diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index 503b8ae43..e4fb48820 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -177,3 +177,4 @@ class ProxyTest(BitcoinTestFramework): if __name__ == '__main__': ProxyTest().main() + diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index 2bd7723ee..1fe2a5dda 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -76,4 +76,3 @@ def create_transaction(prevtx, n, sig, value): tx.vout.append(CTxOut(value, "")) tx.calc_sha256() return tx - diff --git a/qa/rpc-tests/test_framework/equihash.py b/qa/rpc-tests/test_framework/equihash.py index 43eade43e..e40451978 100644 --- a/qa/rpc-tests/test_framework/equihash.py +++ b/qa/rpc-tests/test_framework/equihash.py @@ -291,4 +291,3 @@ def validate_params(n, k): raise ValueError('n must be larger than k') if (((n/(k+1))+1) >= 32): raise ValueError('Parameters must satisfy n/(k+1)+1 < 32') - diff --git a/qa/rpc-tests/test_framework/netutil.py b/qa/rpc-tests/test_framework/netutil.py index 73a39c744..b30a88a4f 100644 --- a/qa/rpc-tests/test_framework/netutil.py +++ b/qa/rpc-tests/test_framework/netutil.py @@ -137,4 +137,3 @@ def addr_to_hex(addr): else: raise ValueError('Could not parse address %s' % addr) return binascii.hexlify(bytearray(addr)) - diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 65b0ed32c..1ff4209ed 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -368,3 +368,33 @@ def assert_raises(exc, fun, *args, **kwds): raise AssertionError("Unexpected exception raised: "+type(e).__name__) else: raise AssertionError("No exception raised") + +# Returns txid if operation was a success or None +def wait_and_assert_operationid_status(node, myopid, in_status='success', in_errormsg=None): + print('waiting for async operation {}'.format(myopid)) + opids = [] + opids.append(myopid) + timeout = 300 + status = None + errormsg = None + txid = None + for x in xrange(1, timeout): + results = node.z_getoperationresult(opids) + if len(results)==0: + time.sleep(1) + else: + status = results[0]["status"] + if status == "failed": + errormsg = results[0]['error']['message'] + elif status == "success": + txid = results[0]['result']['txid'] + break + assert_equal(in_status, status) + if errormsg is not None: + assert(in_errormsg is not None) + assert_equal(in_errormsg in errormsg, True) + if os.getenv("PYTHON_DEBUG", ""): + print('...returned status: {}'.format(status)) + if errormsg is not None: + print('...returned error: {}'.format(errormsg)) + return txid diff --git a/qa/rpc-tests/wallet_1941.py b/qa/rpc-tests/wallet_1941.py index d6f024020..d70b514fc 100755 --- a/qa/rpc-tests/wallet_1941.py +++ b/qa/rpc-tests/wallet_1941.py @@ -8,9 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, initialize_chain_clean, \ initialize_datadir, start_nodes, start_node, connect_nodes_bi, \ - bitcoind_processes + bitcoind_processes, wait_and_assert_operationid_status -import time from decimal import Decimal starttime = 1388534400 @@ -41,30 +40,6 @@ class Wallet1941RegressionTest (BitcoinTestFramework): connect_nodes_bi(self.nodes, 0, 1) self.sync_all() - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - time.sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - break - print('...returned status: {}'.format(status)) - print('...error msg: {}'.format(errormsg)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - def run_test (self): print "Mining blocks..." @@ -78,7 +53,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework): recipients = [] recipients.append({"address":myzaddr, "amount":Decimal('10.0') - Decimal('0.0001')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.nodes[0].generate(1) # Ensure the block times of the latest blocks exceed the variability diff --git a/qa/rpc-tests/wallet_nullifiers.py b/qa/rpc-tests/wallet_nullifiers.py index b7e94b6a6..743af7c92 100755 --- a/qa/rpc-tests/wallet_nullifiers.py +++ b/qa/rpc-tests/wallet_nullifiers.py @@ -170,5 +170,50 @@ class WalletNullifiersTest (BitcoinTestFramework): assert_equal(self.nodes[1].z_getbalance(myzaddr), zaddrremaining2) assert_equal(self.nodes[2].z_getbalance(myzaddr), zaddrremaining2) + # Test viewing keys + + node3mined = Decimal('250.0') + assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance().items()}, { + 'transparent': node3mined, + 'private': zsendmany2notevalue, + 'total': node3mined + zsendmany2notevalue, + }) + + # add node 1 address and node 2 viewing key to node 3 + myzvkey = self.nodes[2].z_exportviewingkey(myzaddr) + self.nodes[3].importaddress(mytaddr1) + self.nodes[3].z_importviewingkey(myzvkey) + + # Check the address has been imported + assert_equal(myzaddr in self.nodes[3].z_listaddresses(), False) + assert_equal(myzaddr in self.nodes[3].z_listaddresses(True), True) + + # Node 3 should see the same received notes as node 2 + assert_equal( + self.nodes[2].z_listreceivedbyaddress(myzaddr), + self.nodes[3].z_listreceivedbyaddress(myzaddr)) + + # Node 3's balances should be unchanged without explicitly requesting + # to include watch-only balances + assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance().items()}, { + 'transparent': node3mined, + 'private': zsendmany2notevalue, + 'total': node3mined + zsendmany2notevalue, + }) + + # Wallet can't cache nullifiers for notes received by addresses it only has a + # viewing key for, and therefore can't detect spends. So it sees a balance + # corresponding to the sum of all notes the address received. + # TODO: Fix this during the Sapling upgrade (via #2277) + assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance(1, True).items()}, { + 'transparent': node3mined + Decimal('1.0'), + 'private': zsendmany2notevalue + zsendmanynotevalue + zaddrremaining + zaddrremaining2, + 'total': node3mined + Decimal('1.0') + zsendmany2notevalue + zsendmanynotevalue + zaddrremaining + zaddrremaining2, + }) + + # Check individual balances reflect the above + assert_equal(self.nodes[3].z_getbalance(mytaddr1), Decimal('1.0')) + assert_equal(self.nodes[3].z_getbalance(myzaddr), zsendmanynotevalue + zaddrremaining + zaddrremaining2) + if __name__ == '__main__': WalletNullifiersTest().main () diff --git a/qa/rpc-tests/wallet_protectcoinbase.py b/qa/rpc-tests/wallet_protectcoinbase.py index ee2e8de1d..25b1e794a 100755 --- a/qa/rpc-tests/wallet_protectcoinbase.py +++ b/qa/rpc-tests/wallet_protectcoinbase.py @@ -7,13 +7,23 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, initialize_chain_clean, \ - start_nodes, connect_nodes_bi, stop_node + start_nodes, connect_nodes_bi, stop_node, wait_and_assert_operationid_status import sys import time import timeit from decimal import Decimal +def check_value_pool(node, name, total): + value_pools = node.getblockchaininfo()['valuePools'] + found = False + for pool in value_pools: + if pool['id'] == name: + found = True + assert_equal(pool['monitored'], True) + assert_equal(pool['chainValue'], total) + assert(found) + class WalletProtectCoinbaseTest (BitcoinTestFramework): def setup_chain(self): @@ -76,6 +86,11 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(self.nodes[2].getbalance(), 0) assert_equal(self.nodes[3].getbalance(), 0) + check_value_pool(self.nodes[0], 'sprout', 0) + check_value_pool(self.nodes[1], 'sprout', 0) + check_value_pool(self.nodes[2], 'sprout', 0) + check_value_pool(self.nodes[3], 'sprout', 0) + # Send will fail because we are enforcing the consensus rule that # coinbase utxos can only be sent to a zaddr. errorString = "" @@ -141,10 +156,11 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal("wallet does not allow any change" in errorString, True) # This send will succeed. We send two coinbase utxos totalling 20.0 less a fee of 0.00010000, with no change. + shieldvalue = Decimal('20.0') - Decimal('0.0001') recipients = [] - recipients.append({"address":myzaddr, "amount": Decimal('20.0') - Decimal('0.0001')}) + recipients.append({"address":myzaddr, "amount": shieldvalue}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - mytxid = self.wait_and_assert_operationid_status(myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -169,11 +185,15 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(Decimal(resp["private"]), Decimal('19.9999')) assert_equal(Decimal(resp["total"]), Decimal('39.9999')) + # The Sprout value pool should reflect the send + sproutvalue = shieldvalue + check_value_pool(self.nodes[0], 'sprout', sproutvalue) + # A custom fee of 0 is okay. Here the node will send the note value back to itself. recipients = [] recipients.append({"address":myzaddr, "amount": Decimal('19.9999')}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0')) - mytxid = self.wait_and_assert_operationid_status(myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -182,11 +202,15 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(Decimal(resp["private"]), Decimal('19.9999')) assert_equal(Decimal(resp["total"]), Decimal('39.9999')) + # The Sprout value pool should be unchanged + check_value_pool(self.nodes[0], 'sprout', sproutvalue) + # convert note to transparent funds + unshieldvalue = Decimal('10.0') recipients = [] - recipients.append({"address":mytaddr, "amount":Decimal('10.0')}) + recipients.append({"address":mytaddr, "amount": unshieldvalue}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - mytxid = self.wait_and_assert_operationid_status(myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) assert(mytxid is not None) self.sync_all() @@ -198,10 +222,12 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): self.sync_all() # check balances + sproutvalue -= unshieldvalue + Decimal('0.0001') resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('30.0')) assert_equal(Decimal(resp["private"]), Decimal('9.9998')) assert_equal(Decimal(resp["total"]), Decimal('39.9998')) + check_value_pool(self.nodes[0], 'sprout', sproutvalue) # z_sendmany will return an error if there is transparent change output considered dust. # UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first. @@ -210,7 +236,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): amount = Decimal('10.0') - Decimal('0.00010000') - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold recipients.append({"address":self.nodes[0].getnewaddress(), "amount":amount }) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid, "failed", "Insufficient transparent funds, have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)") # Send will fail because send amount is too big, even when including coinbase utxos errorString = "" @@ -224,9 +250,9 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): recipients = [] recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000.0')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid, "failed", "Insufficient transparent funds, have 10.00, need 10000.0001") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 10.00, need 10000.0001") myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - self.wait_and_assert_operationid_status(myopid, "failed", "Insufficient protected funds, have 9.9998, need 10000.0001") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient protected funds, have 9.9998, need 10000.0001") # Send will fail because of insufficient funds unless sender uses coinbase utxos try: @@ -263,7 +289,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): myopid = self.nodes[0].z_sendmany(myzaddr, recipients) try: - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) except JSONRPCException as e: print("JSONRPC error: "+e.error['message']) assert(False) @@ -277,7 +303,9 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): # check balance node2balance = amount_per_recipient * num_t_recipients + sproutvalue -= node2balance + Decimal('0.0001') assert_equal(self.nodes[2].getbalance(), node2balance) + check_value_pool(self.nodes[0], 'sprout', sproutvalue) # Send will fail because fee is negative try: @@ -326,7 +354,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): newzaddr = self.nodes[2].z_getnewaddress() recipients.append({"address":newzaddr, "amount":amount_per_recipient}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -336,7 +364,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(Decimal(resp["private"]), send_amount) resp = self.nodes[0].z_getbalance(myzaddr) assert_equal(Decimal(resp), zbalance - custom_fee - send_amount) + sproutvalue -= custom_fee + check_value_pool(self.nodes[0], 'sprout', sproutvalue) if __name__ == '__main__': WalletProtectCoinbaseTest().main() - diff --git a/qa/rpc-tests/wallet_shieldcoinbase.py b/qa/rpc-tests/wallet_shieldcoinbase.py index ce2fbb9d7..31048e163 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase.py +++ b/qa/rpc-tests/wallet_shieldcoinbase.py @@ -7,9 +7,9 @@ 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, sync_blocks + start_node, connect_nodes_bi, sync_blocks, sync_mempools, \ + wait_and_assert_operationid_status -import time from decimal import Decimal class WalletShieldCoinbaseTest (BitcoinTestFramework): @@ -19,11 +19,11 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): initialize_chain_clean(self.options.tmpdir, 4) def setup_network(self, split=False): - args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', '-experimentalfeatures', '-zshieldcoinbase'] + args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe'] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) self.nodes.append(start_node(1, self.options.tmpdir, args)) - args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', '-experimentalfeatures', '-zshieldcoinbase', "-mempooltxinputlimit=7"] + args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"] self.nodes.append(start_node(2, self.options.tmpdir, args2)) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) @@ -31,34 +31,6 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): self.is_network_split=False self.sync_all() - # Returns txid if operation was a success or None - def wait_and_assert_operationid_status(self, nodeid, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - txid = None - for x in xrange(1, timeout): - results = self.nodes[nodeid].z_getoperationresult(opids) - if len(results)==0: - time.sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - elif status == "success": - txid = results[0]['result']['txid'] - break - print('...returned status: {}'.format(status)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - return txid - def run_test (self): print "Mining blocks..." @@ -115,9 +87,23 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): errorString = e.error['message'] assert_equal("Insufficient coinbase funds" in errorString, True) + # Shielding will fail because limit parameter must be at least 0 + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), -1) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Limit on maximum number of utxos cannot be negative" in errorString, True) + + # Shielding will fail because limit parameter is absurdly large + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), 99999999999999) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("JSON integer out of range" in errorString, True) + # Shield coinbase utxos from node 0 of value 40, standard fee of 0.00010000 result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr) - self.wait_and_assert_operationid_status(0, result['opid']) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -131,7 +117,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): # Shield coinbase utxos from any node 2 taddr, and set fee to 0 result = self.nodes[2].z_shieldcoinbase("*", myzaddr, 0) - self.wait_and_assert_operationid_status(2, result['opid']) + wait_and_assert_operationid_status(self.nodes[2], result['opid']) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -152,14 +138,15 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): # Shielding the 800 utxos will occur over two transactions, since max tx size is 100,000 bytes. # We don't verify shieldingValue as utxos are not selected in any specific order, so value can change on each test run. - result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, 0) + # We set an unrealistically high limit parameter of 99999, to verify that max tx size will constrain the number of utxos. + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, 0, 99999) assert_equal(result["shieldingUTXOs"], Decimal('662')) assert_equal(result["remainingUTXOs"], Decimal('138')) remainingValue = result["remainingValue"] opid1 = result['opid'] # Verify that utxos are locked (not available for selection) by queuing up another shielding operation - result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr) + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, 0, 0) assert_equal(result["shieldingValue"], Decimal(remainingValue)) assert_equal(result["shieldingUTXOs"], Decimal('138')) assert_equal(result["remainingValue"], Decimal('0')) @@ -167,24 +154,47 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework): opid2 = result['opid'] # wait for both aysnc operations to complete - self.wait_and_assert_operationid_status(0, opid1) - self.wait_and_assert_operationid_status(0, opid2) + wait_and_assert_operationid_status(self.nodes[0], opid1) + wait_and_assert_operationid_status(self.nodes[0], opid2) # sync_all() invokes sync_mempool() but node 2's mempool limit will cause tx1 and tx2 to be rejected. - # So instead, we sync on blocks, and after a new block is generated, all nodes will have an empty mempool. - sync_blocks(self.nodes) + # So instead, we sync on blocks and mempool for node 0 and node 1, and after a new block is generated + # which mines tx1 and tx2, all nodes will have an empty mempool which can then be synced. + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) self.nodes[1].generate(1) self.sync_all() # Verify maximum number of utxos which node 2 can shield is limited by option -mempooltxinputlimit + # This option is used when the limit parameter is set to 0. mytaddr = self.nodes[2].getnewaddress() - result = self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr, 0) + result = self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001'), 0) assert_equal(result["shieldingUTXOs"], Decimal('7')) assert_equal(result["remainingUTXOs"], Decimal('13')) - self.wait_and_assert_operationid_status(2, result['opid']) + wait_and_assert_operationid_status(self.nodes[2], result['opid']) + self.sync_all() + self.nodes[1].generate(1) self.sync_all() + + # Verify maximum number of utxos which node 0 can shield is set by default limit parameter of 50 + self.nodes[0].generate(200) + self.sync_all() + mytaddr = self.nodes[0].getnewaddress() + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001')) + assert_equal(result["shieldingUTXOs"], Decimal('50')) + assert_equal(result["remainingUTXOs"], Decimal('50')) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + + # Verify maximum number of utxos which node 0 can shield can be set by the limit parameter + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001'), 33) + assert_equal(result["shieldingUTXOs"], Decimal('33')) + assert_equal(result["remainingUTXOs"], Decimal('17')) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) self.nodes[1].generate(1) self.sync_all() if __name__ == '__main__': - WalletShieldCoinbaseTest().main() \ No newline at end of file + WalletShieldCoinbaseTest().main() diff --git a/qa/rpc-tests/wallet_treestate.py b/qa/rpc-tests/wallet_treestate.py index c7fe9bf25..b3edcd7c5 100755 --- a/qa/rpc-tests/wallet_treestate.py +++ b/qa/rpc-tests/wallet_treestate.py @@ -6,7 +6,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, initialize_chain_clean, \ - start_nodes, connect_nodes_bi + start_nodes, connect_nodes_bi, wait_and_assert_operationid_status import time from decimal import Decimal @@ -26,30 +26,6 @@ class WalletTreeStateTest (BitcoinTestFramework): self.is_network_split=False self.sync_all() - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - time.sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - break - print('...returned status: {}'.format(status)) - print('...error msg: {}'.format(errormsg)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - def run_test (self): print "Mining blocks..." @@ -65,17 +41,17 @@ class WalletTreeStateTest (BitcoinTestFramework): recipients = [] recipients.append({"address":myzaddr, "amount":Decimal('10.0') - Decimal('0.0001')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -92,7 +68,7 @@ class WalletTreeStateTest (BitcoinTestFramework): recipients = [] recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('10.0') - Decimal('0.0001')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) # Tx 2 will consume all three notes, which must take at least two joinsplits. This is regardless of # the z_sendmany implementation because there are only two inputs per joinsplit. @@ -115,7 +91,7 @@ class WalletTreeStateTest (BitcoinTestFramework): self.sync_all() # Wait for Tx 2 to be created - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) # Note that a bug existed in v1.0.0-1.0.3 where Tx 2 creation would fail with an error: # "Witness for spendable note does not have same anchor as change input" diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index 37c1db9a5..78128ad49 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -34,6 +34,7 @@ and confirm again balances are correct. """ from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, initialize_chain_clean, \ start_nodes, start_node, connect_nodes, stop_node, \ sync_blocks, sync_mempools @@ -141,6 +142,14 @@ class WalletBackupTest(BitcoinTestFramework): self.nodes[2].backupwallet("walletbak") self.nodes[2].dumpwallet("walletdump") + # Verify dumpwallet cannot overwrite an existing file + try: + self.nodes[2].dumpwallet("walletdump") + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Cannot overwrite existing file" in errorString) + logging.info("More transactions") for i in range(5): self.do_one_round() diff --git a/qa/rpc-tests/zkey_import_export.py b/qa/rpc-tests/zkey_import_export.py new file mode 100644 index 000000000..f6d7af765 --- /dev/null +++ b/qa/rpc-tests/zkey_import_export.py @@ -0,0 +1,189 @@ +#!/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 decimal import Decimal +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_greater_than, start_nodes, initialize_chain_clean, connect_nodes_bi + +import logging +import time +import math + +logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) + + +class ZkeyImportExportTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 5) + + def setup_network(self, split=False): + self.nodes = start_nodes(5, self.options.tmpdir ) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) + connect_nodes_bi(self.nodes,0,4) + self.is_network_split=False + self.sync_all() + + # TODO: Refactor in z_addr test_framework file + # Returns txid if operation was a success or None + def wait_and_assert_operationid_status(self, node, myopid, in_status='success', in_errormsg=None): + print('waiting for async operation {}'.format(myopid)) + opids = [] + opids.append(myopid) + timeout = 300 + status = None + errormsg = None + txid = None + for x in xrange(1, timeout): + results = node.z_getoperationresult(opids) + if len(results)==0: + time.sleep(1) + else: + print("Results", results[0]) + status = results[0]["status"] + if status == "failed": + errormsg = results[0]['error']['message'] + elif status == "success": + txid = results[0]['result']['txid'] + break + print('...returned status: {}'.format(status)) + assert_equal(in_status, status) + if errormsg is not None: + assert(in_errormsg is not None) + assert_equal(in_errormsg in errormsg, True) + print('...returned error: {}'.format(errormsg)) + return txid + + def run_test(self): + [alice, bob, charlie, david, miner] = self.nodes + + def z_send(from_node, from_addr, to_addr, amount): + opid = from_node.z_sendmany(from_addr, [{"address": to_addr, "amount": Decimal(amount)}]) + self.wait_and_assert_operationid_status(from_node, opid) + self.sync_all() + miner.generate(1) + self.sync_all() + + def z_getbalance(node, zaddr): + bal = node.z_getbalance(zaddr) + # Ignore fees for sake of comparison + round_balance = math.ceil(bal*100)/100 + return round_balance + + def verify_utxos(node, amts, zaddr): + amts.sort(reverse=True) + txs = node.z_listreceivedbyaddress(zaddr) + + def cmp_confirmations_high_to_low(a, b): + return cmp(b["amount"], a["amount"]) + + txs.sort(cmp_confirmations_high_to_low) + print("Sorted txs", txs) + print("amts", amts) + + try: + assert_equal(amts, [tx["amount"] for tx in txs]) + except AssertionError: + logging.error( + 'Expected amounts: %r; txs: %r', + amts, txs) + raise + + def get_private_balance(node): + balance = node.z_gettotalbalance() + return balance['private'] + + def find_imported_key(node, import_zaddr): + zaddrs = node.z_listaddresses() + assert(import_zaddr in zaddrs) + return import_zaddr + + # Seed Alice with some funds + alice.generate(10) + self.sync_all() + miner.generate(100) + self.sync_all() + # Shield Alice's coinbase funds to her zaddr + alice_zaddr = alice.z_getnewaddress() + res = alice.z_shieldcoinbase("*", alice_zaddr) + self.wait_and_assert_operationid_status(alice, res['opid']) + miner.generate(6) + self.sync_all() + + # Now get a pristine z-address for receiving transfers: + bob_zaddr = bob.z_getnewaddress() + verify_utxos(bob, [], bob_zaddr) + # TODO: Verify that charlie doesn't have funds in addr + # verify_utxos(charlie, []) + + # the amounts of each txn embodied which generates a single UTXO: + amounts = map(Decimal, ['2.3', '3.7', '0.1', '0.5', '1.0', '0.19']) + + # Internal test consistency assertion: + assert_greater_than( + get_private_balance(alice), + reduce(Decimal.__add__, amounts)) + + logging.info("Sending pre-export txns...") + for amount in amounts[0:2]: + z_send(alice, alice_zaddr, bob_zaddr, amount) + + logging.info("Exporting privkey from bob...") + privkey = bob.z_exportkey(bob_zaddr) + + logging.info("Sending post-export txns...") + for amount in amounts[2:4]: + z_send(alice, alice_zaddr, bob_zaddr, amount) + + print("Bob amounts:", amounts[:4]) + verify_utxos(bob, amounts[:4], bob_zaddr) + # verify_utxos(charlie, []) + + logging.info("Importing privkey into charlie...") + # z_importkey rescan defaults to "whenkeyisnew", so should rescan here + charlie.z_importkey(privkey) + ipk_zaddr = find_imported_key(charlie, bob_zaddr) + + # z_importkey should have rescanned for new key, so this should pass: + verify_utxos(charlie, amounts[:4], ipk_zaddr) + + # Verify idempotent behavior: + charlie.z_importkey(privkey) + ipk_zaddr2 = find_imported_key(charlie, bob_zaddr) + + # amounts should be unchanged + verify_utxos(charlie, amounts[:4], ipk_zaddr2) + + logging.info("Sending post-import txns...") + for amount in amounts[4:]: + z_send(alice, alice_zaddr, bob_zaddr, amount) + + verify_utxos(bob, amounts, bob_zaddr) + verify_utxos(charlie, amounts, ipk_zaddr) + verify_utxos(charlie, amounts, ipk_zaddr2) + + # Try to reproduce zombie balance reported in #1936 + # At generated zaddr, receive ZEC, and send ZEC back out. bob -> alice + for amount in amounts[:2]: + print("Sending amount from bob to alice: ", amount) + z_send(bob, bob_zaddr, alice_zaddr, amount) + + balance = float(sum(amounts) - sum(amounts[:2])) + assert_equal(z_getbalance(bob, bob_zaddr), balance) + + # z_import onto new node "david" (blockchain rescan, default or True?) + david.z_importkey(privkey) + d_ipk_zaddr = find_imported_key(david, bob_zaddr) + + # Check if amt bob spent is deducted for charlie and david + assert_equal(z_getbalance(charlie, ipk_zaddr), balance) + assert_equal(z_getbalance(david, d_ipk_zaddr), balance) + +if __name__ == '__main__': + ZkeyImportExportTest().main() diff --git a/share/ui.rc b/share/ui.rc index 817f18540..c3cece1ef 100644 --- a/share/ui.rc +++ b/share/ui.rc @@ -12,4 +12,4 @@ addressbook16 BITMAP "pixmaps/addressbook16.bmp" addressbook16mask BITMAP "pixmaps/addressbook16mask.bmp" addressbook20 BITMAP "pixmaps/addressbook20.bmp" addressbook20mask BITMAP "pixmaps/addressbook20mask.bmp" -favicon ICON "pixmaps/favicon.ico" \ No newline at end of file +favicon ICON "pixmaps/favicon.ico" diff --git a/zcutil/build-debian-package.sh b/zcutil/build-debian-package.sh index c69e27d95..34617e8e4 100755 --- a/zcutil/build-debian-package.sh +++ b/zcutil/build-debian-package.sh @@ -71,4 +71,4 @@ fakeroot dpkg-deb --build $BUILD_DIR cp $BUILD_PATH/$PACKAGE_NAME-$PACKAGE_VERSION-amd64.deb $SRC_PATH # Analyze with Lintian, reporting bugs and policy violations lintian -i $SRC_PATH/$PACKAGE_NAME-$PACKAGE_VERSION-amd64.deb -exit 0 \ No newline at end of file +exit 0 diff --git a/zcutil/cleanup-tags.sh b/zcutil/cleanup-tags.sh index 915debcef..0d01d2d63 100755 --- a/zcutil/cleanup-tags.sh +++ b/zcutil/cleanup-tags.sh @@ -23,4 +23,4 @@ do [ "$i" -ge "$MAXJOBS" ] && wait -n done -wait \ No newline at end of file +wait diff --git a/zcutil/fetch-params.sh b/zcutil/fetch-params.sh index fce4cee8d..3102488eb 100755 --- a/zcutil/fetch-params.sh +++ b/zcutil/fetch-params.sh @@ -164,4 +164,4 @@ EOF main rm -f /tmp/fetch_params.lock -exit 0 \ No newline at end of file +exit 0 diff --git a/zcutil/make-release.py b/zcutil/make-release.py index 470805b0f..d8838eddf 100755 --- a/zcutil/make-release.py +++ b/zcutil/make-release.py @@ -26,6 +26,7 @@ def main(args=sys.argv[1:]): main_logged( opts.RELEASE_VERSION, opts.RELEASE_PREV, + opts.RELEASE_FROM, opts.RELEASE_HEIGHT, opts.HOTFIX, ) @@ -61,6 +62,11 @@ def parse_args(args): type=Version.parse_arg, help='The previously released version.', ) + p.add_argument( + 'RELEASE_FROM', + type=Version.parse_arg, + help='The previously released non-beta non-RC version. May be the same as RELEASE_PREV.', + ) p.add_argument( 'RELEASE_HEIGHT', type=int, @@ -70,8 +76,8 @@ def parse_args(args): # Top-level flow: -def main_logged(release, releaseprev, releaseheight, hotfix): - verify_releaseprev_tag(releaseprev) +def main_logged(release, releaseprev, releasefrom, releaseheight, hotfix): + verify_tags(releaseprev, releasefrom) verify_version(release, releaseprev, hotfix) initialize_git(release, hotfix) patch_version_in_files(release, releaseprev) @@ -82,7 +88,7 @@ def main_logged(release, releaseprev, releaseheight, hotfix): gen_manpages() commit('Updated manpages for {}.'.format(release.novtext)) - gen_release_notes(release) + gen_release_notes(release, releasefrom) update_debian_changelog(release) commit( 'Updated release notes and changelog for {}.'.format( @@ -101,8 +107,8 @@ def phase(message): return deco -@phase('Checking RELEASE_PREV tag.') -def verify_releaseprev_tag(releaseprev): +@phase('Checking tags.') +def verify_tags(releaseprev, releasefrom): candidates = [] # Any tag beginning with a 'v' followed by [1-9] must be a version @@ -130,6 +136,31 @@ def verify_releaseprev_tag(releaseprev): ), ) + candidates.reverse() + prev_tags = [] + for candidate in candidates: + if releasefrom == candidate: + break + else: + prev_tags.append(candidate) + else: + raise SystemExit( + '{} does not appear in `git tag --list`' + .format( + releasefrom.vtext, + ), + ) + + for tag in prev_tags: + if not tag.betarc: + raise SystemExit( + '{} appears to be a more recent non-beta non-RC release than {}' + .format( + tag.vtext, + releasefrom.vtext, + ), + ) + @phase('Checking version.') def verify_version(release, releaseprev, hotfix): @@ -238,8 +269,18 @@ def gen_manpages(): @phase('Generating release notes.') -def gen_release_notes(release): - sh_log('python', './zcutil/release-notes.py', '--version', release.novtext) +def gen_release_notes(release, releasefrom): + release_notes = [ + 'python', + './zcutil/release-notes.py', + '--version', + release.novtext, + '--prev', + releasefrom.vtext, + ] + if not release.betarc: + release_notes.append('--clear') + sh_log(*release_notes) sh_log( 'git', 'add', @@ -625,4 +666,4 @@ if __name__ == '__main__': sys.argv = actualargs print '=== Running ===' - main() \ No newline at end of file + main() diff --git a/zcutil/release-notes.py b/zcutil/release-notes.py index cbeacc0b9..01f658f07 100755 --- a/zcutil/release-notes.py +++ b/zcutil/release-notes.py @@ -70,6 +70,10 @@ def document_authors(): f.write('Zcash Contributors\n==================\n\n') total_contrib = {} for notes in os.listdir(os.path.join(doc_dir, 'release-notes')): + # Commits are duplicated across beta, RC and final release notes, + # except for the pre-launch release notes. + if ('-beta' in notes or '-rc' in notes) and '1.0.0-' not in notes: + continue authors = authors_in_release_notes(notes) for author in authors: commits = int(authors[author]) @@ -83,11 +87,15 @@ def document_authors(): f.write("{0} ({1})\n".format(n, c)) ## Writes release note to ./doc/release-notes based on git shortlog when current version number is specified -def generate_release_note(version, filename): +def generate_release_note(version, prev, clear): + filename = 'release-notes-{0}.md'.format(version) print "Automatically generating release notes for {0} from git shortlog. Should review {1} for accuracy.".format(version, filename) - # fetches latest tags, so that latest_tag will be correct - subprocess.Popen(['git fetch -t'], shell=True, stdout=subprocess.PIPE).communicate()[0] - latest_tag = subprocess.Popen(['git describe --abbrev=0'], shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + if prev: + latest_tag = prev + else: + # fetches latest tags, so that latest_tag will be correct + subprocess.Popen(['git fetch -t'], shell=True, stdout=subprocess.PIPE).communicate()[0] + latest_tag = subprocess.Popen(['git describe --abbrev=0'], shell=True, stdout=subprocess.PIPE).communicate()[0].strip() print "Previous release tag: ", latest_tag notes = subprocess.Popen(['git shortlog --no-merges {0}..HEAD'.format(latest_tag)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[0] lines = notes.split('\n') @@ -100,30 +108,35 @@ def generate_release_note(version, filename): notable_changes = notable_changes[3:] + ['\n'] else: notable_changes = [] - release_note = os.path.join(doc_dir, 'release-notes', 'release-notes-{0}.md'.format(version)) + release_note = os.path.join(doc_dir, 'release-notes', filename) with open(release_note, 'w') as f: f.writelines(notable_changes) f.writelines(RELEASE_NOTES_CHANGELOG_HEADING) f.writelines('\n'.join(lines)) - # Clear temporary release notes file - with open(temp_release_note, 'w') as f: - f.writelines(TEMP_RELEASE_NOTES_HEADER) + if clear: + # Clear temporary release notes file + with open(temp_release_note, 'w') as f: + f.writelines(TEMP_RELEASE_NOTES_HEADER) -def main(version, filename): +def main(version, prev, clear): if version != None: - generate_release_note(version, filename) + generate_release_note(version, prev, clear) document_authors() if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--version') + parser.add_argument('--version', help='Upcoming version, without leading v') + parser.add_argument('--prev', help='Previous version, with leading v') + parser.add_argument('--clear', help='Wipe doc/release-notes.md', action='store_true') args = parser.parse_args() root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) doc_dir = os.path.join(root_dir, 'doc') version = None - filename = None + prev = None + clear = False if args.version: version = args.version - filename = 'release-notes-{0}.md'.format(version) - main(version, filename) \ No newline at end of file + prev = args.prev + clear = args.clear + main(version, prev, clear)