From 2f3698aa3aa1530870b70497ca52d71a1490266f Mon Sep 17 00:00:00 2001 From: mdr0id Date: Thu, 9 Sep 2021 14:57:20 -0700 Subject: [PATCH 001/514] Remove sprout funding flow logic --- qa/zcash/smoke_tests.py | 249 ++++++++-------------------------------- 1 file changed, 49 insertions(+), 200 deletions(-) diff --git a/qa/zcash/smoke_tests.py b/qa/zcash/smoke_tests.py index 9298873a719..cd42bf4904d 100755 --- a/qa/zcash/smoke_tests.py +++ b/qa/zcash/smoke_tests.py @@ -36,49 +36,49 @@ ('2a', True, True), # Run getinfo ('2b', True, True), # Run help # Address generation - ('3a', True, True), # Generate a Sprout z-addr - ('3b', True, True), # Generate multiple Sprout z-addrs + #('3a', True, True), # Generate a Sprout z-addr + #('3b', True, True), # Generate multiple Sprout z-addrs ('3c', True, True), # Generate a t-addr ('3d', True, True), # Generate multiple t-addrs ('3e', True, True), # Generate a Sapling z-addr ('3f', True, True), # Generate multiple Sapling z-addrs # Transactions - ('4a', True, True ), # Send funds from Sprout z-addr to same Sprout z-addr - ('4b', True, True ), # Send funds from Sprout z-addr to a different Sprout z-addr - ('4c', True, True ), # Send funds from Sprout z-addr to a t-addr - ('4d', False, False ), # Send funds from t-addr to Sprout z-addr(expected fail as of Canopy) + #('4a', True, True ), # Send funds from Sprout z-addr to same Sprout z-addr + #('4b', True, True ), # Send funds from Sprout z-addr to a different Sprout z-addr + #('4c', True, True ), # Send funds from Sprout z-addr to a t-addr + #('4d', False, False ), # Send funds from t-addr to Sprout z-addr(expected fail as of Canopy) ('4e', True, True ), # Send funds from t-addr to t-addr ('4f', True, True ), # Send funds from t-addr to Sapling z-addr ('4g', True, True ), # Send funds from Sapling z-addr to same Sapling z-addr ('4h', True, True ), # Send funds from Sapling z-addr to a different Sapling z-addr ('4i', True, True ), # Send funds from Sapling z-addr to a t-addr - ('4j', False, False), # Send funds from Sprout z-addr to Sapling z-addr - ('4k', True, True ), # Send funds from Sprout z-addr to multiple Sprout z-addrs - ('4l', True, True ), # Send funds from Sprout z-addr to multiple t-addrs - ('4m', True, True ), # Send funds from Sprout z-addr to t-addr and Sprout z-addrs - ('4n', False, False), # Send funds from Sprout z-addr to t-addr and Sapling z-addr - ('4o', False, False), # Send funds from Sprout z-addr to multiple Sapling z-addrs + #('4j', False, False), # Send funds from Sprout z-addr to Sapling z-addr + #('4k', True, True ), # Send funds from Sprout z-addr to multiple Sprout z-addrs + #('4l', True, True ), # Send funds from Sprout z-addr to multiple t-addrs + #('4m', True, True ), # Send funds from Sprout z-addr to t-addr and Sprout z-addrs + #('4n', False, False), # Send funds from Sprout z-addr to t-addr and Sapling z-addr + #('4o', False, False), # Send funds from Sprout z-addr to multiple Sapling z-addrs ('4p', True, True ), # Send funds from t-addr to multiple t-addrs - ('4q', False, False ), # Send funds from t-addr to multiple Sprout z-addrs(expected fail as of Canopy) + #('4q', False, False ), # Send funds from t-addr to multiple Sprout z-addrs(expected fail as of Canopy) ('4r', True, True ), # Send funds from t-addr to multiple Sapling z-addrs - ('4s', False, False), # Send funds from t-addr to Sprout z-addr and Sapling z-addr + #('4s', False, False), # Send funds from t-addr to Sprout z-addr and Sapling z-addr ('4t', True, True ), # Send funds from Sapling z-addr to multiple Sapling z-addrs - ('4u', False, False), # Send funds from Sapling z-addr to multiple Sprout z-addrs + #('4u', False, False), # Send funds from Sapling z-addr to multiple Sprout z-addrs ('4v', True, True ), # Send funds from Sapling z-addr to multiple t-addrs ('4w', True, True ), # Send funds from Sapling z-addr to t-addr and Sapling z-addr - ('4x', False, False), # Send funds from Sapling z-addr to Sapling z-addr and Sprout z-addr - ('4y', False, False ), # Send funds from t-addr to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) - ('4z', False, False ), # Send funds from 2 different t-addrs to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) - ('4aa', False, False), # Send funds from the same 2 t-addrs to Sprout z-addr using z_mergetoaddress + #('4x', False, False), # Send funds from Sapling z-addr to Sapling z-addr and Sprout z-addr + #('4y', False, False ), # Send funds from t-addr to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) + #('4z', False, False ), # Send funds from 2 different t-addrs to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) + #('4aa', False, False), # Send funds from the same 2 t-addrs to Sprout z-addr using z_mergetoaddress ('4bb', True, True ), # Send funds from 2 different t-addrs to Sapling z-addr using z_mergetoaddress ('4cc', True, True ), # Send funds from t-addr to Sapling z-addr using z_mergetoaddress - ('4dd', False, False ), # Send funds from t-addr and Sprout z-addr to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) + #('4dd', False, False ), # Send funds from t-addr and Sprout z-addr to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) ('4ee', True, True ), # Send funds from t-addr and Sapling z-addr to Sapling z-addr using z_mergetoaddress - ('4ff', True, True ), # Send funds from Sprout z-addr and Sprout z-addr to Sprout z-addr using z_mergetoaddress + #('4ff', True, True ), # Send funds from Sprout z-addr and Sprout z-addr to Sprout z-addr using z_mergetoaddress ('4gg', True, True ), # Send funds from Sapling z-addr and Sapling z-addr to Sapling z-addr using z_mergetoaddress # Wallet ('5a', True, True), # After generating multiple z-addrs, run z_listaddresses - ('5b', True, True), # Run z_validateaddress with a Sprout z-addr + #('5b', True, True), # Run z_validateaddress with a Sprout z-addr ('5c', True, True), # Run z_validateaddress with a Sapling z-addr ('5d', True, True), # After a transaction, run z_listunspent ('5e', True, True), # After a transaction, run z_listreceivedbyaddress @@ -308,9 +308,6 @@ def transaction_chain(zcash): results = {} # Generate the various addresses we will use - sprout_zaddr_1 = run_cmd(results, '3a', zcash, 'z_getnewaddress', ['sprout']) - sprout_zaddr_2 = run_cmd(results, '3b', zcash, 'z_getnewaddress', ['sprout']) - sprout_zaddr_3 = run_cmd(results, '3b', zcash, 'z_getnewaddress', ['sprout']) taddr_1 = run_cmd(results, '3c', zcash, 'getnewaddress') taddr_2 = run_cmd(results, '3d', zcash, 'getnewaddress') taddr_3 = run_cmd(results, '3d', zcash, 'getnewaddress') @@ -322,17 +319,11 @@ def transaction_chain(zcash): # Check that the zaddrs are all listed zaddrs = run_cmd(results, '5a', zcash, 'z_listaddresses') - if (sprout_zaddr_1 not in zaddrs or - sprout_zaddr_2 not in zaddrs or - sapling_zaddr_1 not in zaddrs or + if ( sapling_zaddr_1 not in zaddrs or sapling_zaddr_2 not in zaddrs): results['5a'] = False # Validate the addresses - ret = run_cmd(results, '5b', zcash, 'z_validateaddress', [sprout_zaddr_1]) - if not ret['isvalid'] or ret['type'] != 'sprout': - results['5b'] = False - ret = run_cmd(results, '5c', zcash, 'z_validateaddress', [sapling_zaddr_1]) if not ret['isvalid'] or ret['type'] != 'sapling': results['5c'] = False @@ -345,7 +336,7 @@ def transaction_chain(zcash): if zcash.use_faucet: print('Tapping the testnet faucet for testing funds...') chain_end = get_zfaucet_taddr() - tap_zfaucet(sprout_zaddr_1) + tap_zfaucet(taddr_1) print('Done! Leftover funds will be sent back to the faucet.') else: chain_end = input('Type or paste transparent address where leftover funds should be sent: ') @@ -354,14 +345,14 @@ def transaction_chain(zcash): return results print() print('Please send at least 0.01 ZEC/TAZ to the following address:') - print(sprout_zaddr_1) + print(taddr_1) print() input('Press Enter once the funds have been sent.') print() # Wait to receive starting balance - sprout_balance = wait_for_balance(zcash, sprout_zaddr_1) - starting_balance = sprout_balance + taddr_balance = wait_for_balance(zcash, taddr_1) + starting_balance = taddr_balance # # Start the transaction chain! @@ -372,19 +363,6 @@ def transaction_chain(zcash): print('#') print() try: - # - # First, split the funds across all three pools - # - - # Sprout -> taddr - taddr_balance = check_z_sendmany( - results, '4c', zcash, sprout_zaddr_1, [(taddr_1, (starting_balance / Decimal('10')) * Decimal('6'))])[0] - sprout_balance -= taddr_balance + DEFAULT_FEE - - balance = Decimal(run_cmd(results, '5f', zcash, 'z_getbalance', [sprout_zaddr_1])).quantize(Decimal('1.00000000')) - if balance != sprout_balance: - results['5f'] = False - # taddr -> Sapling # Send it all here because z_sendmany pick a new t-addr for change sapling_balance = check_z_sendmany( @@ -400,30 +378,20 @@ def transaction_chain(zcash): # Intra-pool tests # - # Sprout -> same Sprout # Sapling -> same Sapling - (sprout_balance, sapling_balance) = check_z_sendmany_parallel(results, zcash, [ - ('4a', sprout_zaddr_1, [(sprout_zaddr_1, sprout_balance - DEFAULT_FEE)]), - ('4g', sapling_zaddr_1, [(sapling_zaddr_1, sapling_balance - DEFAULT_FEE)]), - ]) + sapling_balance = check_z_sendmany( + results, '4g',zcash, sapling_zaddr_1, [(sapling_zaddr_1, sapling_balance - DEFAULT_FEE)])[0] - # Sprout -> different Sprout # taddr -> different taddr # Sapling -> different Sapling - (sprout_balance, taddr_balance, sapling_balance) = check_z_sendmany_parallel(results, zcash, [ - ('4b', sprout_zaddr_1, [(sprout_zaddr_2, sprout_balance - DEFAULT_FEE)]), + (taddr_balance, sapling_balance) = check_z_sendmany_parallel(results, zcash, [ ('4e', taddr_1, [(taddr_2, taddr_balance - DEFAULT_FEE)]), ('4h', sapling_zaddr_1, [(sapling_zaddr_2, sapling_balance - DEFAULT_FEE)]), ]) - # Sprout -> multiple Sprout # taddr -> multiple taddr # Sapling -> multiple Sapling check_z_sendmany_parallel(results, zcash, [ - ('4k', sprout_zaddr_2, [ - (sprout_zaddr_1, starting_balance / Decimal('10')), - (sprout_zaddr_3, starting_balance / Decimal('10')), - ]), ('4p', taddr_2, [ (taddr_1, starting_balance / Decimal('10')), (taddr_3, taddr_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE), @@ -433,193 +401,75 @@ def transaction_chain(zcash): (sapling_zaddr_3, starting_balance / Decimal('10')), ]), ]) - sprout_balance -= DEFAULT_FEE + taddr_balance -= DEFAULT_FEE sapling_balance -= DEFAULT_FEE - # multiple Sprout -> Sprout # multiple Sapling -> Sapling # multiple taddr -> taddr check_z_mergetoaddress_parallel(results, zcash, [ - ('4ff', [sprout_zaddr_1, sprout_zaddr_3], sprout_zaddr_2, sprout_balance - DEFAULT_FEE), ('4gg', [sapling_zaddr_1, sapling_zaddr_3], sapling_zaddr_2, sapling_balance - DEFAULT_FEE), ('', [taddr_1, taddr_3], taddr_2, taddr_balance - DEFAULT_FEE), ]) - sprout_balance -= DEFAULT_FEE sapling_balance -= DEFAULT_FEE taddr_balance -= DEFAULT_FEE - # - # Now test a bunch of failing cases - # - - # Sprout -> Sapling - txid = z_sendmany(results, '4j', zcash, sprout_zaddr_2, [(sapling_zaddr_1, sprout_balance - DEFAULT_FEE)]) - if txid is not None: - print('Should have failed') - return results - - # Sprout -> taddr and Sapling - txid = z_sendmany(results, '4n', zcash, sprout_zaddr_2, [ - (taddr_2, starting_balance / Decimal('10')), - (sapling_zaddr_1, starting_balance / Decimal('10')), - ]) - if txid is not None: - print('Should have failed') - return results - - # Sprout -> multiple Sapling - txid = z_sendmany(results, '4o', zcash, sprout_zaddr_2, [ - (sapling_zaddr_1, starting_balance / Decimal('10')), - (sapling_zaddr_2, starting_balance / Decimal('10')), - ]) - if txid is not None: - print('Should have failed') - return results - - # taddr -> Sprout and Sapling - txid = z_sendmany(results, '4s', zcash, taddr_2, [ - (sprout_zaddr_1, starting_balance / Decimal('10')), - (sapling_zaddr_1, starting_balance / Decimal('10')), - ]) - if txid is not None: - print('Should have failed') - return results - - # Sapling -> multiple Sprout - txid = z_sendmany(results, '4u', zcash, sapling_zaddr_2, [ - (sprout_zaddr_1, starting_balance / Decimal('10')), - (sprout_zaddr_2, starting_balance / Decimal('10')), - ]) - if txid is not None: - print('Should have failed') - return results - - # Sapling -> Sapling and Sprout - txid = z_sendmany(results, '4x', zcash, sapling_zaddr_2, [ - (sapling_zaddr_1, starting_balance / Decimal('10')), - (sprout_zaddr_1, starting_balance / Decimal('10')), - ]) - if txid is not None: - print('Should have failed') - return results - - # multiple same taddr -> Sprout - txid = z_mergetoaddress(results, '4aa', zcash, [taddr_2, taddr_2], sprout_zaddr_2) - if txid is not None: - print('Should have failed') - return results - # # Inter-pool tests # - # Sprout -> taddr and Sprout # Sapling -> taddr and Sapling - check_z_sendmany_parallel(results, zcash, [ - ('4m', sprout_zaddr_2, [ - (taddr_1, starting_balance / Decimal('10')), - (sprout_zaddr_1, starting_balance / Decimal('10')), - ]), - ('4w', sapling_zaddr_2, [ + check_z_sendmany( + results, '4w', zcash, sapling_zaddr_2,[ (taddr_3, starting_balance / Decimal('10')), - (sapling_zaddr_1, starting_balance / Decimal('10')), - ]), - ]) - sprout_balance -= (starting_balance / Decimal('10')) + DEFAULT_FEE + (sapling_zaddr_1, starting_balance / Decimal('10'))])[0] + sapling_balance -= (starting_balance / Decimal('10')) + DEFAULT_FEE taddr_balance += (starting_balance / Decimal('10')) * Decimal('2') - # taddr and Sprout -> Sprout # taddr and Sapling -> Sapling - check_z_mergetoaddress_parallel(results, zcash, [ - ('4dd', [taddr_1, sprout_zaddr_1], sprout_zaddr_2, sprout_balance + (starting_balance / Decimal('10')) - DEFAULT_FEE), - ('4ee', [taddr_3, sapling_zaddr_1], sapling_zaddr_2, sapling_balance + (starting_balance / Decimal('10')) - DEFAULT_FEE), - ]) - sprout_balance += (starting_balance / Decimal('10')) - DEFAULT_FEE + check_z_mergetoaddress(results, '4ee', zcash, [taddr_3, sapling_zaddr_1], sapling_zaddr_2, sapling_balance + (starting_balance / Decimal('10')) - DEFAULT_FEE) + sapling_balance += (starting_balance / Decimal('10')) - DEFAULT_FEE taddr_balance -= (starting_balance / Decimal('10')) * Decimal('2') - # Sprout -> multiple taddr # Sapling -> multiple taddr - check_z_sendmany_parallel(results, zcash, [ - ('4l', sprout_zaddr_2, [ - (taddr_1, (starting_balance / Decimal('10'))), - (taddr_3, (starting_balance / Decimal('10'))), - ]), - ('4v', sapling_zaddr_2, [ + check_z_sendmany(results, '4v', zcash, sapling_zaddr_2, [ (taddr_4, (starting_balance / Decimal('10'))), - (taddr_5, (starting_balance / Decimal('10'))), - ]), - ]) - sprout_balance -= ((starting_balance / Decimal('10')) * Decimal('2')) + DEFAULT_FEE + (taddr_5, (starting_balance / Decimal('10')))])[0] + sapling_balance -= ((starting_balance / Decimal('10')) * Decimal('2')) + DEFAULT_FEE taddr_balance += (starting_balance / Decimal('10')) * Decimal('4') - # multiple taddr -> Sprout # multiple taddr -> Sapling - check_z_mergetoaddress_parallel(results, zcash, [ - ('4z', [taddr_1, taddr_3], sprout_zaddr_2, sprout_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE), - ('4bb', [taddr_4, taddr_5], sapling_zaddr_2, sapling_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE), - ]) - sprout_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE + check_z_mergetoaddress(results, '4bb',zcash, [taddr_4, taddr_5], sapling_zaddr_2, sapling_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE) sapling_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE taddr_balance -= (starting_balance / Decimal('10')) * Decimal('4') - # taddr -> Sprout - check_z_sendmany_parallel(results, zcash, [ - ('4d', taddr_2, [(sprout_zaddr_3, taddr_balance - DEFAULT_FEE)]), - ]) - sprout_balance += taddr_balance - DEFAULT_FEE taddr_balance = Decimal('0') - # multiple Sprout -> taddr # multiple Sapling -> taddr - check_z_mergetoaddress_parallel(None, zcash, [ - ('', [sprout_zaddr_1, sprout_zaddr_2, sprout_zaddr_3], taddr_1, sprout_balance - DEFAULT_FEE), - ('', [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, sapling_balance - DEFAULT_FEE), - ]) - taddr_balance = sprout_balance + sapling_balance - (2 * DEFAULT_FEE) - sprout_balance = Decimal('0') + check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, sapling_balance - DEFAULT_FEE) + taddr_balance = sapling_balance - (2 * DEFAULT_FEE) sapling_balance = Decimal('0') - # taddr -> multiple Sprout # taddr -> multiple Sapling - taddr_1_balance = Decimal(zcash.z_getbalance(taddr_1)).quantize(Decimal('1.00000000')) taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) - check_z_sendmany_parallel(results, zcash, [ - ('4q', taddr_1, [ - (sprout_zaddr_1, (starting_balance / Decimal('10'))), - (sprout_zaddr_2, taddr_1_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE), - ]), - ('4r', taddr_2, [ + check_z_sendmany(results, '4r', zcash, taddr_2, [ (sapling_zaddr_1, (starting_balance / Decimal('10'))), - (sapling_zaddr_2, taddr_2_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE), - ]), - ]) - sprout_balance = taddr_1_balance - DEFAULT_FEE + (sapling_zaddr_2, taddr_2_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE)])[0] + sapling_balance = taddr_2_balance - DEFAULT_FEE taddr_balance = Decimal('0') - # multiple Sprout -> taddr # multiple Sapling -> taddr - check_z_mergetoaddress_parallel(None, zcash, [ - ('', [sprout_zaddr_1, sprout_zaddr_2], taddr_1, sprout_balance - DEFAULT_FEE), - ('', [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - DEFAULT_FEE), - ]) - taddr_balance = sprout_balance + sapling_balance - (Decimal('2') * DEFAULT_FEE) - sprout_balance = Decimal('0') + check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - DEFAULT_FEE) + taddr_balance = sapling_balance - (Decimal('2') * DEFAULT_FEE) sapling_balance = Decimal('0') - # z_mergetoaddress taddr -> Sprout # z_mergetoaddress taddr -> Sapling - taddr_1_balance = Decimal(zcash.z_getbalance(taddr_1)).quantize(Decimal('1.00000000')) taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) - check_z_mergetoaddress_parallel(results, zcash, [ - ('4y', [taddr_1], sprout_zaddr_1, taddr_1_balance - DEFAULT_FEE), - ('4cc', [taddr_2], sapling_zaddr_1, taddr_2_balance - DEFAULT_FEE), - ]) - sprout_balance = taddr_1_balance - DEFAULT_FEE + check_z_mergetoaddress(results, '4cc', zcash, [taddr_2], sapling_zaddr_1, taddr_2_balance - DEFAULT_FEE) sapling_balance = taddr_2_balance - DEFAULT_FEE taddr_balance = Decimal('0') except Exception as e: @@ -634,7 +484,6 @@ def transaction_chain(zcash): print('# Finishing transaction chain') print('#') all_addrs = [ - sprout_zaddr_1, sprout_zaddr_2, sprout_zaddr_3, taddr_1, taddr_2, taddr_3, taddr_4, taddr_5, sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3, ] From 4721ee3eaa45a846d99656d8957008a9406371d7 Mon Sep 17 00:00:00 2001 From: mdr0id Date: Thu, 9 Sep 2021 14:58:30 -0700 Subject: [PATCH 002/514] Add fix and note for timing issue --- qa/zcash/smoke_tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qa/zcash/smoke_tests.py b/qa/zcash/smoke_tests.py index cd42bf4904d..09da1f9c2b0 100755 --- a/qa/zcash/smoke_tests.py +++ b/qa/zcash/smoke_tests.py @@ -166,6 +166,8 @@ def wait_for_balance(zcash, zaddr, expected=None): ttl = 300 def wait_and_check_balance(results, case, zcash, addr, expected): + #Wait for aysnc call to finish and persist completely to caller + time.sleep(5) balance = wait_for_balance(zcash, addr, expected) if balance != expected and results is not None and len(case) > 0: results[case] = False From 7d7d0e8565feac00df1faaf326ce93f61c2d7cf2 Mon Sep 17 00:00:00 2001 From: mdr0id Date: Fri, 10 Sep 2021 19:46:33 -0700 Subject: [PATCH 003/514] Update funding logic bug --- qa/zcash/smoke_tests.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qa/zcash/smoke_tests.py b/qa/zcash/smoke_tests.py index 09da1f9c2b0..9628a34370a 100755 --- a/qa/zcash/smoke_tests.py +++ b/qa/zcash/smoke_tests.py @@ -448,11 +448,9 @@ def transaction_chain(zcash): sapling_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE taddr_balance -= (starting_balance / Decimal('10')) * Decimal('4') - taddr_balance = Decimal('0') - # multiple Sapling -> taddr check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, sapling_balance - DEFAULT_FEE) - taddr_balance = sapling_balance - (2 * DEFAULT_FEE) + taddr_balance += sapling_balance - (2 * DEFAULT_FEE) sapling_balance = Decimal('0') # taddr -> multiple Sapling @@ -462,18 +460,18 @@ def transaction_chain(zcash): (sapling_zaddr_2, taddr_2_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE)])[0] sapling_balance = taddr_2_balance - DEFAULT_FEE - taddr_balance = Decimal('0') + taddr_balance -= taddr_2_balance # multiple Sapling -> taddr check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - DEFAULT_FEE) - taddr_balance = sapling_balance - (Decimal('2') * DEFAULT_FEE) + taddr_balance += sapling_balance - (Decimal('2') * DEFAULT_FEE) sapling_balance = Decimal('0') # z_mergetoaddress taddr -> Sapling taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) check_z_mergetoaddress(results, '4cc', zcash, [taddr_2], sapling_zaddr_1, taddr_2_balance - DEFAULT_FEE) sapling_balance = taddr_2_balance - DEFAULT_FEE - taddr_balance = Decimal('0') + taddr_balance -= taddr_2_balance except Exception as e: print('Error: %s' % e) traceback.print_exc() From b6987844f8ed7d39b3f09d6b4a913f9ce76bda0c Mon Sep 17 00:00:00 2001 From: mdr0id Date: Fri, 10 Sep 2021 19:49:52 -0700 Subject: [PATCH 004/514] Add usage documentation for manual and faucet driven tests --- qa/zcash/smoke_tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qa/zcash/smoke_tests.py b/qa/zcash/smoke_tests.py index 9628a34370a..e5263d297a4 100755 --- a/qa/zcash/smoke_tests.py +++ b/qa/zcash/smoke_tests.py @@ -2,6 +2,12 @@ # # Execute the standard smoke tests for Zcash releases. # +# Usage: +# +# ZCASHD=./src/zcashd ZCASHCLI=./src/zcash-cli ./qa/zcash/smoke_tests.py --wallet=wallet.smoketest.dat "$HOME/.zcash" +# +# ZCASHD=./src/zcashd ZCASHCLI=./src/zcash-cli ./qa/zcash/smoke_tests.py --wallet=wallet.smoketest.dat "$HOME/.zcash" --automate --use-faucet +# import argparse import datetime From dc41c13593c937d028a0098f13df246e82d98787 Mon Sep 17 00:00:00 2001 From: mdr0id Date: Mon, 13 Sep 2021 09:54:29 -0700 Subject: [PATCH 005/514] Update funding logic --- qa/zcash/smoke_tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qa/zcash/smoke_tests.py b/qa/zcash/smoke_tests.py index e5263d297a4..5d214b20849 100755 --- a/qa/zcash/smoke_tests.py +++ b/qa/zcash/smoke_tests.py @@ -433,13 +433,13 @@ def transaction_chain(zcash): (sapling_zaddr_1, starting_balance / Decimal('10'))])[0] sapling_balance -= (starting_balance / Decimal('10')) + DEFAULT_FEE - taddr_balance += (starting_balance / Decimal('10')) * Decimal('2') + taddr_balance += starting_balance / Decimal('10') # taddr and Sapling -> Sapling check_z_mergetoaddress(results, '4ee', zcash, [taddr_3, sapling_zaddr_1], sapling_zaddr_2, sapling_balance + (starting_balance / Decimal('10')) - DEFAULT_FEE) sapling_balance += (starting_balance / Decimal('10')) - DEFAULT_FEE - taddr_balance -= (starting_balance / Decimal('10')) * Decimal('2') + taddr_balance -= starting_balance / Decimal('10') # Sapling -> multiple taddr check_z_sendmany(results, '4v', zcash, sapling_zaddr_2, [ @@ -447,16 +447,16 @@ def transaction_chain(zcash): (taddr_5, (starting_balance / Decimal('10')))])[0] sapling_balance -= ((starting_balance / Decimal('10')) * Decimal('2')) + DEFAULT_FEE - taddr_balance += (starting_balance / Decimal('10')) * Decimal('4') + taddr_balance += (starting_balance / Decimal('10')) * Decimal('2') # multiple taddr -> Sapling check_z_mergetoaddress(results, '4bb',zcash, [taddr_4, taddr_5], sapling_zaddr_2, sapling_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE) sapling_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE - taddr_balance -= (starting_balance / Decimal('10')) * Decimal('4') + taddr_balance -= (starting_balance / Decimal('10')) * Decimal('2') # multiple Sapling -> taddr check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, sapling_balance - DEFAULT_FEE) - taddr_balance += sapling_balance - (2 * DEFAULT_FEE) + taddr_balance += sapling_balance - DEFAULT_FEE sapling_balance = Decimal('0') # taddr -> multiple Sapling @@ -470,7 +470,7 @@ def transaction_chain(zcash): # multiple Sapling -> taddr check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - DEFAULT_FEE) - taddr_balance += sapling_balance - (Decimal('2') * DEFAULT_FEE) + taddr_balance += sapling_balance - DEFAULT_FEE sapling_balance = Decimal('0') # z_mergetoaddress taddr -> Sapling From dffeb7df046fb15cb74d1569c166273ac86922eb Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Tue, 14 Sep 2021 10:15:35 +0100 Subject: [PATCH 006/514] Update authors of librustzcash to everyone currently and formerly on Core team (in alphabetical order). Signed-off-by: Daira Hopwood --- Cargo.toml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6be4d0aa848..d440ca91bbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,15 @@ name = "librustzcash" description = "Rust FFI used by the zcashd binary. Not an official API." version = "0.2.0" authors = [ - "Sean Bowe ", - "Jack Grigg ", - "Jay Graber ", - "Simon Liu " + "Sean Bowe ", + "Jay Graber", + "Jack Grigg ", + "Daira Hopwood ", + "Ying Tong Lai ", + "Simon Liu", + "Kris Nuttycombe ", + "Larry Ruane ", + "Steven Smith " ] homepage = "https://github.com/zcash/zcash" repository = "https://github.com/zcash/zcash" From 6e562a27df49fc5b64fe9715d750b0bb334cd5d2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 14 Sep 2021 17:33:25 +0100 Subject: [PATCH 007/514] Postpone dependency updates that require CMake --- qa/zcash/postponed-updates.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index b3878d21213..39fdf300919 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -5,11 +5,13 @@ # # Ccache 4.0 requires adding CMake to the depends system. -native_ccache 4.0 2021-09-01 -native_ccache 4.1 2021-09-01 -native_ccache 4.2 2021-09-01 -native_ccache 4.2.1 2021-09-01 -native_ccache 4.3 2021-09-01 +native_ccache 4.0 2021-10-01 +native_ccache 4.1 2021-10-01 +native_ccache 4.2 2021-10-01 +native_ccache 4.2.1 2021-10-01 +native_ccache 4.3 2021-10-01 +native_ccache 4.4 2021-10-01 +native_ccache 4.4.1 2021-10-01 # Clang is currently pinned to LLVM 12 @@ -27,5 +29,5 @@ native_b2 1.75.0 2021-09-01 native_b2 1.76.0 2021-09-01 # Google Test 1.10.0 requires adding CMake to the depends system. -googletest 1.10.0 2021-09-01 -googletest 1.11.0 2021-09-01 +googletest 1.10.0 2021-10-01 +googletest 1.11.0 2021-10-01 From 966e285b61c5a1a481a37cf3bb989748dfeeb736 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 14 Sep 2021 17:40:18 +0100 Subject: [PATCH 008/514] depends: Update Rust to 1.54.0 --- depends/packages/native_rust.mk | 17 ++++++++--------- qa/zcash/postponed-updates.txt | 2 +- rust-toolchain | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/depends/packages/native_rust.mk b/depends/packages/native_rust.mk index 0cab70854b8..a48d683b455 100644 --- a/depends/packages/native_rust.mk +++ b/depends/packages/native_rust.mk @@ -1,15 +1,14 @@ package=native_rust -$(package)_version=1.54.0 +$(package)_version=1.55.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=350354495b1d4b6dd2ec7cf96aa9bc61d031951cf667a31e8cf401dc508639e6 +$(package)_sha256_hash_linux=2080253a2ec36ac8ed6e060d30802d888533124b8d16545cfd4af898b365eaac $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=5eb27a4f5f7a4699bc70cf1848e340ddd74e151488bfcb26853fd584958e3d33 +$(package)_sha256_hash_darwin=2e345ac7724c192c9487a2c6bd4f6c52c884d791981510288830d27d9a0bf2f3 $(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz -$(package)_sha256_hash_freebsd=026a40470b9fddfbb4abff3546e620eceaa2812ffc13e180bbb9360c01501a16 - +$(package)_sha256_hash_freebsd=7ddb8ec4d431f64dd6428df93d46f726516970b0ca83c71c3efbfe34a42d3113 $(package)_file_name_aarch64_linux=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_aarch64_linux=33a50c5366a57aaab43c1c19e4a49ab7d8ffcd99a72925c315fb1f9389139e6f +$(package)_sha256_hash_aarch64_linux=eebdb2e659ed14884a49f0457d44e5e8c9f89fca3414533752c6dbb96232c156 # Mapping from GCC canonical hosts to Rust targets # If a mapping is not present, we assume they are identical, unless $host_os is @@ -18,9 +17,9 @@ $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu $(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu # Mapping from Rust targets to SHA-256 hashes -$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=2d90cd90734a2d057b2a4eeee36a72d96569fb5fff0ac1e22eeb5fab93e66848 -$(package)_rust_std_sha256_hash_x86_64-apple-darwin=3908ff438cdf39c3f46a103f3a0fa04a0d8add996c87e4290c80f1a766df6a04 -$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=b3d234211c4255bef6eca800cfb4c44e7779e9d4cb7c0dbe6cd69d8722dd55d6 +$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=e30063a259e32cd0e31baadcee82112ef840e0f654d5128dd79fc715ede92058 +$(package)_rust_std_sha256_hash_x86_64-apple-darwin=8888fb0a1cbc645f86e1551d27cc127697361fecab9cd414691e434976412733 +$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=8dfab5489b485417d76a7d266fc795608ba61f9c423f8a71616e01f33e146487 define rust_target $(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2)))) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index 39fdf300919..54c12f24bce 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -15,7 +15,7 @@ native_ccache 4.4.1 2021-10-01 # Clang is currently pinned to LLVM 12 -# Rust is currently pinned to 1.54.0 +# Rust is currently pinned to 1.55.0 bdb 18.1.40 2022-02-01 diff --git a/rust-toolchain b/rust-toolchain index b7921ae87bc..094d6ad00ce 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.54.0 +1.55.0 From 1ad8ee8735943740ca44670fac7d7b440821ad38 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 14 Sep 2021 17:44:51 +0100 Subject: [PATCH 009/514] qa: Boost 1.77.0 - The patches `iostreams-106.patch` and `signals2-noise.patch` were incorporated into Boost 1.75. - The allocator access deprecation issue was fixed in Boost 1.76. Closes zcash/zcash#4945. --- depends/packages/boost.mk | 8 +- .../boost/deprecated-two-arg-allocate.diff | 102 ------------------ depends/patches/boost/iostreams-106.patch | 25 ----- depends/patches/boost/signals2-noise.patch | 23 ---- qa/zcash/postponed-updates.txt | 9 -- 5 files changed, 2 insertions(+), 165 deletions(-) delete mode 100644 depends/patches/boost/deprecated-two-arg-allocate.diff delete mode 100644 depends/patches/boost/iostreams-106.patch delete mode 100644 depends/patches/boost/signals2-noise.patch diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 7d31a88032c..2f961fa7b11 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,10 +1,9 @@ package=boost -$(package)_version=1_74_0 +$(package)_version=1_77_0 $(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$(subst _,.,$($(package)_version))/source/ $(package)_file_name=boost_$($(package)_version).tar.bz2 -$(package)_sha256_hash=83bfc1507731a0906e387fc28b7ef5417d591429e51e788417fe9ff025e116b1 +$(package)_sha256_hash=fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854 $(package)_dependencies=native_b2 -$(package)_patches=iostreams-106.patch signals2-noise.patch deprecated-two-arg-allocate.diff ifneq ($(host_os),darwin) $(package)_dependencies+=libcxx @@ -42,9 +41,6 @@ endif endef define $(package)_preprocess_cmds - patch -p2 < $($(package)_patch_dir)/iostreams-106.patch && \ - patch -p2 < $($(package)_patch_dir)/signals2-noise.patch && \ - patch -p2 < $($(package)_patch_dir)/deprecated-two-arg-allocate.diff && \ echo "using $($(package)_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cflags)\" \"$($(package)_cxxflags)\" \"$($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$($(package)_ar)\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef diff --git a/depends/patches/boost/deprecated-two-arg-allocate.diff b/depends/patches/boost/deprecated-two-arg-allocate.diff deleted file mode 100644 index f9622856636..00000000000 --- a/depends/patches/boost/deprecated-two-arg-allocate.diff +++ /dev/null @@ -1,102 +0,0 @@ -diff -ur orig/include/boost/core/allocator_access.hpp patched/include/boost/core/allocator_access.hpp ---- orig/include/boost/core/allocator_access.hpp 2021-01-18 21:42:05.926895400 +0000 -+++ patched/include/boost/core/allocator_access.hpp 2021-01-18 21:44:26.426895400 +0000 -@@ -11,7 +11,7 @@ - #include - #if !defined(BOOST_NO_CXX11_ALLOCATOR) - #include --#if !defined(BOOST_MSVC) -+#if !defined(BOOST_MSVC) && !defined(BOOST_CLANG) - #include - #else - #include -@@ -49,7 +49,7 @@ - struct allocator_pointer { - typedef typename A::pointer type; - }; --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - struct allocator_pointer { - typedef typename std::allocator_traits::pointer type; -@@ -72,7 +72,7 @@ - struct allocator_const_pointer { - typedef typename A::const_pointer type; - }; --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - struct allocator_const_pointer { - typedef typename std::allocator_traits::const_pointer type; -@@ -137,7 +137,7 @@ - struct allocator_difference_type { - typedef typename A::difference_type type; - }; --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - struct allocator_difference_type { - typedef typename std::allocator_traits::difference_type type; -@@ -161,7 +161,7 @@ - struct allocator_size_type { - typedef typename A::size_type type; - }; --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - struct allocator_size_type { - typedef typename std::allocator_traits::size_type type; -@@ -260,7 +260,7 @@ - struct allocator_rebind { - typedef typename A::template rebind::other type; - }; --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - struct allocator_rebind { - typedef typename std::allocator_traits::template rebind_alloc type; -@@ -313,7 +313,7 @@ - { - return a.allocate(n, h); - } --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - inline typename allocator_pointer::type - allocator_allocate(A& a, typename allocator_size_type::type n, -@@ -400,7 +400,7 @@ - ::new((void*)p) T(v); - } - #endif --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - inline void - allocator_construct(A& a, T* p, Args&&... args) -@@ -449,7 +449,7 @@ - p->~T(); - (void)p; - } --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - inline void - allocator_destroy(A& a, T* p) -@@ -496,7 +496,7 @@ - { - return a.max_size(); - } --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - inline typename allocator_size_type::type - allocator_max_size(const A& a) -@@ -545,7 +545,7 @@ - { - return a; - } --#elif defined(BOOST_MSVC) -+#elif defined(BOOST_MSVC) || defined(BOOST_CLANG) - template - inline A - allocator_select_on_container_copy_construction(const A& a) diff --git a/depends/patches/boost/iostreams-106.patch b/depends/patches/boost/iostreams-106.patch deleted file mode 100644 index dcecd5d0df7..00000000000 --- a/depends/patches/boost/iostreams-106.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 4e76f73826fd0a7067b837e4850a9051436f5ec5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= - -Date: Sun, 22 Dec 2019 10:26:38 +0100 -Subject: [PATCH] Fix build on windows with libc++ - -Proposed by @SquallATF in #67 ---- - include/boost/iostreams/detail/config/fpos.hpp | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/include/boost/iostreams/detail/config/fpos.hpp b/include/boost/iostreams/detail/config/fpos.hpp -index c5dc6cf59..a5835421f 100644 ---- a/include/boost/iostreams/detail/config/fpos.hpp -+++ b/include/boost/iostreams/detail/config/fpos.hpp -@@ -26,7 +26,8 @@ - - # if (defined(_YVALS) || defined(_CPPLIB_VER)) && !defined(__SGI_STL_PORT) && \ - !defined(_STLPORT_VERSION) && !defined(__QNX__) && !defined(_VX_CPU) && !defined(__VXWORKS__) \ -- && !((defined(BOOST_MSVC) || defined(BOOST_CLANG)) && _MSVC_STL_VERSION >= 141) -+ && !((defined(BOOST_MSVC) || defined(BOOST_CLANG)) && _MSVC_STL_VERSION >= 141) \ -+ && !defined(_LIBCPP_VERSION) - /**/ - - #include diff --git a/depends/patches/boost/signals2-noise.patch b/depends/patches/boost/signals2-noise.patch deleted file mode 100644 index e0a9ce48f34..00000000000 --- a/depends/patches/boost/signals2-noise.patch +++ /dev/null @@ -1,23 +0,0 @@ -From fd27423fea5537bc857c1fa14bb0c25b994f77b3 Mon Sep 17 00:00:00 2001 -From: Frank Mori Hess -Date: Mon, 20 Jul 2020 14:17:05 -0400 -Subject: [PATCH] Fix warning about deprecated - boost/function_output_iterator.hpp - ---- - include/boost/signals2/detail/null_output_iterator.hpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/boost/signals2/detail/null_output_iterator.hpp b/include/boost/signals2/detail/null_output_iterator.hpp -index 9e986959..dee4373c 100644 ---- a/include/boost/signals2/detail/null_output_iterator.hpp -+++ b/include/boost/signals2/detail/null_output_iterator.hpp -@@ -11,7 +11,7 @@ - #ifndef BOOST_SIGNALS2_NULL_OUTPUT_ITERATOR_HPP - #define BOOST_SIGNALS2_NULL_OUTPUT_ITERATOR_HPP - --#include -+#include - - namespace boost - { diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index 54c12f24bce..45f0db1bc68 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -19,15 +19,6 @@ native_ccache 4.4.1 2021-10-01 bdb 18.1.40 2022-02-01 -# Boost 1.75 starts using the statx syscall where available, which causes -# permission errors in some environments. -# https://github.com/zcash/zcash/issues/4945 -# native_b2 is pinned to the same version as Boost. -boost 1.75.0 2021-09-01 -boost 1.76.0 2021-09-01 -native_b2 1.75.0 2021-09-01 -native_b2 1.76.0 2021-09-01 - # Google Test 1.10.0 requires adding CMake to the depends system. googletest 1.10.0 2021-10-01 googletest 1.11.0 2021-10-01 From 4b8885759e4c955923814afcb1f62c8cc1c19761 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 14 Sep 2021 17:45:37 +0100 Subject: [PATCH 010/514] cargo update --- Cargo.lock | 176 ++++++++++++++++++++++++++--------------------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 874259926bc..411b8d51785 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495ee669413bfbe9e8cace80f4d3d78e6d8c8d99579f97fb93bde351b185f2d4" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", "cipher 0.3.0", @@ -109,9 +109,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64ct" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +checksum = "40a96587c05c810ddbb79e2674d519cff1379517e7b91d166dff7a7cc0e9af6e" [[package]] name = "bech32" @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitvec" @@ -268,9 +268,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cfg-if" @@ -323,9 +323,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "cpufeatures" -version = "0.1.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" dependencies = [ "libc", ] @@ -338,7 +338,7 @@ checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" dependencies = [ "cfg-if 0.1.10", "crossbeam-channel 0.4.4", - "crossbeam-deque 0.7.3", + "crossbeam-deque 0.7.4", "crossbeam-epoch 0.8.2", "crossbeam-queue", "crossbeam-utils 0.7.2", @@ -366,9 +366,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" dependencies = [ "crossbeam-epoch 0.8.2", "crossbeam-utils 0.7.2", @@ -479,9 +479,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" dependencies = [ "quote", "syn", @@ -489,9 +489,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639891fde0dbea823fc3d798a0fdf9d2f9440a42d64a78ab3488b0ca025117b3" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", "digest", @@ -576,9 +576,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63eec06c61e487eecf0f7e6e6372e596a81922c28d33e645d6983ca6493a1af0" +checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" dependencies = [ "bitvec", "rand_core 0.6.3", @@ -599,7 +599,7 @@ checksum = "a25080721bbcd2cd4d765b7d607ea350425fa087ce53cd3e31afcacdab850352" dependencies = [ "aes 0.6.0", "block-modes 0.7.0", - "num-bigint 0.3.2", + "num-bigint 0.3.3", "num-integer", "num-traits", ] @@ -613,7 +613,7 @@ dependencies = [ "block-modes 0.8.1", "cipher 0.3.0", "libm", - "num-bigint 0.4.0", + "num-bigint 0.4.2", "num-integer", "num-traits", ] @@ -632,18 +632,18 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures-channel" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" [[package]] name = "futures-cpupool" @@ -657,15 +657,15 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" [[package]] name = "futures-util" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" dependencies = [ "autocfg", "futures-core", @@ -778,9 +778,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" +checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" dependencies = [ "bytes", "http", @@ -789,9 +789,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" [[package]] name = "httpdate" @@ -857,9 +857,9 @@ checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "jubjub" @@ -882,9 +882,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.98" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "libm" @@ -929,9 +929,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" dependencies = [ "scopeguard", ] @@ -971,9 +971,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memoffset" @@ -1105,9 +1105,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" dependencies = [ "autocfg", "num-integer", @@ -1116,9 +1116,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" dependencies = [ "autocfg", "num-integer", @@ -1193,9 +1193,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039f02eb0f69271f26abe3202189275d7aa2258b903cb0281b5de710a2570ff3" +checksum = "97c9d06878b3a851e8026ef94bf7fef9ba93062cd412601da4d9cf369b1cc62d" dependencies = [ "num-traits", ] @@ -1211,9 +1211,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", @@ -1222,9 +1222,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ "cfg-if 1.0.0", "instant", @@ -1236,9 +1236,9 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd482dfb8cfba5a93ec0f91e1c0f66967cb2fdc1a8dba646c4f9202c5d05d785" +checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" dependencies = [ "base64ct", "rand_core 0.6.3", @@ -1247,9 +1247,9 @@ dependencies = [ [[package]] name = "pasta_curves" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "179df750e20069669699f537ec0c4bcb248283f45a78b8c66d797fb73dbbb455" +checksum = "9a21914ddc589b74e7638091c8e91a4e8b083ee5e1d0066d64407131b7570799" dependencies = [ "blake2b_simd", "ff", @@ -1316,9 +1316,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" dependencies = [ "unicode-xid", ] @@ -1465,9 +1465,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] @@ -1516,18 +1516,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.126" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -1536,9 +1536,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer", "cfg-if 1.0.0", @@ -1549,9 +1549,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" +checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" dependencies = [ "lazy_static", ] @@ -1593,9 +1593,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.74" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" dependencies = [ "proc-macro2", "quote", @@ -1622,18 +1622,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.26" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.26" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" dependencies = [ "proc-macro2", "quote", @@ -1661,9 +1661,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" +checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986" dependencies = [ "tinyvec_macros", ] @@ -1676,9 +1676,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b7b349f11a7047e6d1276853e612d152f5e8a352c61917887cc2169e2366b4c" +checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" dependencies = [ "autocfg", "libc", @@ -1707,9 +1707,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +checksum = "c2ba9ab62b7d6497a8638dfda5e5c4fb3b2d5a7fca4118f2b96151c8ef1a437e" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1730,9 +1730,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" +checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77" dependencies = [ "proc-macro2", "quote", @@ -1741,18 +1741,18 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf" dependencies = [ "lazy_static", ] [[package]] name = "tracing-subscriber" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab69019741fca4d98be3c62d2b75254528b5432233fd8a4d2739fec20278de48" +checksum = "62af966210b88ad5776ee3ba12d5f35b8d6a2b2a12168f3080cf02b814d7376b" dependencies = [ "ansi_term", "chrono", @@ -1773,9 +1773,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "typenum" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "unicode-normalization" @@ -1890,7 +1890,7 @@ name = "zcash_primitives" version = "0.5.0" source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" dependencies = [ - "aes 0.7.4", + "aes 0.7.5", "bip0039", "bitvec", "blake2b_simd", From 710a5f8a9e208530a33219519a722480b872e905 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 14 Sep 2021 20:53:08 +0100 Subject: [PATCH 011/514] Migrate to latest revisions of Zcash Rust crates --- Cargo.lock | 249 ++++++++++++++++-------------------- Cargo.toml | 24 ++-- src/rust/src/orchard_ffi.rs | 4 +- 3 files changed, 127 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 411b8d51785..6534bbaecae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,14 +3,12 @@ version = 3 [[package]] -name = "aes" -version = "0.6.0" +name = "aead" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", + "generic-array", ] [[package]] @@ -20,31 +18,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", - "cipher 0.3.0", + "cipher", "cpufeatures", "opaque-debug", ] -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - [[package]] name = "ahash" version = "0.7.4" @@ -121,21 +99,22 @@ checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" [[package]] name = "bellman" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db9a104adfbc817ea09dec27d616c32dbf1d56fd741dcdc2444a3dfa1b9fffd" +checksum = "0944d18a9a37691b87733b39c9360c9950af9aa5f97e2455bc108d8eb64fc1c1" dependencies = [ "bitvec", "blake2s_simd", "byteorder", - "crossbeam", + "crossbeam-channel 0.5.1", "ff", - "futures", - "futures-cpupool", "group", + "lazy_static", + "log", "num_cpus", "pairing", "rand_core 0.6.3", + "rayon", "subtle", ] @@ -212,16 +191,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-modes" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" -dependencies = [ - "block-padding", - "cipher 0.2.5", -] - [[package]] name = "block-modes" version = "0.8.1" @@ -229,7 +198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" dependencies = [ "block-padding", - "cipher 0.3.0", + "cipher", ] [[package]] @@ -240,9 +209,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bls12_381" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397" +checksum = "6d28daeeded7949f1c7c72693377c98473b00be0aa0023760a84a300e4e7c74b" dependencies = [ "ff", "group", @@ -284,6 +253,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b84ed6d1d5f7aa9bdde921a5090e0ca4d934d250ea3b402a5fab3a994e28a2a" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.19" @@ -297,15 +291,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - [[package]] name = "cipher" version = "0.3.0" @@ -462,21 +447,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930b6a026ce9d358a17f9c9046c55d90b14bb847f36b6ebb6b19365d4feffb8" -dependencies = [ - "crypto_api", -] - [[package]] name = "ctor" version = "0.1.21" @@ -568,7 +538,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" dependencies = [ "blake2b_simd", "byteorder", @@ -576,9 +546,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" +checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" dependencies = [ "bitvec", "rand_core 0.6.3", @@ -591,29 +561,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "fpe" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25080721bbcd2cd4d765b7d607ea350425fa087ce53cd3e31afcacdab850352" -dependencies = [ - "aes 0.6.0", - "block-modes 0.7.0", - "num-bigint 0.3.3", - "num-integer", - "num-traits", -] - [[package]] name = "fpe" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcf3e40fc9accc7218e082db8a75aeea244b8f5db73e591774ef93b4276365e6" dependencies = [ - "block-modes 0.8.1", - "cipher 0.3.0", + "block-modes", + "cipher", "libm", - "num-bigint 0.4.2", + "num-bigint", "num-integer", "num-traits", ] @@ -624,12 +581,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "futures-channel" version = "0.3.17" @@ -645,16 +596,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -dependencies = [ - "futures", - "num_cpus", -] - [[package]] name = "futures-task" version = "0.3.17" @@ -708,9 +649,9 @@ dependencies = [ [[package]] name = "group" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" dependencies = [ "byteorder", "ff", @@ -721,7 +662,7 @@ dependencies = [ [[package]] name = "halo2" version = "0.0.1" -source = "git+https://github.com/zcash/halo2.git?rev=27c4187673a9c6ade13fbdbd4f20955530c22d7f#27c4187673a9c6ade13fbdbd4f20955530c22d7f" +source = "git+https://github.com/zcash/halo2.git?rev=26047eaf323929935fd1e6aa3ae100b1113706e0#26047eaf323929935fd1e6aa3ae100b1113706e0" dependencies = [ "blake2b_simd", "ff", @@ -863,8 +804,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "jubjub" -version = "0.7.0" -source = "git+https://github.com/zkcrypto/jubjub.git?rev=96ab4162b83303378eae32a326b54d88b75bffc2#96ab4162b83303378eae32a326b54d88b75bffc2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7baec19d4e83f9145d4891178101a604565edff9645770fc979804138b04c" dependencies = [ "bitvec", "bls12_381", @@ -908,6 +850,7 @@ dependencies = [ "ipnet", "jubjub", "libc", + "memuse", "metrics", "metrics-exporter-prometheus", "nonempty", @@ -993,6 +936,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memuse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f69d25cd7528769ad3d897e99eb942774bff8b23165012af490351a44c5b583b" +dependencies = [ + "nonempty", +] + [[package]] name = "metrics" version = "0.17.0" @@ -1103,17 +1055,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-bigint" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.2" @@ -1169,19 +1110,20 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.0.0" -source = "git+https://github.com/zcash/orchard.git?rev=d0baa18fc6105df4a7847de2b6dc50c5919b3123#d0baa18fc6105df4a7847de2b6dc50c5919b3123" +source = "git+https://github.com/zcash/orchard.git?rev=78e22f6325d00ba69e3cb0860c6de27f5129ac04#78e22f6325d00ba69e3cb0860c6de27f5129ac04" dependencies = [ - "aes 0.6.0", + "aes", "arrayvec 0.7.1", "bigint", "bitvec", "blake2b_simd", "ff", - "fpe 0.4.0", + "fpe", "group", "halo2", "incrementalmerkletree", "lazy_static", + "memuse", "nonempty", "pasta_curves", "rand", @@ -1202,9 +1144,9 @@ dependencies = [ [[package]] name = "pairing" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3" +checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" dependencies = [ "group", ] @@ -1247,9 +1189,9 @@ dependencies = [ [[package]] name = "pasta_curves" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a21914ddc589b74e7638091c8e91a4e8b083ee5e1d0066d64407131b7570799" +checksum = "e2e9f954e56b84a250978f89358bc9f5a68f6c68f1082d41db1ddc9664316ee5" dependencies = [ "blake2b_simd", "ff", @@ -1302,6 +1244,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1449,7 +1402,7 @@ dependencies = [ [[package]] name = "reddsa" version = "0.0.0" -source = "git+https://github.com/str4d/redjubjub.git?rev=d5d8c5f3bb704bad8ae88fe4a29ae1f744774cb2#d5d8c5f3bb704bad8ae88fe4a29ae1f744774cb2" +source = "git+https://github.com/str4d/redjubjub.git?rev=416a6a8ebf8bd42c114c938883016c04f338de72#416a6a8ebf8bd42c114c938883016c04f338de72" dependencies = [ "blake2b_simd", "byteorder", @@ -1792,6 +1745,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "version_check" version = "0.9.3" @@ -1854,17 +1817,26 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" dependencies = [ "bech32", "blake2b_simd", "bs58", ] +[[package]] +name = "zcash_encoding" +version = "0.0.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +dependencies = [ + "byteorder", + "nonempty", +] + [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" dependencies = [ "bigint", "blake2b_simd", @@ -1874,11 +1846,12 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" dependencies = [ "blake2b_simd", "byteorder", - "crypto_api_chachapoly", + "chacha20", + "chacha20poly1305", "ff", "group", "rand_core 0.6.3", @@ -1888,25 +1861,26 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" dependencies = [ - "aes 0.7.5", + "aes", "bip0039", "bitvec", "blake2b_simd", "blake2s_simd", "bls12_381", "byteorder", - "crypto_api_chachapoly", + "chacha20poly1305", "equihash", "ff", - "fpe 0.5.0", + "fpe", "group", "hex", "incrementalmerkletree", "jubjub", "lazy_static", "log", + "memuse", "nonempty", "orchard", "pasta_curves", @@ -1914,13 +1888,14 @@ dependencies = [ "rand_core 0.6.3", "sha2", "subtle", + "zcash_encoding", "zcash_note_encryption", ] [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" +source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index d440ca91bbd..458c1cac121 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,15 +25,16 @@ path = "src/rust/src/rustzcash.rs" crate-type = ["staticlib"] [dependencies] -bellman = "0.10" +bellman = "0.11" blake2b_simd = "0.5" blake2s_simd = "0.5" -bls12_381 = "0.5" +bls12_381 = "0.6" byteorder = "1" -group = "0.10" +group = "0.11" incrementalmerkletree = "0.1" libc = "0.2" -jubjub = "0.7" +jubjub = "0.8" +memuse = "0.2" nonempty = "0.7" orchard = "0.0" subtle = "2.2" @@ -68,12 +69,11 @@ codegen-units = 1 [patch.crates-io] ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } -halo2 = { git = "https://github.com/zcash/halo2.git", rev = "27c4187673a9c6ade13fbdbd4f20955530c22d7f" } +halo2 = { git = "https://github.com/zcash/halo2.git", rev = "26047eaf323929935fd1e6aa3ae100b1113706e0" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -jubjub = { git = "https://github.com/zkcrypto/jubjub.git", rev = "96ab4162b83303378eae32a326b54d88b75bffc2" } -orchard = { git = "https://github.com/zcash/orchard.git", rev = "d0baa18fc6105df4a7847de2b6dc50c5919b3123" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "19a97f16945c68c33aedcc89f2a4f4d398658b05" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "19a97f16945c68c33aedcc89f2a4f4d398658b05" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "19a97f16945c68c33aedcc89f2a4f4d398658b05" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "19a97f16945c68c33aedcc89f2a4f4d398658b05" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "19a97f16945c68c33aedcc89f2a4f4d398658b05" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "78e22f6325d00ba69e3cb0860c6de27f5129ac04" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } diff --git a/src/rust/src/orchard_ffi.rs b/src/rust/src/orchard_ffi.rs index eb782f5cb5d..db280a43dec 100644 --- a/src/rust/src/orchard_ffi.rs +++ b/src/rust/src/orchard_ffi.rs @@ -1,11 +1,13 @@ use std::{mem, ptr}; use libc::size_t; +use memuse::DynamicUsage; use orchard::{ bundle::Authorized, keys::OutgoingViewingKey, + note_encryption::OrchardDomain, primitives::redpallas::{self, Binding, SpendAuth}, - Bundle, OrchardDomain, + Bundle, }; use rand_core::OsRng; use tracing::{debug, error}; From 07143679dc5b5f82ed0990d5bd7b3c29fe767dd4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 15 Sep 2021 17:50:20 +0100 Subject: [PATCH 012/514] test: Set up mininodes at the start of feature_zip239 --- qa/rpc-tests/feature_zip239.py | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/qa/rpc-tests/feature_zip239.py b/qa/rpc-tests/feature_zip239.py index 62fbc14f11e..182ecda5771 100755 --- a/qa/rpc-tests/feature_zip239.py +++ b/qa/rpc-tests/feature_zip239.py @@ -115,6 +115,24 @@ def verify_disconnected(self, testnode, timeout=30): fail("Should have received pong") def run_test(self): + # Set up test nodes. + # - test_nodes[0] will only request v4 transactions + # - test_nodes[1] will only request v5 transactions + # - test_nodes[2] will test invalid v4 request using MSG_WTXID + # - test_nodes[3] will test invalid v5 request using MSG_TX + test_nodes = [] + connections = [] + + for i in range(4): + test_nodes.append(TestNode()) + connections.append(NodeConn( + '127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i], + protocol_version=NU5_PROTO_VERSION)) + test_nodes[i].add_connection(connections[i]) + + NetworkThread().start() # Start up network handling in another thread + [x.wait_for_verack() for x in test_nodes] + net_version = self.nodes[0].getnetworkinfo()["protocolversion"] if net_version < NU5_PROTO_VERSION: print("Node's block index is not NU5-aware, skipping remaining tests") @@ -152,24 +170,6 @@ def run_test(self): # Wait for the mempools to sync. self.sync_all() - # Set up test nodes. - # - test_nodes[0] will only request v4 transactions - # - test_nodes[1] will only request v5 transactions - # - test_nodes[2] will test invalid v4 request using MSG_WTXID - # - test_nodes[3] will test invalid v5 request using MSG_TX - test_nodes = [] - connections = [] - - for i in range(4): - test_nodes.append(TestNode()) - connections.append(NodeConn( - '127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i], - protocol_version=NU5_PROTO_VERSION)) - test_nodes[i].add_connection(connections[i]) - - NetworkThread().start() # Start up network handling in another thread - [x.wait_for_verack() for x in test_nodes] - # # inv # From 5a2e6183f366348bdba232495aa9eb621739d532 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 15 Sep 2021 17:51:06 +0100 Subject: [PATCH 013/514] net: Reject unknown CInv message types Nodes will now reject messages containing unknown CInv message types, instead of (mostly) ignoring them. --- qa/rpc-tests/feature_zip239.py | 65 +++++++++++++++++++++++-- qa/rpc-tests/test_framework/mininode.py | 10 +++- src/main.cpp | 8 +++ src/protocol.h | 29 ++++++++++- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/qa/rpc-tests/feature_zip239.py b/qa/rpc-tests/feature_zip239.py index 182ecda5771..19ec1550fcd 100755 --- a/qa/rpc-tests/feature_zip239.py +++ b/qa/rpc-tests/feature_zip239.py @@ -11,6 +11,7 @@ mininode_lock, msg_getdata, msg_mempool, + msg_reject, uint256_from_str, ) from test_framework.test_framework import BitcoinTestFramework @@ -26,23 +27,37 @@ hex_str_to_bytes, nuparams, p2p_port, - start_nodes, + start_node, wait_and_assert_operationid_status, ) from tx_expiry_helper import TestNode +import tempfile import time # Test ZIP 239 behaviour before and after NU5. class Zip239Test(BitcoinTestFramework): def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + extra_args=[ # Enable Canopy at height 205 to allow shielding Sprout funds first. nuparams(BLOSSOM_BRANCH_ID, 205), nuparams(HEARTWOOD_BRANCH_ID, 205), nuparams(CANOPY_BRANCH_ID, 205), nuparams(NU5_BRANCH_ID, 210), - ]] * self.num_nodes) + ] + + # We log the stderr of node 0, which the test nodes connect to. This + # enables us to check that we see the expected error logged, and also + # ensures that the test itself passes (as otherwise the stderr output + # would be interpreted as an error from the test itself). + self.log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) + + nodes = [] + nodes.append(start_node(0, self.options.tmpdir, extra_args, stderr=self.log_stderr)) + nodes.append(start_node(1, self.options.tmpdir, extra_args)) + nodes.append(start_node(2, self.options.tmpdir, extra_args)) + nodes.append(start_node(3, self.options.tmpdir, extra_args)) + return nodes def cinv_for(self, txid, authDigest=None): if authDigest is not None: @@ -104,6 +119,32 @@ def verify_last_notfound(self, testnode, txid, authDigest=None): assert_equal(len(testnode.last_notfound.inv), 1) assert_equal(testnode.last_notfound.inv[0], self.cinv_for(txid, authDigest)) + def verify_invalid_cinv(self, testnode, conn, msg_type, expected_msg): + # Send p2p message "getdata" containing an invalid CInv message + getdatamsg = msg_getdata() + getdatamsg.inv = [CInv(msg_type, 1)] + with mininode_lock: + testnode.last_tx = None + testnode.last_notfound = None + testnode.send_message(getdatamsg) + + # Sync up with node after p2p messages delivered + testnode.sync_with_ping() + + # Verify that we get a reject message + expected = msg_reject() + expected.message = b"getdata" + expected.code = msg_reject.REJECT_MALFORMED + expected.reason = b"error parsing message" + assert_equal(conn.rejectMessage, expected) + + # Verify that we see the expected error on stderr + self.log_stderr.seek(0) + stderr = self.log_stderr.read().decode('utf-8') + self.log_stderr.truncate(0) + if expected_msg not in stderr: + raise AssertionError("Expected error \"" + expected_msg + "\" not found in:\n" + stderr) + def verify_disconnected(self, testnode, timeout=30): sleep_time = 0.05 while timeout > 0: @@ -135,6 +176,18 @@ def run_test(self): net_version = self.nodes[0].getnetworkinfo()["protocolversion"] if net_version < NU5_PROTO_VERSION: + # Sending a getdata message containing a MSG_WTX CInv message type + # results in a reject message. + self.verify_invalid_cinv( + test_nodes[0], connections[0], 5, + "Negotiated protocol version does not support CInv message type MSG_WTX", + ) + + # Sending a getdata message containing an invalid CInv message type + # results in a reject message. + self.verify_invalid_cinv( + test_nodes[1], connections[1], 0xffff, "Unknown CInv message type") + print("Node's block index is not NU5-aware, skipping remaining tests") return @@ -207,7 +260,13 @@ def run_test(self): self.send_data_message(test_nodes[3], v5_txid) self.verify_disconnected(test_nodes[3]) + # Sending a getdata message containing an invalid CInv message type + # results in a reject message. + self.verify_invalid_cinv( + test_nodes[0], connections[0], 0xffff, "Unknown CInv message type") + [c.disconnect_node() for c in connections] + self.log_stderr.close() if __name__ == '__main__': Zip239Test().main() diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index c5f5bc1bd7f..10712aa5eba 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -374,7 +374,7 @@ def __eq__(self, other): def __repr__(self): return "CInv(type=%s hash=%064x hash_aux=%064x)" \ - % (self.typemap[self.type], self.hash, self.hash_aux) + % (self.typemap.get(self.type, self.type), self.hash, self.hash_aux) class CBlockLocator(object): @@ -1754,6 +1754,14 @@ def serialize(self): r += ser_uint256(self.data) return r + def __eq__(self, other): + return ( + (type(self) == type(other)) and ( + (self.message, self.code, self.reason, self.data) == + (other.message, other.code, other.reason, other.data) + ) + ) + def __repr__(self): return "msg_reject: %s %d %s [%064x]" \ % (self.message, self.code, self.reason, self.data) diff --git a/src/main.cpp b/src/main.cpp index c011a90eebb..f1b27f8ef91 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7522,6 +7522,10 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) const uint256& hash = txinfo.tx->GetHash(); CInv inv = InvForTransaction(txinfo.tx); pto->setInventoryTxToSend.erase(hash); + // ZIP 239: We won't have v5 transactions in our mempool until after + // NU5 activates, at which point we will only be connected to peers + // that understand MSG_WTX. + if (inv.type == MSG_WTX) assert(pto->nVersion >= CINV_WTX_VERSION); if (IsExpiringSoonTx(*txinfo.tx, currentHeight + 1)) continue; if (pto->pfilter) { if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; @@ -7570,6 +7574,10 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) continue; } CInv inv = InvForTransaction(txinfo.tx); + // ZIP 239: We won't have v5 transactions in our mempool until after + // NU5 activates, at which point we will only be connected to peers + // that understand MSG_WTX. + if (inv.type == MSG_WTX) assert(pto->nVersion >= CINV_WTX_VERSION); if (IsExpiringSoonTx(*txinfo.tx, currentHeight + 1)) continue; if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; // Send diff --git a/src/protocol.h b/src/protocol.h index 59be50ab620..f182d0f0606 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -145,8 +145,35 @@ class CInv { int nVersion = s.GetVersion(); READWRITE(type); + + // The implicit P2P network protocol inherited from Bitcoin Core has + // zcashd nodes sort-of ignoring unknown CInv message types in inv + // messages: they are added to the known transaction inventory, but + // AlreadyHave returns true, so we do nothing with them. Meanwhile for + // getdata messages, ProcessGetData ignores unknown message types + // entirely. + // + // As of v4.5.0, we change the implementation behaviour to reject + // undefined message types instead of ignoring them. + switch (type) { + case MSG_TX: + case MSG_BLOCK: + case MSG_FILTERED_BLOCK: + break; + case MSG_WTX: + if (nVersion < CINV_WTX_VERSION) { + throw std::ios_base::failure( + "Negotiated protocol version does not support CInv message type MSG_WTX"); + } + break; + default: + // This includes UNDEFINED, which should never be serialized. + throw std::ios_base::failure("Unknown CInv message type"); + } + READWRITE(hash); - if (type == MSG_WTX && nVersion >= CINV_WTX_VERSION) { + if (type == MSG_WTX) { + // We've already checked above that nVersion >= CINV_WTX_VERSION. READWRITE(hashAux); } else if (type == MSG_TX && ser_action.ForRead()) { // Ensure that this value is set consistently in memory for MSG_TX. From 8a9f29eaf17e6f82a8fde1bf2f3fe69f6f45ddef Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Sep 2021 23:15:40 +0100 Subject: [PATCH 014/514] make-release.py: Versioning changes for 4.5.0-rc1. --- README.md | 2 +- configure.ac | 6 +++--- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 6 +++--- src/deprecation.h | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1a84cfe6425..66528e79bcd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.4.1 +Zcash 4.5.0-rc1 =========== diff --git a/configure.ac b/configure.ac index ac565f294cc..5683ff1eca3 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) -define(_CLIENT_VERSION_MINOR, 4) -define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 50) +define(_CLIENT_VERSION_MINOR, 5) +define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_BUILD, 25) 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))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 3eddeff5a54..4c332d4214d 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.4.1" +name: "zcash-4.5.0-rc1" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index 7a8e56902cb..1a849b601af 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,9 +16,9 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 4 -#define CLIENT_VERSION_MINOR 4 -#define CLIENT_VERSION_REVISION 1 -#define CLIENT_VERSION_BUILD 50 +#define CLIENT_VERSION_MINOR 5 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 25 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/deprecation.h b/src/deprecation.h index e96630d6ebf..620e42a47d1 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -10,7 +10,7 @@ // Per https://zips.z.cash/zip-0200 // Shut down nodes running this version of code, 16 weeks' worth of blocks after the estimated // release block height. A warning is shown during the 14 days' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 1279420; +static const int APPROX_RELEASE_HEIGHT = 1392800; static const int RELEASE_TO_DEPRECATION_WEEKS = 16; static const int EXPECTED_BLOCKS_PER_HOUR = 3600 / Consensus::POST_BLOSSOM_POW_TARGET_SPACING; static_assert(EXPECTED_BLOCKS_PER_HOUR == 48, "The value of Consensus::POST_BLOSSOM_POW_TARGET_SPACING was chosen such that this assertion holds."); From 518895a3dd0cd8f67b7c91255a74a96bb41b7cd3 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Sep 2021 23:34:14 +0100 Subject: [PATCH 015/514] make-release.py: Updated manpages for 4.5.0-rc1. --- doc/man/zcash-cli.1 | 6 +++--- doc/man/zcash-tx.1 | 6 +++--- doc/man/zcashd.1 | 14 +++++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index f0161a6ccd4..4c52fe0da0e 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "June 2021" "zcash-cli v4.4.1" "User Commands" +.TH ZCASH-CLI "1" "September 2021" "zcash-cli v4.5.0-rc1" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.4.1 +zcash-cli \- manual page for zcash-cli v4.5.0-rc1 .SH DESCRIPTION -Zcash RPC client version v4.4.1 +Zcash RPC client version v4.5.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 950fdb3885c..a638db35818 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "June 2021" "zcash-tx v4.4.1" "User Commands" +.TH ZCASH-TX "1" "September 2021" "zcash-tx v4.5.0-rc1" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.4.1 +zcash-tx \- manual page for zcash-tx v4.5.0-rc1 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.4.1 +Zcash zcash\-tx utility version v4.5.0\-rc1 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index cd103ba400a..7bd7193d973 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "June 2021" "zcashd v4.4.1" "User Commands" +.TH ZCASHD "1" "September 2021" "zcashd v4.5.0-rc1" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.4.1 +zcashd \- manual page for zcashd v4.5.0-rc1 .SH DESCRIPTION -Zcash Daemon version v4.4.1 +Zcash Daemon version v4.5.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -99,9 +99,13 @@ Reverting this setting requires re\-downloading the entire blockchain. (default: 0 = disable pruning blocks, >550 = target size in MiB to use for block files) .HP +\fB\-reindex\-chainstate\fR +.IP +Rebuild chain state from the currently indexed blocks +.HP \fB\-reindex\fR .IP -Rebuild block chain index from current blk000??.dat files on startup +Rebuild chain state and block index from the blk*.dat files on disk .HP \fB\-sysperms\fR .IP @@ -395,7 +399,7 @@ Debugging/Testing options: Output debugging information (default: 0, supplying is optional). If is not supplied or if = 1, output all debugging information. can be: addrman, alert, bench, -coindb, db, estimatefee, http, libevent, lock, mempool, net, +coindb, db, estimatefee, http, libevent, lock, mempool, mempoolrej, net, partitioncheck, pow, proxy, prune, rand, receiveunsafe, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc). For multiple specific categories use \fB\-debug=\fR multiple times. From bfd7b31efea18f0b4147f993f54c506e1103d4e0 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Sep 2021 23:34:14 +0100 Subject: [PATCH 016/514] make-release.py: Updated release notes and changelog for 4.5.0-rc1. --- contrib/debian/changelog | 6 + doc/release-notes/release-notes-4.5.0-rc1.md | 311 +++++++++++++++++++ 2 files changed, 317 insertions(+) create mode 100644 doc/release-notes/release-notes-4.5.0-rc1.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 91583c1792e..7e2a6018026 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.5.0~rc1) stable; urgency=medium + + * 4.5.0-rc1 release. + + -- Electric Coin Company Thu, 16 Sep 2021 23:34:14 +0100 + zcash (4.4.1) stable; urgency=medium * 4.4.1 release. diff --git a/doc/release-notes/release-notes-4.5.0-rc1.md b/doc/release-notes/release-notes-4.5.0-rc1.md new file mode 100644 index 00000000000..c91384283d7 --- /dev/null +++ b/doc/release-notes/release-notes-4.5.0-rc1.md @@ -0,0 +1,311 @@ +Changelog +========= + +Aditya Kulkarni (1): + Sort taddr txns by txindex in addition to height + +Alex Morcos (4): + Store the total sig op count of a tx. + Add a score index to the mempool. + Add TxPriority class and comparator + tidy up CInv::GetCommand + +Daira Hopwood (8): + Improve error message when a block would overfill the Orchard commitment tree. + More precise terminology: "lock free" -> "unlocked" + ZIP 339 support. + Update URL for Boost source download (from dl.bintray.com to boostorg.jfrog.io). + Cargo.toml: use librustzcash after the merge of https://github.com/zcash/librustzcash/pull/424 . + Update unified address test data to take account of HRPs in padding (https://github.com/zcash/librustzcash/pull/419). + Avoid need to cast away const in the C caller of zip339_free_phrase. + Update authors of librustzcash to everyone currently and formerly on Core team (in alphabetical order). + +Eric Lombrozo (2): + Removed ppszTypeName from protocol.cpp + getdata enum issue fix + +Ethan Heilman (1): + Fix typo adddrman to addrman as requested in #8070 + +Ethan Heilman (1): + Remove non-determinism which is breaking net_tests #8069 + +Gregory Maxwell (4): + Eliminate TX trickle bypass, sort TX invs for privacy and priority. + Move bloom and feerate filtering to just prior to tx sending. + Do not use mempool for GETDATA for tx accepted after the last mempool req. + Defer inserting into maprelay until just before relaying. + +Jack Grigg (109): + Re-include reading blocks from disk in block connection benchmark + cargo update + Migrate to latest zcash_* crates + metrics 0.16 and metrics-exporter-prometheus 0.5 + Implement ZIP 216 consensus rules + Extract SpendDescriptionV5 and OutputDescriptionV5 classes + rust: Enable C++ streams to be passed into Rust code + ZIP 225 tx format constants + v5 transaction format parser + contrib: Add BOSL to contrib/debian/copyright + Remove early return logic from transaction parsing + rust: Document read_callback_t and write_callback_t + CTransaction: Make new ZIP 225 fields non-const and private + ZIP 244 transaction digests + ZIP 244 signature digests + ZIP 244 hashAuthDataRoot computation + Fix tests that assume CTxOuts can be "null" + test: Generate valid Sapling types + test: Small fixes to sighash test vector generation + test: Regenerate sighash.json after generator fixes + Throw an exception instead of asserting if Rust tx parser fails + CI: Publish correct book directory + CI: Build book with latest mdbook + rust: Documentation improvements to FFI methods + Implement Orchard authorization batch validator + Implement Orchard signature validation consensus rules + rust: Fix patched dependencies + book: Add dev guide page about Rust dependencies + Rename hashLightClientRoot to hashBlockCommitments in block header + ZIP 244 hashBlockCommitments implementation + test: Check for valid hashBlockCommitments construction post-NU5 + Skip hashBlockCommitments check when testing block templates + test: Check hashBlockCommitments before, at, and after NU5 activation + ConnectBlock: Check NU activation when deriving block commitments + Copy authDigest in CTransaction::operator=(const CTransaction &tx) + rust: Move history tree FFI logic into a module + rust: Migrate to zcash_history with versioned trees + rust: Move history tree FFI declarations into a separate header + test: Use valid consensus branch IDs in history tree tests + Use V2 history trees from NU5 onward + test: Check history trees across Canopy and NU5 activations + rpc: Document getblock RPC finalorchardroot field, omit before NU5 + rust: Document some requirements for history tree FFI methods + test: Add test case for popping from an empty history tree + Implement Orchard pool value tracking + rust: Load Orchard circuit parameters at startup + Check Orchard bundle-specific consensus rules, i.e. proofs + test: Update CCoinsViewTest with changes to CCoinsView interface + Include Orchard bundle in transaction dynamic usage + ZIP 203: Enforce coinbase nExpiryHeight consensus rule from NU5 + test: Check for updated empty-tx reject messages in transaction tests + test: Fix OverwinterExpiryHeight test after ZIP 203 contextual changes + miner: Set coinbase expiry height to block height from NU5 activation + Introduce libzcash::RawAddress type + Use `libzcash::RawAddress` in `CWallet::GetFilteredNotes` + Use a visitor for handling -mineraddress config option + Add support for decoding and encoding Unified Addresses + Pass network type through to UA address handling logic + CI: Add workflow that runs general lints + CI: Check scripted diffs + CI: Add Rust lints + Document why a nested call to ExtractMinerAddress is not recursive + Add constants for UA typecodes + Postpone dependency updates we aren't doing in this release + depends: Update Rust to 1.54.0 + depends: Update Clang / libcxx to LLVM 12 + depends: Update utfcpp to 3.2.1 + depends: Fix issue cross-compiling BDB to Windows with Clang 12 + rust: cargo update + rust: metrics 0.17 + CI: Use Rust 1.54 for lints + cargo fmt + test: Wait for transaction propagation in shorter_block_times RPC test + test: Fix race condition in p2p_txexpiringsoon + Revert "Remove reference to -reindex-chainstate" + test: Flush wallet in WriteCryptedSaplingZkeyDirectToDb before reopening + qa: Bump `sync_mempool` timeout for `prioritisetransaction.py` + ProcessGetData(): Rework IsExpiringSoon check + test: Print reject reason if RPC test block rejected instead of accepted + test: Fix pyflakes warnings + CI: Ignore errors from general lints we don't yet have passing + lint: remove duplicate include + lint: Add missing include guards + test: Add NU5 test cases to existing RPC tests + builder: Generate v5 transactions from NU5 activation + Print `nConsensusBranchId` in `CTransaction::ToString` + Separate the consensus and internal consistency checks for branch ID + Parse consensus branch ID when reading v5 transaction format + test: Use correct field of getnetworkinfo to read protocol version + CI: Add Dependabot config to keep Actions up-to-date + Introduce a WTxId struct + Implement CInv message type MSG_WTX + test: Fix bugs in mininode transaction parser + test: Add v5 tx support to mininode + ProcessGetData: Respond to MSG_WTX requests + Add MSG_WTX support to inv messages + Use wtxid for peer state management + test: Implement CInv.__eq__() for mininode to simplify RPC test + Postpone dependency updates that require CMake + depends: Update Rust to 1.54.0 + test: Fix bug in mininode.SpendDescription.deserialize + Add named constants for legacy tx authDigest placeholder value + qa: Boost 1.77.0 + cargo update + Migrate to latest revisions of Zcash Rust crates + test: Set up mininodes at the start of feature_zip239 + net: Reject unknown CInv message types + make-release.py: Versioning changes for 4.5.0-rc1. + make-release.py: Updated manpages for 4.5.0-rc1. + +John Newbery (10): + [tests] Remove wallet accounts test + [wallet] GetBalance can take an isminefilter filter. + [wallet] Factor out GetWatchOnlyBalance() + [wallet] deduplicate GetAvailableCredit logic + [wallet] factor out GetAvailableWatchOnlyBalance() + [wallet] GetBalance can take a min_depth argument. + [RPC] [wallet] allow getbalance to use min_conf and watch_only without accounts. + [wallet] Remove wallet account RPCs + [wallet] Kill accounts + [net] split PushInventory() + +Jonas Schnelli (1): + fix locking issue with new mempool limiting + +Kris Nuttycombe (46): + Update transaction auth commitments for pre-v5 transactions. + Move OrchardBundle to its own header file. + Implement the Rust side of the incremental merkle tree FFI. + Orchard changes to coins & consensus. + Return std::optional for GetAnchor + Check nullifiers length against bundle actions length. + Add Orchard bundle commitments to merkle tree. + Add Orchard merkle tree anchor tests. + Documentation cleanup. + Update orchard dependency. + Update to released version of incrementalmerkletree + Apply suggestions from code review + Apply suggestions from code review + Fix Orchard incremental Merkle tree empty root. + Fix header guards for incremental_sinsemilla_tree.h + Apply style suggestions. + Consistently panic on null commitment tree pointers. + Fix implmentation of OrchardMerkleTree.DynamicMemoryUsage + Document source of Orchard merkle tree test data. + Apply suggestions from code review + Add consensus check for duplicate Orchard nullifiers within a single transaction. + Add Orchard nullifiers to nullifiers cache. + Apply suggestions from code review + Ensure Sapling versions are valid after NU5 + Make CTransaction::nConsensusBranchId a std::optional + Add NU5 upper bound check on nSpendsSapling, nOutputsSapling, nActionsOrchard + Check consensus branch ID for V5 transactions. + Rename tx.valueBalance -> tx.valueBalanceSapling + Make valueBalanceSapling a private non-const member of CTransaction. + Add Orchard value balance checks. + Account for Orchard balance in GetValueOut and GetShieldedValueIn. + Retract partial Orchard test support. + Add check that v5 transactions have empty Sprout joinsplits. + Prevent undefined behaviour in `CTransaction::GetValueOut()` + ZIP 213: Add checks to support Orchard shielded coinbase outputs. + Add check for consistency between nActionsOrchard and Orchard flags. + Ensure that the Orchard note commitment tree does not exceed its maximum size. + Update Orchard commitment tree hashes to use total MerkleCRH^Orchard. + Apply suggestions from code review + Make Sapling Spend and Ouput count, and Orchard Action count checks be noncontextual. + Use DOS level 100 for noncontextual checks. + Fix error strings to correctly reflect context. + Remove unused account-related wallet methods. + Use manual serialization for Merkle frontiers rather than bincode. + Fix clippy complaints. + Lock the wallet in SetBestChainINTERNAL + +Larry Ruane (1): + ZIP 225: v5 transaction check rules + +Luke Dashjr (1): + Optimisation: Store transaction list order in memory rather than compute it every need + +Marco Falke (1): + [qa] py2: Unfiddle strings into bytes explicitly + +Matt Corallo (4): + Fix calling mempool directly, instead of pool, in ATMP + Track (and define) ::minRelayTxFee in CTxMemPool + Add CFeeRate += operator + Print mempool size in KB when adding txn + +Patrick Strateman (5): + Fix insanity of CWalletDB::WriteTx and CWalletTx::WriteToDisk + Add CWallet::ListAccountCreditDebit + Add CWallet::ReorderTransactions and use in accounting_tests.cpp + Move CWalletDB::ReorderTransactions to CWallet + Move GetAccountBalance from rpcwallet.cpp into CWallet::GetAccountBalance + +Pieter Wuille (16): + Replace trickle nodes with per-node/message Poisson delays + Change mapRelay to store CTransactions + Make ProcessNewBlock dbp const and update comment + Switch reindexing to AcceptBlock in-loop and ActivateBestChain afterwards + Optimize ActivateBestChain for long chains + Add -reindex-chainstate that does not rebuild block index + Report reindexing progress in GUI + Split up and optimize transaction and block inv queues + Handle mempool requests in send loop, subject to trickle + Return mempool queries in dependency order + Add support for unique_ptr and shared_ptr to memusage + Switch CTransaction storage in mempool to std::shared_ptr + Optimize the relay map to use shared_ptr's + Optimization: don't check the mempool at all if no mempool req ever + Optimization: use usec in expiration and reuse nNow + Get rid of CTxMempool::lookup() entirely + +Russell Yanofsky (2): + [wallet] Add GetLegacyBalance method to simplify getbalance RPC + [wallet] Remove unneeded legacy getbalance code + +Shaul Kfir (1): + Add absurdly high fee message to validation state (for RPC propagation) + +Suhas Daftuar (3): + Use txid as key in mapAlreadyAskedFor + Reverse the sort on the mempool's feerate index + Only use AddInventoryKnown for transactions + +Technetium (1): + add missing aarch64 build deps + +Wladimir J. van der Laan (17): + streams: Add data() method to CDataStream + streams: Remove special cases for ancient MSVC + dbwrapper: Use new .data() method of CDataStream + wallet: Use CDataStream.data() + net: Consistent checksum handling + net: Hardcode protocol sizes and use fixed-size types + protocol.h: Move MESSAGE_START_SIZE into CMessageHeader + protocol.h: Make enums in GetDataMsg concrete values + Add assertion and cast before sending reject code + Add debug message to CValidationState for optional extra information + Introduce REJECT_INTERNAL codes for local AcceptToMempool errors + Add function to convert CValidationState to a human-readable message + Remove most logging from transaction validation + Add information to errors in ConnectBlock, CheckBlock + Move mempool rejections to new debug category + net: Fix sent reject messages for blocks and transactions + test: Add basic test for `reject` code + +hexabot (2): + Update depends/packages/native_clang.mk + Update depends/packages/native_rust.mk + +Marshall Gaucher (5): + Remove sprout funding flow logic + Add fix and note for timing issue + Update funding logic bug + Add usage documentation for manual and faucet driven tests + Update funding logic + +Jack Grigg (12): + Document next_pow2 effects and algorithm source + Improvements to CBlock::BuildAuthDataMerkleTree + rust: Explicitly return null hash for pre-v5 auth digests + book: Note that cargo patches work with absolute paths + Improve docs about setting CBlockIndex hash fields + test: Cleanups to ZIP 221 Python test code + Minor fixes to documentation and formatting + Fix typo in method documentation + Track lengths when copying receiver data from C++ to Rust + depends: Greatly simplify the Clang 12 patch + Adjust code comments to remove topological-sort references + Fix typos + From 2aa9f766b2d3dd79c1b6349b539997595374974c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Sep 2021 23:58:18 +0100 Subject: [PATCH 017/514] Migrate to latest revisions of orchard and the zcash_* crates --- Cargo.lock | 17 ++-- Cargo.toml | 12 +-- src/key_io.cpp | 4 +- src/rust/include/rust/address.h | 4 +- src/rust/src/address_ffi.rs | 85 ++++++++++--------- .../incremental_sinsemilla_tree_ffi.rs | 52 ++++++------ src/test/data/unified_addrs.json | 21 +++-- src/zcash/Address.cpp | 8 +- src/zcash/Address.hpp | 12 +-- 9 files changed, 112 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6534bbaecae..67490b6d81a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,7 +538,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "blake2b_simd", "byteorder", @@ -1110,7 +1110,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.0.0" -source = "git+https://github.com/zcash/orchard.git?rev=78e22f6325d00ba69e3cb0860c6de27f5129ac04#78e22f6325d00ba69e3cb0860c6de27f5129ac04" +source = "git+https://github.com/zcash/orchard.git?rev=f8280c98a3d0e41b8ff5b7f615802bd197f781e1#f8280c98a3d0e41b8ff5b7f615802bd197f781e1" dependencies = [ "aes", "arrayvec 0.7.1", @@ -1817,17 +1817,18 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "bech32", "blake2b_simd", "bs58", + "zcash_encoding", ] [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "byteorder", "nonempty", @@ -1836,7 +1837,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "bigint", "blake2b_simd", @@ -1846,7 +1847,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "blake2b_simd", "byteorder", @@ -1861,7 +1862,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "aes", "bip0039", @@ -1895,7 +1896,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=a3df9dd47d6c3fa89b08483cb8276515464de321#a3df9dd47d6c3fa89b08483cb8276515464de321" +source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 458c1cac121..9a46be4a987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,9 +71,9 @@ codegen-units = 1 ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } halo2 = { git = "https://github.com/zcash/halo2.git", rev = "26047eaf323929935fd1e6aa3ae100b1113706e0" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -orchard = { git = "https://github.com/zcash/orchard.git", rev = "78e22f6325d00ba69e3cb0860c6de27f5129ac04" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "a3df9dd47d6c3fa89b08483cb8276515464de321" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "f8280c98a3d0e41b8ff5b7f615802bd197f781e1" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } diff --git a/src/key_io.cpp b/src/key_io.cpp index 86bec602e65..db83bea04fd 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -44,7 +44,7 @@ class DestinationEncoder std::string operator()(const CNoDestination& no) const { return {}; } }; -static uint8_t GetTypecode(const void* ua, size_t index) +static uint32_t GetTypecode(const void* ua, size_t index) { return std::visit( TypecodeForReceiver(), @@ -441,7 +441,7 @@ static bool AddP2PKHReceiver(void* ua, const unsigned char* raw) return reinterpret_cast(ua)->AddReceiver(receiver); } -static bool AddUnknownReceiver(void* ua, uint8_t typecode, const unsigned char* data, size_t len) +static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* data, size_t len) { libzcash::UnknownReceiver receiver(typecode, std::vector(data, data + len)); return reinterpret_cast(ua)->AddReceiver(receiver); diff --git a/src/rust/include/rust/address.h b/src/rust/include/rust/address.h index ab9e8750a7f..6773497632d 100644 --- a/src/rust/include/rust/address.h +++ b/src/rust/include/rust/address.h @@ -12,10 +12,10 @@ extern "C" { typedef bool (*raw_to_receiver_t)(void* ua, const unsigned char* raw); typedef bool (*unknown_receiver_t)( void* ua, - uint8_t typecode, + uint32_t typecode, const unsigned char* data, size_t len); -typedef uint8_t (*get_typecode_t)(const void* ua, size_t index); +typedef uint32_t (*get_typecode_t)(const void* ua, size_t index); typedef size_t (*get_receiver_len_t)(const void* ua, size_t index); typedef void (*get_receiver_t)(const void* ua, size_t index, unsigned char* data, size_t length); diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index 6694e25b88d..a77488743b3 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -13,11 +13,11 @@ pub type AddReceiverCb = unsafe extern "C" fn(ua: Option, raw: *const u8) -> bool; pub type UnknownReceiverCb = unsafe extern "C" fn( ua: Option, - typecode: u8, + typecode: u32, data: *const u8, len: usize, ) -> bool; -pub type GetTypecodeCb = unsafe extern "C" fn(ua: Option, index: usize) -> u8; +pub type GetTypecodeCb = unsafe extern "C" fn(ua: Option, index: usize) -> u32; pub type GetReceiverLenCb = unsafe extern "C" fn(ua: Option, index: usize) -> usize; pub type GetReceiverDataCb = @@ -162,43 +162,52 @@ pub extern "C" fn zcash_address_serialize_unified( None => return ptr::null_mut(), }; - let receivers: Vec = (0..receivers_len) - .map( - |i| match unsafe { (typecode_cb.unwrap())(ua_obj, i) }.into() { - unified::Typecode::Orchard => { - // TODO: Replace with Orchard support. - let data_len = unsafe { (receiver_len_cb.unwrap())(ua_obj, i) }; - let mut data = vec![0; data_len]; - unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data_len) }; - unified::Receiver::Unknown { - typecode: 0x03, - data, + let receivers: Vec = match (0..receivers_len) + .map(|i| { + Ok( + match unsafe { (typecode_cb.unwrap())(ua_obj, i) }.try_into()? { + unified::Typecode::Orchard => { + // TODO: Replace with Orchard support. + let data_len = unsafe { (receiver_len_cb.unwrap())(ua_obj, i) }; + let mut data = vec![0; data_len]; + unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data_len) }; + unified::Receiver::Unknown { + typecode: 0x03, + data, + } } - } - unified::Typecode::Sapling => { - let mut data = [0; 43]; - unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) }; - unified::Receiver::Sapling(data) - } - unified::Typecode::P2sh => { - let mut data = [0; 20]; - unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) }; - unified::Receiver::P2sh(data) - } - unified::Typecode::P2pkh => { - let mut data = [0; 20]; - unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) }; - unified::Receiver::P2pkh(data) - } - unified::Typecode::Unknown(typecode) => { - let data_len = unsafe { (receiver_len_cb.unwrap())(ua_obj, i) }; - let mut data = vec![0; data_len]; - unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data_len) }; - unified::Receiver::Unknown { typecode, data } - } - }, - ) - .collect(); + unified::Typecode::Sapling => { + let mut data = [0; 43]; + unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) }; + unified::Receiver::Sapling(data) + } + unified::Typecode::P2sh => { + let mut data = [0; 20]; + unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) }; + unified::Receiver::P2sh(data) + } + unified::Typecode::P2pkh => { + let mut data = [0; 20]; + unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) }; + unified::Receiver::P2pkh(data) + } + unified::Typecode::Unknown(typecode) => { + let data_len = unsafe { (receiver_len_cb.unwrap())(ua_obj, i) }; + let mut data = vec![0; data_len]; + unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data_len) }; + unified::Receiver::Unknown { typecode, data } + } + }, + ) + }) + .collect::>() + { + Ok(receivers) => receivers, + Err(e) => { + tracing::error!("{}", e); + return ptr::null_mut(); + } + }; let ua: unified::Address = match receivers.try_into() { Ok(ua) => ua, diff --git a/src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs b/src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs index 74c5552ede8..370ae521c47 100644 --- a/src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs +++ b/src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs @@ -5,7 +5,7 @@ use incrementalmerkletree::{ use std::mem::size_of_val; use std::ptr; -use orchard::{bundle::Authorized, tree::MerkleCrhOrchardOutput}; +use orchard::{bundle::Authorized, tree::MerkleHashOrchard}; use zcash_primitives::{ merkle_tree::incremental::{read_frontier_v1, read_tree, write_frontier_v1, write_tree}, @@ -23,15 +23,15 @@ pub const MAX_CHECKPOINTS: usize = 100; #[no_mangle] pub extern "C" fn orchard_merkle_frontier_empty( -) -> *mut bridgetree::Frontier { - let empty_tree = bridgetree::Frontier::::empty(); +) -> *mut bridgetree::Frontier { + let empty_tree = bridgetree::Frontier::::empty(); Box::into_raw(Box::new(empty_tree)) } #[no_mangle] pub extern "C" fn orchard_merkle_frontier_clone( - tree: *const bridgetree::Frontier, -) -> *mut bridgetree::Frontier { + tree: *const bridgetree::Frontier, +) -> *mut bridgetree::Frontier { unsafe { tree.as_ref() } .map(|tree| Box::into_raw(Box::new(tree.clone()))) .unwrap_or(std::ptr::null_mut()) @@ -39,7 +39,7 @@ pub extern "C" fn orchard_merkle_frontier_clone( #[no_mangle] pub extern "C" fn orchard_merkle_frontier_free( - tree: *mut bridgetree::Frontier, + tree: *mut bridgetree::Frontier, ) { if !tree.is_null() { drop(unsafe { Box::from_raw(tree) }); @@ -50,7 +50,7 @@ pub extern "C" fn orchard_merkle_frontier_free( pub extern "C" fn orchard_merkle_frontier_parse( stream: Option, read_cb: Option, -) -> *mut bridgetree::Frontier { +) -> *mut bridgetree::Frontier { let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); match read_frontier_v1(reader) { @@ -64,7 +64,7 @@ pub extern "C" fn orchard_merkle_frontier_parse( #[no_mangle] pub extern "C" fn orchard_merkle_frontier_serialize( - frontier: *const bridgetree::Frontier, + frontier: *const bridgetree::Frontier, stream: Option, write_cb: Option, ) -> bool { @@ -86,7 +86,7 @@ pub extern "C" fn orchard_merkle_frontier_serialize( #[no_mangle] pub extern "C" fn orchard_merkle_frontier_append_bundle( - tree: *mut bridgetree::Frontier, + tree: *mut bridgetree::Frontier, bundle: *const orchard::Bundle, ) -> bool { let tree = unsafe { @@ -95,7 +95,7 @@ pub extern "C" fn orchard_merkle_frontier_append_bundle( }; if let Some(bundle) = unsafe { bundle.as_ref() } { for action in bundle.actions().iter() { - if !tree.append(&MerkleCrhOrchardOutput::from_cmx(action.cmx())) { + if !tree.append(&MerkleHashOrchard::from_cmx(action.cmx())) { error!("Orchard note commitment tree is full."); return false; } @@ -107,7 +107,7 @@ pub extern "C" fn orchard_merkle_frontier_append_bundle( #[no_mangle] pub extern "C" fn orchard_merkle_frontier_root( - tree: *const bridgetree::Frontier, + tree: *const bridgetree::Frontier, root_ret: *mut [u8; 32], ) { let tree = unsafe { @@ -126,7 +126,7 @@ pub extern "C" fn orchard_merkle_frontier_root( #[no_mangle] pub extern "C" fn orchard_merkle_frontier_num_leaves( - tree: *const bridgetree::Frontier, + tree: *const bridgetree::Frontier, ) -> usize { let tree = unsafe { tree.as_ref() @@ -138,7 +138,7 @@ pub extern "C" fn orchard_merkle_frontier_num_leaves( #[no_mangle] pub extern "C" fn orchard_merkle_frontier_dynamic_mem_usage( - tree: *const bridgetree::Frontier, + tree: *const bridgetree::Frontier, ) -> usize { let tree = unsafe { tree.as_ref() @@ -155,15 +155,15 @@ pub extern "C" fn orchard_merkle_frontier_dynamic_mem_usage( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_empty( -) -> *mut BridgeTree { - let empty_tree = BridgeTree::::new(MAX_CHECKPOINTS); +) -> *mut BridgeTree { + let empty_tree = BridgeTree::::new(MAX_CHECKPOINTS); Box::into_raw(Box::new(empty_tree)) } #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_clone( - tree: *const BridgeTree, -) -> *mut BridgeTree { + tree: *const BridgeTree, +) -> *mut BridgeTree { unsafe { tree.as_ref() } .map(|tree| Box::into_raw(Box::new(tree.clone()))) .unwrap_or(std::ptr::null_mut()) @@ -171,7 +171,7 @@ pub extern "C" fn incremental_sinsemilla_tree_clone( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_free( - tree: *mut BridgeTree, + tree: *mut BridgeTree, ) { if !tree.is_null() { drop(unsafe { Box::from_raw(tree) }); @@ -182,7 +182,7 @@ pub extern "C" fn incremental_sinsemilla_tree_free( pub extern "C" fn incremental_sinsemilla_tree_parse( stream: Option, read_cb: Option, -) -> *mut BridgeTree { +) -> *mut BridgeTree { let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); match read_tree(reader) { @@ -196,7 +196,7 @@ pub extern "C" fn incremental_sinsemilla_tree_parse( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_serialize( - tree: *const BridgeTree, + tree: *const BridgeTree, stream: Option, write_cb: Option, ) -> bool { @@ -217,7 +217,7 @@ pub extern "C" fn incremental_sinsemilla_tree_serialize( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_append_bundle( - tree: *mut BridgeTree, + tree: *mut BridgeTree, bundle: *const orchard::Bundle, ) -> bool { let tree = unsafe { @@ -226,7 +226,7 @@ pub extern "C" fn incremental_sinsemilla_tree_append_bundle( }; if let Some(bundle) = unsafe { bundle.as_ref() } { for action in bundle.actions().iter() { - if !tree.append(&MerkleCrhOrchardOutput::from_cmx(action.cmx())) { + if !tree.append(&MerkleHashOrchard::from_cmx(action.cmx())) { error!("Orchard note commitment tree is full."); return false; } @@ -238,7 +238,7 @@ pub extern "C" fn incremental_sinsemilla_tree_append_bundle( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_checkpoint( - tree: *mut BridgeTree, + tree: *mut BridgeTree, ) { let tree = unsafe { tree.as_mut() @@ -250,7 +250,7 @@ pub extern "C" fn incremental_sinsemilla_tree_checkpoint( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_rewind( - tree: *mut BridgeTree, + tree: *mut BridgeTree, ) -> bool { let tree = unsafe { tree.as_mut() @@ -262,7 +262,7 @@ pub extern "C" fn incremental_sinsemilla_tree_rewind( #[no_mangle] pub extern "C" fn incremental_sinsemilla_tree_root( - tree: *const BridgeTree, + tree: *const BridgeTree, root_ret: *mut [u8; 32], ) { let tree = unsafe { @@ -289,7 +289,7 @@ pub extern "C" fn incremental_sinsemilla_tree_empty_root(root_ret: *mut [u8; 32] let altitude = Altitude::from(MERKLE_DEPTH); - let digest = MerkleCrhOrchardOutput::empty_root(altitude).to_bytes(); + let digest = MerkleHashOrchard::empty_root(altitude).to_bytes(); *root_ret = digest; } diff --git a/src/test/data/unified_addrs.json b/src/test/data/unified_addrs.json index dd14807df9b..899a162a052 100644 --- a/src/test/data/unified_addrs.json +++ b/src/test/data/unified_addrs.json @@ -1,15 +1,14 @@ [ ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_address.py"], ["p2pkh_bytes, p2sh_bytes, sapling_raw_addr, orchard_raw_addr, unified_addr"], - [null, "7a8f739a2d9e945b0ce152a8049e294c4d6e66b1", null, "dcb1d2a37762148db4cee3bbf19fb1ec05891894b13801c622ba6a90faf1119f8224ae3985c6abd3b7bbae", "75316532386638787a6e656d676574797872647a6b66676a756773667839646b713274686e657a65396c333474707674706d656a656774643567686b7768676a7268736466646e74336b34736632757939656138637461756d7a7075646c657a356667756c77716b70753566766a77796374747a79646c383664373434613430617175747a687a7674797a6e68"], - ["b3534201cfb1cd8dbf69b8250c18ef41294ca979", null, "902b6565a1c44e7e7a080571af1dd774697cc126f1fc0435d3cdbf868783e9fb4620df4bf175cbf2c3e36f", "05f61273a7201295332fee4579474534809a0aeb817a2bc0594166ad7a462067712533b6eec0fa2d1be99f", "75313371733278796b66737464343332777076366368367336636b683473346833736372746435686b6179367277366a6e3667656e3734726d76636136357775736771653637716d6b6b667a79686c6b7767383071796a33726363616e68356667377a73393032793234676e3271796636706b636363686730356a7a70337972666e3470753861746538673771397239737465767832383434357235776e3378333570663771713332346e746878747a6377786576796661346c326d326d327873376564766570396674613476747776686b617878"], - [null, "e8c7203d996af7d477083756d59af80d06a745f4", null, "4ea7d6b3dfa338192af06cbbf47ad405715bc7832bedb1466217dc0d93314de9f3c25eec89f9a21bfe0e93", "75317270776b64357837746534657a636d6c716172676b66336372737066716b686d787766737170713032346a6173733267373465347233367a70353071737333766863743732736163687573306b37303578796768756c726a786364777764617130716a6e6e737366326676616b7a7a386634367077387263613933366665796566366d6c36657137776c6c"], - [null, null, "02f1536b622c01346742d8f90e9d4ff39137f1bebe6e23ad9971776b3372702494cc08951eef032b35350f", null, "7531396468786d38386776637739747774767a6b7537637a376a753372767075356a766832637138676d38716465766e6c34657365793833667a7239796d666133676a6d6d6b3472786a3279703838677478646b71676e6a366e6e717974667568303875796335667572"], - [null, "183e31d49f25c9a138f49b1a537edcf04be34a98", "3246b59a5b492dab1855cc176bddfa28418f11f97f7b361cc3e8834b2c30d2a1717df323ef98ea7de71d2e", "ab6d26252c521547049de208283d96278bb221a6874cb5a86af1d3f8b3db3fbee3dbefedcb2c71e3ca1ead", "75313563327972666a7774743636336e713068707173683678746a793238387a7476616375616c37336d666675723338336d7465706339687278743374703465613568373830706733686b35756a326775393770326c756d39716a6836327872613763336879366e6471327877686737657138713736766632386333326437766d6c337a323934767438656374636c773672307032336e64703578647a666578633971773065777572796e6a353235736b78376d306a756b636a7265616b6a3261703332376b6a7172767567656435667930737a79"], - [null, null, "970dc3450d34554141d356cb548056279c57708fa73bd16ffe9a2e24ea694898a7b8af1b0ff92585d02623", "0414bb62b86149ee731851f27d532ac0361169da46e6d53d19d3dfd07a5bae22969922d8d0af7dc1e13bae", "75317a6467397a376c7732713037357977396e306a376668387279756a73726a727237347a71783939337463736874666d6d3271786b74763372303438653774723471356a38736665703367396e76387770786639767472336a7a6e6d6d35367a3775706a6530356b7a766d75726d706371703034633033377a32646833356364383232357874336b34376e7a6668327974737630376d337438676433337035723276376b66656739343571376a63306768"], - [null, "098b79535e790fe53e29fef2b3766697ac32b4f4", "a8a8797c1ba69f78672affa65b943975026931ea628431f0991e744872ac9f36946f5dcd6851a0b5af29cf", "678ab0079bea28bf165c1ab976a2a58c18a7811ca2ad0ad649e876273d04325da6ca53cdb83c111e8e4394", "7531796a7733366d7a777a346a643879746e726436676b68376179676d656a3272746c716733766a397965656866646b3233356a657336727170666a6c777167333776346d653575676a6c7a72646e68676c383872726c6567383378386130716d77797a7936393872387675636e346877616772367163657a7373663663683074373466633068766e366d373737687037353761766632636b7a63333239357033323430716c7675707964656c37336c377163746e343063796b357536736573617772367a6d6b746c6a6678687163633765773468"], - [null, null, "3509c9e069e89fe501d97622c283ac98923da2d7e6eb346b4bafa67865e1e6dae7cf213b1ea3648dc09b48", null, "753171786b796d6b68647a6372727a6c79346672717563646b66736e6a377776776c716575306b3466337472706e657a3476777a75686b64616a6b356537363437346d6a34396b75653978396866776566386d34377363633861703933686e6773657567333864706371"], - [null, "30d069896cff30eb414f727b89e001afa2fb8dc3", "55bc46aea6f60c1d61915640029b2af6334d7d27e1c47a248ab47c9fbe5d2d7bb5818739f062e37136654c", null, "75316433726b71777a396b37346b7475766c746e32746d746a36676b6c6739336877733937797473337a75363038766b76673363327973686d6d3330713668386b6434727433733076703475616a776668396137356538613837306e39346c6b70657835617337716e77333436346e7261747563356e35793575766e7a7666326d3466656a687a6576686e306d"], - [null, null, "5c26a8117729334a957ca7941d47b2ce7040e844fa9882c25bfd2fcf51fa8ab21376f5300d0123f5703e9e", null, "75316166617430647574617379666b736161766d34726c336178716e75786c777437363573393476397a716a7430397a793633646c356d6d7970397835636b7672756c6d647134636b6a337272763766396c37756e78666c306438723635383234746773677276673071"] + [null, "7a8f739a2d9e945b0ce152a8049e294c4d6e66b1", null, "dcb1d2a37762148db4cee3bbf19fb1ec05891894b13801c622ba6a90faf1119f8224ae3985c6abd3b7bbae", "7531743779336e647634367476636465377834363835616a6d676b353970643039326c68733477616e776c646834666d3775636a723074343468667a356a6375667a75616b65667a7571767a376a30617974736e6677347065796478387538746a7371347738756b7336727973633635356d786b6a367778786870676b6e79733533657978706b383978797363"], + ["b3534201cfb1cd8dbf69b8250c18ef41294ca979", null, "902b6565a1c44e7e7a080571af1dd774697cc126f1fc0435d3cdbf868783e9fb4620df4bf175cbf2c3e36f", "05f61273a7201295332fee4579474534809a0aeb817a2bc0594166ad7a462067712533b6eec0fa2d1be99f", "75316665686b656a646d3630613871353567736576716c78676632396a38676c377364393436756e6a73346e6c343473633434307274657435756771373264637839706a7468396c323372706a7566757a63776837357971657a753235617532383970717274616c713637776e343066787177306c6e377a78777a397973787575307a39337772676e6d6671376b33666e36747a63307637727a396733706a67777571746c6b7a786873783839617a39656868336c356a357175757572776e376e33746a786c616e3235726e7134676a7a61387436"], + [null, "e8c7203d996af7d477083756d59af80d06a745f4", null, "4ea7d6b3dfa338192af06cbbf47ad405715bc7832bedb1466217dc0d93314de9f3c25eec89f9a21bfe0e93", "7531646a686778773579747a7463356d75323272346d733567796d3864776867383432326667757879766c7465787638787572677338637230756b7264797337667668716d6d6173616667666530677435776e306d756a6e63333467746d637734397565646b68707a79787a34397a7678356339757130686a64356e37747267736e656a686a716d7376723663"], + [null, null, "02f1536b622c01346742d8f90e9d4ff39137f1bebe6e23ad9971776b3372702494cc08951eef032b35350f", null, "75313363366d36716e657a72337966753468757630356e687961356372786e35347861786a78376d6b6674393861796e7a336b68636e6176647961306c746a797565717a3577706d306d7a6a357a646c343464323076657a6768753272743861737635636c6133746463"], + [null, "183e31d49f25c9a138f49b1a537edcf04be34a98", "3246b59a5b492dab1855cc176bddfa28418f11f97f7b361cc3e8834b2c30d2a1717df323ef98ea7de71d2e", "ab6d26252c521547049de208283d96278bb221a6874cb5a86af1d3f8b3db3fbee3dbefedcb2c71e3ca1ead", "75313565326e323536746b636837396677797a766c66337378666c66727772666d3664336d6539306e763932666b6575776e653477737975657a656e71356a7368716e6730396e776c7930353735717370373668687878776b6a6373736b6365666c38356c307465793338773236746c6c6575787a66676d38703732727435373033637238787579307577376c7263746e6767396b377a75387a75667576397178633674796a7278337833676a76796b646d79616d34797633793471376a666c783070306d39347066737739687a6b376132387079"], + [null, null, "970dc3450d34554141d356cb548056279c57708fa73bd16ffe9a2e24ea694898a7b8af1b0ff92585d02623", "0414bb62b86149ee731851f27d532ac0361169da46e6d53d19d3dfd07a5bae22969922d8d0af7dc1e13bae", "75316b616c6b6d3276717267716d386c6c71657568353372777a6c657435676561346d6b647739376a3970613372636574767a76336c6b6b33653075333534337670746774326b3075616638383630386675337563656676776577797a3974717765616a3377756c6c30706437306d746e687977656b306c3576617a6361366577663472646d7778713866327334706d35687175366d6567397a6774677a76393872787a6832777835766576797330346573"], + [null, "098b79535e790fe53e29fef2b3766697ac32b4f4", "a8a8797c1ba69f78672affa65b943975026931ea628431f0991e744872ac9f36946f5dcd6851a0b5af29cf", "678ab0079bea28bf165c1ab976a2a58c18a7811ca2ad0ad649e876273d04325da6ca53cdb83c111e8e4394", "75316d30767a6e3738676830796d71356b717a346c7064343039357770756538686e687a7373357a34736c3338737a6e38767736683934787576636a763561686b6e706d6c72613637756563766e6b6d73397966717834733768736e737a7a6d63706776787232306c6834353970733375337766366d727a77653678396b646a3874703061366373656432656d7870367137716e7761746432786d3470736a683472346739706866663534747375786535373638746873787373617678726679747064706d37326b6a7372797061797a7767713978"], + [null, null, "3509c9e069e89fe501d97622c283ac98923da2d7e6eb346b4bafa67865e1e6dae7cf213b1ea3648dc09b48", null, "7531357676383834637a356436346e677232717634307835797171677a366a743368657a75396d6b7566323064756a61663871766b736c6e78793738663264707879323470767637797666637a6b6c307761653435613070683664377a37646574336a67347267677866"], + [null, "30d069896cff30eb414f727b89e001afa2fb8dc3", "55bc46aea6f60c1d61915640029b2af6334d7d27e1c47a248ab47c9fbe5d2d7bb5818739f062e37136654c", null, "753137766a736b6c7234756a6463386c307a686b646d6b71717778343833673861363432653937366c7466733073346a65643761673376336138726e677275366e653438687a7233336b6b7a70366d61677a336679656a777a6a6734336a6470657274667078656e326661747a356b3830636a6b6c6e39713772396d3033646e366736306332796c3336393474"], + [null, null, "5c26a8117729334a957ca7941d47b2ce7040e844fa9882c25bfd2fcf51fa8ab21376f5300d0123f5703e9e", null, "75313976636e33726564706170687834326d6e307379633236793877397766676c657a396175736b61787267686d78383064756e61333663616763337973376d6e336a373639766367387275336b646e61713470683436303438646873766c35646d6473677879653833"] ] - diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index dd9893e69fb..d827a395a25 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -69,25 +69,25 @@ bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) { return !std::holds_alternative(zkey); } -uint8_t TypecodeForReceiver::operator()( +uint32_t TypecodeForReceiver::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { return ZCASH_UA_TYPECODE_SAPLING; } -uint8_t TypecodeForReceiver::operator()( +uint32_t TypecodeForReceiver::operator()( const libzcash::P2SHAddress &p2sh) const { return ZCASH_UA_TYPECODE_P2SH; } -uint8_t TypecodeForReceiver::operator()( +uint32_t TypecodeForReceiver::operator()( const libzcash::P2PKHAddress &p2sh) const { return ZCASH_UA_TYPECODE_P2PKH; } -uint8_t TypecodeForReceiver::operator()( +uint32_t TypecodeForReceiver::operator()( const libzcash::UnknownReceiver &unknown) const { return unknown.typecode; diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index d47c5ec4470..38d416ee2e9 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -32,10 +32,10 @@ class InvalidEncoding { class UnknownReceiver { public: - uint8_t typecode; + uint32_t typecode; std::vector data; - UnknownReceiver(uint8_t typecode, std::vector data) : + UnknownReceiver(uint32_t typecode, std::vector data) : typecode(typecode), data(data) {} friend inline bool operator==(const UnknownReceiver& a, const UnknownReceiver& b) { @@ -176,10 +176,10 @@ class TypecodeForReceiver { public: TypecodeForReceiver() {} - uint8_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - uint8_t operator()(const libzcash::P2SHAddress &p2sh) const; - uint8_t operator()(const libzcash::P2PKHAddress &p2pkh) const; - uint8_t operator()(const libzcash::UnknownReceiver &p2pkh) const; + uint32_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + uint32_t operator()(const libzcash::P2SHAddress &p2sh) const; + uint32_t operator()(const libzcash::P2PKHAddress &p2pkh) const; + uint32_t operator()(const libzcash::UnknownReceiver &p2pkh) const; }; /** From 9803111210257157ce273eef4398fb827cae0de0 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 17 Sep 2021 13:36:45 +0100 Subject: [PATCH 018/514] contrib: Add script for generating a graph of our Rust dependencies Co-authored-by: Daira Hopwood --- contrib/devtools/rust-deps-graph.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 contrib/devtools/rust-deps-graph.sh diff --git a/contrib/devtools/rust-deps-graph.sh b/contrib/devtools/rust-deps-graph.sh new file mode 100755 index 00000000000..a78cda5c1be --- /dev/null +++ b/contrib/devtools/rust-deps-graph.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +export LC_ALL=C + +cargo deps --no-transitive-deps | dot \ + -Earrowhead=vee \ + -Gratio=0.45 \ + -Gsize=50 \ + -Nheight=0.8 \ + -Nfillcolor=LavenderBlush \ + -Nstyle=filled \ + -Nfontsize=18 \ + -Nmargin=0.05,0.05 \ + -Tpng > rust-dependency-graph.png From d087c8b0894004bc923f7a7cb36de6ef7f72b76d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 17 Sep 2021 13:52:09 +0100 Subject: [PATCH 019/514] cargo update --- Cargo.lock | 118 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67490b6d81a..f62b15708df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -229,6 +229,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + [[package]] name = "byteorder" version = "1.4.3" @@ -447,16 +453,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ctor" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -802,6 +798,15 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "jubjub" version = "0.8.0" @@ -824,9 +829,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.101" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" [[package]] name = "libm" @@ -958,9 +963,9 @@ dependencies = [ [[package]] name = "metrics-exporter-prometheus" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db7052a7cb0b0c0922a1ce499c9e78576763b1d47499ba980a4fe731b96909d" +checksum = "343a5ceb38235928e7a5687412590f07e6d281522dcd9ff51246f8856eef5fe5" dependencies = [ "hyper", "ipnet", @@ -988,9 +993,9 @@ dependencies = [ [[package]] name = "metrics-util" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58e622a425b7308e73e4390c68b8cb3df4443f2a57495e391b978412531638c" +checksum = "74c9b6aee519e1461b678952d3671652bb341d0664b1188f895a436a4e2e6ffa" dependencies = [ "ahash", "aho-corasick", @@ -1278,16 +1283,17 @@ dependencies = [ [[package]] name = "quanta" -version = "0.7.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e76a3afdefd0ce2c0363bf3146271e947782240ea617885dd64e56c4de9fb3c9" +checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" dependencies = [ - "atomic-shim", - "ctor", + "crossbeam-utils 0.8.5", "libc", "mach", "once_cell", "raw-cpuid", + "wasi 0.10.2+wasi-snapshot-preview1", + "web-sys", "winapi", ] @@ -1367,9 +1373,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "9.1.1" +version = "10.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1733f6f80c9c24268736a501cd00d41a9849b4faa7a9f9334c096e5d10553206" +checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2" dependencies = [ "bitflags", ] @@ -1703,9 +1709,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62af966210b88ad5776ee3ba12d5f35b8d6a2b2a12168f3080cf02b814d7376b" +checksum = "56c42e73a9d277d4d2b6a88389a137ccf3c58599660b17e8f5fc39305e490669" dependencies = [ "ansi_term", "chrono", @@ -1783,6 +1789,70 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" From 9c20d7405fab40f030fced7b8772f306ba3e0cfa Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 20 Sep 2021 15:49:28 +0100 Subject: [PATCH 020/514] build: Add primitives/orchard.h to list of header files --- src/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile.am b/src/Makefile.am index 5f4ee7270c4..03485657962 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -180,6 +180,7 @@ BITCOIN_CORE_H = \ pow.h \ prevector.h \ primitives/block.h \ + primitives/orchard.h \ primitives/transaction.h \ proof_verifier.h \ protocol.h \ From 49cacbc49fed8b8e41d37a1b2cbc2e5205f03a73 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 20 Sep 2021 19:11:55 +0100 Subject: [PATCH 021/514] build: Ensure that cargo uses vendored dependencies for git repos --- .cargo/config.offline | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.cargo/config.offline b/.cargo/config.offline index a6339df37f3..f1bb1971052 100644 --- a/.cargo/config.offline +++ b/.cargo/config.offline @@ -1,5 +1,35 @@ [source.crates-io] replace-with = "vendored-sources" +[source."https://github.com/ZcashFoundation/ed25519-zebra.git"] +git = "https://github.com/ZcashFoundation/ed25519-zebra.git" +rev = "d3512400227a362d08367088ffaa9bd4142a69c7" +replace-with = "vendored-sources" + +[source."https://github.com/str4d/redjubjub.git"] +git = "https://github.com/str4d/redjubjub.git" +rev = "416a6a8ebf8bd42c114c938883016c04f338de72" +replace-with = "vendored-sources" + +[source."https://github.com/zcash/halo2.git"] +git = "https://github.com/zcash/halo2.git" +rev = "26047eaf323929935fd1e6aa3ae100b1113706e0" +replace-with = "vendored-sources" + +[source."https://github.com/zcash/incrementalmerkletree"] +git = "https://github.com/zcash/incrementalmerkletree" +rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" +replace-with = "vendored-sources" + +[source."https://github.com/zcash/librustzcash.git"] +git = "https://github.com/zcash/librustzcash.git" +rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" +replace-with = "vendored-sources" + +[source."https://github.com/zcash/orchard.git"] +git = "https://github.com/zcash/orchard.git" +rev = "f8280c98a3d0e41b8ff5b7f615802bd197f781e1" +replace-with = "vendored-sources" + [source.vendored-sources] # The directory for this source is set to RUST_VENDORED_SOURCES by src/Makefile.am From 416a4d453fb388221a255778521a43fb58d248ac Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 20 Sep 2021 20:15:38 +0100 Subject: [PATCH 022/514] build: Add missing source file to zcash_gtest_SOURCES --- src/Makefile.gtest.include | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 1cda367e22b..7c088f2fa7c 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -24,6 +24,7 @@ zcash_gtest_SOURCES += \ wallet/gtest/test_wallet_zkeys.cpp endif zcash_gtest_SOURCES += \ + test/data/merkle_roots_orchard.h \ gtest/test_tautology.cpp \ gtest/test_allocator.cpp \ gtest/test_checkblock.cpp \ From f9a1986e4324df56d263a5cb9d647d5248915017 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Sep 2021 12:36:01 +0100 Subject: [PATCH 023/514] rust: Move Orchard batch logic into BatchValidator methods --- src/rust/src/orchard_ffi.rs | 72 ++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/src/rust/src/orchard_ffi.rs b/src/rust/src/orchard_ffi.rs index db280a43dec..d474f736a15 100644 --- a/src/rust/src/orchard_ffi.rs +++ b/src/rust/src/orchard_ffi.rs @@ -200,6 +200,43 @@ impl BatchValidator { fn new() -> Self { BatchValidator { signatures: vec![] } } + + fn add_bundle(&mut self, bundle: &Bundle, txid: TxId) { + for action in bundle.actions().iter() { + self.signatures.push(BundleSignature { + signature: action + .rk() + .create_batch_item(action.authorization().clone(), txid.as_ref()), + }); + } + + self.signatures.push(BundleSignature { + signature: bundle.binding_validating_key().create_batch_item( + bundle.authorization().binding_signature().clone(), + txid.as_ref(), + ), + }); + } + + fn validate(&self) -> bool { + let mut validator = redpallas::batch::Verifier::new(); + for sig in self.signatures.iter() { + validator.queue(sig.signature.clone()); + } + + match validator.verify(OsRng) { + Ok(()) => true, + Err(e) => { + error!("RedPallas batch validation failed: {}", e); + // TODO: Try sub-batches to figure out which signatures are invalid. We can + // postpone this for now: + // - For per-transaction batching (when adding to the mempool), we don't care + // which signature within the transaction failed. + // - For per-block batching, we currently don't care which transaction failed. + false + } + } + } } /// Creates a RedPallas batch validation context. @@ -234,22 +271,7 @@ pub extern "C" fn orchard_batch_add_bundle( let txid = unsafe { txid.as_ref() }.cloned().map(TxId::from_bytes); match (batch, bundle, txid) { - (Some(batch), Some(bundle), Some(txid)) => { - for action in bundle.actions().iter() { - batch.signatures.push(BundleSignature { - signature: action - .rk() - .create_batch_item(action.authorization().clone(), txid.as_ref()), - }); - } - - batch.signatures.push(BundleSignature { - signature: bundle.binding_validating_key().create_batch_item( - bundle.authorization().binding_signature().clone(), - txid.as_ref(), - ), - }); - } + (Some(batch), Some(bundle), Some(txid)) => batch.add_bundle(bundle, txid), (_, _, None) => error!("orchard_batch_add_bundle() called without txid!"), (Some(_), None, Some(txid)) => debug!("Tx {} has no Orchard component", txid), (None, Some(_), _) => debug!("Orchard BatchValidator not provided, assuming disabled."), @@ -264,23 +286,7 @@ pub extern "C" fn orchard_batch_add_bundle( #[no_mangle] pub extern "C" fn orchard_batch_validate(batch: *const BatchValidator) -> bool { if let Some(batch) = unsafe { batch.as_ref() } { - let mut validator = redpallas::batch::Verifier::new(); - for sig in batch.signatures.iter() { - validator.queue(sig.signature.clone()); - } - - match validator.verify(OsRng) { - Ok(()) => true, - Err(e) => { - error!("RedPallas batch validation failed: {}", e); - // TODO: Try sub-batches to figure out which signatures are invalid. We can - // postpone this for now: - // - For per-transaction batching (when adding to the mempool), we don't care - // which signature within the transaction failed. - // - For per-block batching, we currently don't care which transaction failed. - false - } - } + batch.validate() } else { // The orchard::BatchValidator C++ class uses null to represent a disabled batch // validator. From 092ffed2cc6dcdcbd09257e0be6d2fc199582e37 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Sep 2021 14:01:53 +0100 Subject: [PATCH 024/514] wallet: Batch-validate all Orchard signatures in the wallet on load A batch item is 128 bytes, so for a wallet with 200k Orchard actions this would require around 25MiB, which is reasonable. --- src/wallet/walletdb.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 6643100b909..b3d74b0acf9 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -298,8 +298,9 @@ class CWalletScanState { bool fAnyUnordered; int nFileVersion; vector vWalletUpgrade; + orchard::AuthValidator orchardAuth; - CWalletScanState() { + CWalletScanState(): orchardAuth(orchard::AuthValidator::Batch()) { nKeys = nCKeys = nKeyMeta = nZKeys = nCZKeys = nZKeyMeta = nSapZAddrs = 0; fIsEncrypted = false; fAnyUnordered = false; @@ -338,11 +339,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> wtx; CValidationState state; auto verifier = ProofVerifier::Strict(); - auto orchardAuth = orchard::AuthValidator::Batch(); if (!( - CheckTransaction(wtx, state, verifier, orchardAuth) && + CheckTransaction(wtx, state, verifier, wss.orchardAuth) && (wtx.GetHash() == hash) && - orchardAuth.Validate() && state.IsValid()) ) { return false; @@ -802,6 +801,12 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) LogPrintf("%s\n", strErr); } pcursor->close(); + + // Run the Orchard batch validator; if it fails, treat it like a bad transaction record. + if (!wss.orchardAuth.Validate()) { + fNoncriticalErrors = true; + SoftSetBoolArg("-rescan", true); + } } catch (const boost::thread_interrupted&) { throw; From 03ecb1330045abc9a13fc7ee1d3286863deb9f2f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Sep 2021 14:52:47 +0100 Subject: [PATCH 025/514] rust: Skip running the Orchard batch validator on an empty batch An empty batch will always succeed, but costs a 2-base multi-scaler multiplication. --- src/rust/src/orchard_ffi.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rust/src/orchard_ffi.rs b/src/rust/src/orchard_ffi.rs index d474f736a15..7bfea4080f7 100644 --- a/src/rust/src/orchard_ffi.rs +++ b/src/rust/src/orchard_ffi.rs @@ -219,6 +219,11 @@ impl BatchValidator { } fn validate(&self) -> bool { + if self.signatures.is_empty() { + // An empty batch is always valid, but is not free to run; skip it. + return true; + } + let mut validator = redpallas::batch::Verifier::new(); for sig in self.signatures.iter() { validator.queue(sig.signature.clone()); From 5731ba431c0964251279ce7c861a5ad9829d5093 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Sep 2021 17:32:17 +0100 Subject: [PATCH 026/514] bench: Add Orchard logic to zcbenchmarks The missing `ORCHARD` case in `FakeCoinsViewDB::GetBestAnchor` caused an exception to be thrown during the `connectblockslow` benchmark. --- src/zcbenchmarks.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 54a1a256076..ba23c666699 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -448,6 +448,7 @@ class FakeCoinsViewDB : public CCoinsView { uint256 hash; SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; + OrchardMerkleTree orchardTree; public: FakeCoinsViewDB(std::string dbName, uint256& hash) : db(GetDataDir() / dbName, 100, false, false), hash(hash) {} @@ -468,6 +469,14 @@ class FakeCoinsViewDB : public CCoinsView { return false; } + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const { + if (rt == orchardTree.root()) { + tree = orchardTree; + return true; + } + return false; + } + bool GetNullifier(const uint256 &nf, ShieldedType type) const { return false; } @@ -490,6 +499,8 @@ class FakeCoinsViewDB : public CCoinsView { return sproutTree.root(); case SAPLING: return saplingTree.root(); + case ORCHARD: + return orchardTree.root(); default: throw new std::runtime_error("Unknown shielded type"); } @@ -499,10 +510,14 @@ class FakeCoinsViewDB : public CCoinsView { const uint256 &hashBlock, const uint256 &hashSproutAnchor, const uint256 &hashSaplingAnchor, + const uint256 &hashOrchardAnchor, CAnchorsSproutMap &mapSproutAnchors, CAnchorsSaplingMap &mapSaplingAnchors, + CAnchorsOrchardMap &mapOrchardAnchors, CNullifiersMap &mapSproutNullifiers, - CNullifiersMap &mapSaplingNullifiers) { + CNullifiersMap &mapSaplingNullifiers, + CNullifiersMap &mapOrchardNullifiers, + CHistoryCacheMap &historyCacheMap) { return false; } From db8abb7a54f71776f90c3890314745685f57848d Mon Sep 17 00:00:00 2001 From: Charlie O'Keefe Date: Wed, 22 Sep 2021 10:58:34 -0600 Subject: [PATCH 027/514] Add buster to the list of suites used by gitian --- contrib/gitian-descriptors/gitian-linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 4c332d4214d..7c10dd61d61 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -4,6 +4,7 @@ enable_cache: true distro: "debian" suites: - "stretch" +- "buster" architectures: - "amd64" packages: From 660e9384992df43cfdbc9b58cfff1d6af8e8beeb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Sep 2021 22:50:22 +0100 Subject: [PATCH 028/514] cargo update Includes pasta_curves 0.2.1, which relicenses to MIT OR Apache-2.0. --- Cargo.lock | 32 ++++++++++++++++---------------- contrib/debian/copyright | 1 - 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f62b15708df..d6d592c147a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,9 +231,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.7.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" [[package]] name = "byteorder" @@ -704,9 +704,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" dependencies = [ "bytes", "fnv", @@ -1194,9 +1194,9 @@ dependencies = [ [[package]] name = "pasta_curves" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e9f954e56b84a250978f89358bc9f5a68f6c68f1082d41db1ddc9664316ee5" +checksum = "d647d91972bad78120fd61e06b225fcda117805c9bbf17676b51bd03a251278b" dependencies = [ "blake2b_simd", "ff", @@ -1635,9 +1635,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" +checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" dependencies = [ "autocfg", "libc", @@ -1666,9 +1666,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba9ab62b7d6497a8638dfda5e5c4fb3b2d5a7fca4118f2b96151c8ef1a437e" +checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1709,9 +1709,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c42e73a9d277d4d2b6a88389a137ccf3c58599660b17e8f5fc39305e490669" +checksum = "fdd0568dbfe3baf7048b7908d2b32bca0d81cd56bec6d2a8f894b01d74f86be3" dependencies = [ "ansi_term", "chrono", @@ -1983,18 +1983,18 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377db0846015f7ae377174787dd452e1c5f5a9050bc6f954911d01f116daa0cd" +checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1" +checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" dependencies = [ "proc-macro2", "quote", diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 434b7e2f33e..92830bf1140 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -122,7 +122,6 @@ License: Boost-Software-License-1.0 Files: depends/*/vendored-sources/halo2/* depends/*/vendored-sources/orchard/* - depends/*/vendored-sources/pasta_curves/* Copyright: 2020 The Electric Coin Company License: Bootstrap-Open-Source-Licence-1.0 From 00724c9f5a8c515a8961013ab1654bdbddf4cbb1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Sep 2021 23:06:32 +0100 Subject: [PATCH 029/514] Update halo2 and orchard dependencies with BOSL Zcash exception --- .cargo/config.offline | 6 ++--- Cargo.lock | 47 ++++++++++++++++++++++++---------------- Cargo.toml | 14 ++++++------ contrib/debian/copyright | 39 +++++++++++++++++++++++++++++++-- 4 files changed, 75 insertions(+), 31 deletions(-) diff --git a/.cargo/config.offline b/.cargo/config.offline index f1bb1971052..7cc7a813aa3 100644 --- a/.cargo/config.offline +++ b/.cargo/config.offline @@ -13,7 +13,7 @@ replace-with = "vendored-sources" [source."https://github.com/zcash/halo2.git"] git = "https://github.com/zcash/halo2.git" -rev = "26047eaf323929935fd1e6aa3ae100b1113706e0" +rev = "a7cd600eb60b1528159b92af5e426adcc615de1a" replace-with = "vendored-sources" [source."https://github.com/zcash/incrementalmerkletree"] @@ -23,12 +23,12 @@ replace-with = "vendored-sources" [source."https://github.com/zcash/librustzcash.git"] git = "https://github.com/zcash/librustzcash.git" -rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" +rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" replace-with = "vendored-sources" [source."https://github.com/zcash/orchard.git"] git = "https://github.com/zcash/orchard.git" -rev = "f8280c98a3d0e41b8ff5b7f615802bd197f781e1" +rev = "8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0" replace-with = "vendored-sources" [source.vendored-sources] diff --git a/Cargo.lock b/Cargo.lock index d6d592c147a..cd905aa2a5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,9 +87,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64ct" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a96587c05c810ddbb79e2674d519cff1379517e7b91d166dff7a7cc0e9af6e" +checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" [[package]] name = "bech32" @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "bip0039" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac4d1925000f9183da41ed95477ea815512b5bb7e06e6c6964986ca077e2b75c" +checksum = "d0830ae4cc96b0617cc912970c2b17e89456fecbf55e8eed53a956f37ab50c41" dependencies = [ "hmac", "pbkdf2", @@ -487,9 +487,9 @@ dependencies = [ [[package]] name = "directories" -version = "3.0.2" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ "dirs-sys", ] @@ -534,12 +534,20 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "blake2b_simd", "byteorder", ] +[[package]] +name = "f4jumble" +version = "0.0.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +dependencies = [ + "blake2b_simd", +] + [[package]] name = "ff" version = "0.11.0" @@ -658,7 +666,7 @@ dependencies = [ [[package]] name = "halo2" version = "0.0.1" -source = "git+https://github.com/zcash/halo2.git?rev=26047eaf323929935fd1e6aa3ae100b1113706e0#26047eaf323929935fd1e6aa3ae100b1113706e0" +source = "git+https://github.com/zcash/halo2.git?rev=a7cd600eb60b1528159b92af5e426adcc615de1a#a7cd600eb60b1528159b92af5e426adcc615de1a" dependencies = [ "blake2b_simd", "ff", @@ -1115,7 +1123,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.0.0" -source = "git+https://github.com/zcash/orchard.git?rev=f8280c98a3d0e41b8ff5b7f615802bd197f781e1#f8280c98a3d0e41b8ff5b7f615802bd197f781e1" +source = "git+https://github.com/zcash/orchard.git?rev=8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0#8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0" dependencies = [ "aes", "arrayvec 0.7.1", @@ -1183,9 +1191,9 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.2.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" dependencies = [ "base64ct", "rand_core 0.6.3", @@ -1209,9 +1217,9 @@ dependencies = [ [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ "crypto-mac", "password-hash", @@ -1887,18 +1895,19 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "bech32", "blake2b_simd", "bs58", + "f4jumble", "zcash_encoding", ] [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "byteorder", "nonempty", @@ -1907,7 +1916,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "bigint", "blake2b_simd", @@ -1917,7 +1926,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "blake2b_simd", "byteorder", @@ -1932,7 +1941,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "aes", "bip0039", @@ -1966,7 +1975,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=ba3f13bbedd95908419f104f67b9fcd3b3e13111#ba3f13bbedd95908419f104f67b9fcd3b3e13111" +source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 9a46be4a987..bf16d477075 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,11 +69,11 @@ codegen-units = 1 [patch.crates-io] ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } -halo2 = { git = "https://github.com/zcash/halo2.git", rev = "26047eaf323929935fd1e6aa3ae100b1113706e0" } +halo2 = { git = "https://github.com/zcash/halo2.git", rev = "a7cd600eb60b1528159b92af5e426adcc615de1a" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -orchard = { git = "https://github.com/zcash/orchard.git", rev = "f8280c98a3d0e41b8ff5b7f615802bd197f781e1" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "ba3f13bbedd95908419f104f67b9fcd3b3e13111" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 92830bf1140..e1970d6eedb 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -122,8 +122,8 @@ License: Boost-Software-License-1.0 Files: depends/*/vendored-sources/halo2/* depends/*/vendored-sources/orchard/* -Copyright: 2020 The Electric Coin Company -License: Bootstrap-Open-Source-Licence-1.0 +Copyright: 2020-2021 The Electric Coin Company +License: BOSL-1.0-or-later-with-Zcash-exception Files: src/crypto/ctaes/* Copyright: Copyright (c) 2016 Pieter Wuille @@ -1157,6 +1157,41 @@ License: Expat-with-advertising-clause promote the sale, use or other dealings in this Software without prior written authorization from the authors. +License: BOSL-1.0-or-later-with-Zcash-exception + This package ("Original Work") is licensed under the terms of the Bootstrap Open + Source License, version 1.0, or at your option, any later version ("BOSL"). See + the file ./LICENSE-BOSL for the terms of the Bootstrap Open Source Licence, + version 1.0. + . + Only if this Original Work is included as part of the distribution of one of the + following projects ("the Project"): + . + - The Zcash projects published by the Electric Coin Company, + - The Zebra project published by the Zcash Foundation, + . + then License is granted to use this package under the BOSL as modified by the + following clarification and special exception. This exception applies only to + the Original Work when linked or combined with the Project and not to the + Original Work when linked, combined, or included in or with any other software + or project or on a standalone basis. + . + Under the terms of the BOSL, linking or combining this Original Work with + the Project creates a Derivative Work based upon the Original Work and the + terms of the BOSL thus apply to both the Original Work and that Derivative + Work. As a special exception to the BOSL, and to allow this Original Work to + be linked and combined with the Project without having to apply the BOSL to + the other portions of the Project, you are granted permission to link or + combine this Original Work with the Project and to copy and distribute the + resulting work ("Resulting Work") under the open source license applicable + to the Project ("Project License"), provided that any portions of this + Original Work included in the Resulting Work remain subject to the BOSL. For + clarity, you may continue to treat all other portions of the Project under + the Project License, provided that you comply with the BOSL with respect to + the Original Work. If you modify this Original Work, your version of the + Original Work must remain under the BOSL. You may also extend this exception + to your version, but you are not obligated to do so. If you do not wish to + do so, delete this exception statement from your version. + License: Bootstrap-Open-Source-Licence-1.0 ======================================================= Bootstrap Open Source Licence ("BOSL") v. 1.0 From 1bd9152daa1b62aeeee70845d6300204fceb66f9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Sep 2021 04:12:06 +0100 Subject: [PATCH 030/514] make-release.py: Versioning changes for 4.5.0. --- README.md | 2 +- configure.ac | 2 +- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 2 +- src/deprecation.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 66528e79bcd..40c59bf2ece 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.5.0-rc1 +Zcash 4.5.0 =========== diff --git a/configure.ac b/configure.ac index 5683ff1eca3..3f80dfd829f 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) define(_CLIENT_VERSION_MINOR, 5) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 25) +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))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 4c332d4214d..ae9a8ca561f 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.5.0-rc1" +name: "zcash-4.5.0" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index 1a849b601af..03338b98a94 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -18,7 +18,7 @@ #define CLIENT_VERSION_MAJOR 4 #define CLIENT_VERSION_MINOR 5 #define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 25 +#define CLIENT_VERSION_BUILD 50 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/deprecation.h b/src/deprecation.h index 620e42a47d1..6ea5b0cb7d9 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -10,7 +10,7 @@ // Per https://zips.z.cash/zip-0200 // Shut down nodes running this version of code, 16 weeks' worth of blocks after the estimated // release block height. A warning is shown during the 14 days' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 1392800; +static const int APPROX_RELEASE_HEIGHT = 1396476; static const int RELEASE_TO_DEPRECATION_WEEKS = 16; static const int EXPECTED_BLOCKS_PER_HOUR = 3600 / Consensus::POST_BLOSSOM_POW_TARGET_SPACING; static_assert(EXPECTED_BLOCKS_PER_HOUR == 48, "The value of Consensus::POST_BLOSSOM_POW_TARGET_SPACING was chosen such that this assertion holds."); From 5ceb64682c3f51d533269bb53fb53a633b974192 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Sep 2021 04:20:15 +0100 Subject: [PATCH 031/514] make-release.py: Updated manpages for 4.5.0. --- doc/man/zcash-cli.1 | 6 +++--- doc/man/zcash-tx.1 | 6 +++--- doc/man/zcashd.1 | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 4c52fe0da0e..4bd9021120b 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "September 2021" "zcash-cli v4.5.0-rc1" "User Commands" +.TH ZCASH-CLI "1" "September 2021" "zcash-cli v4.5.0" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.5.0-rc1 +zcash-cli \- manual page for zcash-cli v4.5.0 .SH DESCRIPTION -Zcash RPC client version v4.5.0\-rc1 +Zcash RPC client version v4.5.0 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index a638db35818..fe1099d93a7 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "September 2021" "zcash-tx v4.5.0-rc1" "User Commands" +.TH ZCASH-TX "1" "September 2021" "zcash-tx v4.5.0" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.5.0-rc1 +zcash-tx \- manual page for zcash-tx v4.5.0 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.5.0\-rc1 +Zcash zcash\-tx utility version v4.5.0 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index 7bd7193d973..94b17e5aa3c 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "September 2021" "zcashd v4.5.0-rc1" "User Commands" +.TH ZCASHD "1" "September 2021" "zcashd v4.5.0" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.5.0-rc1 +zcashd \- manual page for zcashd v4.5.0 .SH DESCRIPTION -Zcash Daemon version v4.5.0\-rc1 +Zcash Daemon version v4.5.0 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . From a1d17292b46f7953b0885ae35f565870fdcb869c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Sep 2021 04:20:16 +0100 Subject: [PATCH 032/514] make-release.py: Updated release notes and changelog for 4.5.0. --- contrib/debian/changelog | 6 + doc/authors.md | 43 +-- doc/release-notes/release-notes-4.5.0.md | 326 +++++++++++++++++++++++ 3 files changed, 355 insertions(+), 20 deletions(-) create mode 100644 doc/release-notes/release-notes-4.5.0.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 7e2a6018026..4863c22c6ee 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.5.0) stable; urgency=medium + + * 4.5.0 release. + + -- Electric Coin Company Thu, 23 Sep 2021 04:20:16 +0100 + zcash (4.5.0~rc1) stable; urgency=medium * 4.5.0-rc1 release. diff --git a/doc/authors.md b/doc/authors.md index 80fe6602653..0bbe37da462 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,33 +1,34 @@ Zcash Contributors ================== -Jack Grigg (1100) +Jack Grigg (1112) Simon Liu (460) Sean Bowe (367) -Daira Hopwood (262) +Daira Hopwood (270) Eirik Ogilvie-Wigley (216) -Wladimir J. van der Laan (133) -Kris Nuttycombe (117) +Kris Nuttycombe (163) +Wladimir J. van der Laan (150) Alfredo Garcia (116) Taylor Hornby (114) -Marshall Gaucher (105) +Marshall Gaucher (110) +Pieter Wuille (102) +Jonas Schnelli (89) Jay Graber (89) -Jonas Schnelli (88) -Pieter Wuille (86) -Marco Falke (80) +Marco Falke (81) Cory Fields (75) -Larry Ruane (60) +Larry Ruane (61) Ying Tong Lai (56) Nathan Wilcox (56) -Matt Corallo (48) +Matt Corallo (52) practicalswift (38) Kevin Gallagher (38) fanquake (36) Dimitris Apostolou (34) Carl Dong (26) +Gregory Maxwell (23) Jorge Timón (22) -Luke Dashjr (20) -Gregory Maxwell (19) +Luke Dashjr (21) +John Newbery (21) Jonathan "Duke" Leto (18) syd (16) Patick Strateman (16) @@ -36,24 +37,23 @@ avnish (14) Per Grön (14) Benjamin Winston (13) Pavel Janík (12) +Patrick Strateman (12) Ariel Gabizon (12) +Suhas Daftuar (11) Paige Peterson (11) Kaz Wesley (11) -John Newbery (11) +Alex Morcos (11) Philip Kaufmann (10) Peter Todd (10) João Barbosa (10) nomnombtc (9) Marius Kjærstad (9) kozyilmaz (8) -Suhas Daftuar (8) Jeremy Rubin (8) Jeff Garzik (8) Charlie O'Keefe (8) Ben Wilson (8) -Patrick Strateman (7) Karl-Johan Alm (7) -Alex Morcos (7) ying tong (6) James O'Beirne (6) David Mercer (6) @@ -73,6 +73,7 @@ gladcow (4) Zancas Wilcox (4) WO (4) Sjors Provoost (4) +Russell Yanofsky (4) Nate Wilcox (4) mruddy (3) lpescher (3) @@ -84,6 +85,8 @@ Julian Fleischer (3) Jim Posen (3) Jason Davies (3) Evan Klitzke (3) +Ethan Heilman (3) +Eric Lombrozo (3) Danny Willems (3) Anthony Towns (3) Alfie John (3) @@ -94,6 +97,7 @@ ptschip (2) noname45688@gmail.com (2) kpcyrd (2) kobake (2) +hexabot (2) face (2) aniemerg (2) Yuri Zhykin (2) @@ -104,7 +108,6 @@ Solar Designer (2) Sebastian Falbesoner (2) Scott (2) S. Matthew English (2) -Russell Yanofsky (2) Robert C. Seacord (2) Pejvan (2) Pavol Rusnak (2) @@ -116,7 +119,6 @@ Joe Turgeon (2) Jeffrey Czyz (2) Jack Gavigan (2) ITH4Coinomia (2) -Ethan Heilman (2) Dagur Valberg Johannsson (2) Bryant Eisenbach (2) Brad Miller (2) @@ -125,6 +127,7 @@ Ben Woosley (2) Amgad Abdelhafez (2) Alex Tsankov (2) Akio Nakamura (2) +Aditya Kulkarni (2) ロハン ダル (1) zathras-crypto (1) vim88 (1) @@ -158,8 +161,10 @@ Vasil Dimov (1) Ulrich Kempken (1) Tom Ritter (1) Tom Harding (1) +Technetium (1) Steven Smith (1) Stephen (1) +Shaul Kfir (1) Ross Nicoll (1) Rod Vagg (1) Robert McLaughlin (1) @@ -207,7 +212,6 @@ Gregory Sanders (1) Gaurav Rana (1) Forrest Voight (1) Florian Schmaus (1) -Eric Lombrozo (1) Eran Tromer (1) Elliot Olds (1) Dimitris Tsapakidis (1) @@ -232,7 +236,6 @@ Allan Niemerg (1) Alex van der Peet (1) Alex (1) Ahmad Kazi (1) -Aditya Kulkarni (1) Adam Weiss (1) Adam Langley (1) Adam Brown (1) diff --git a/doc/release-notes/release-notes-4.5.0.md b/doc/release-notes/release-notes-4.5.0.md new file mode 100644 index 00000000000..c95b5561d57 --- /dev/null +++ b/doc/release-notes/release-notes-4.5.0.md @@ -0,0 +1,326 @@ +Changelog +========= + +Aditya Kulkarni (1): + Sort taddr txns by txindex in addition to height + +Alex Morcos (4): + Store the total sig op count of a tx. + Add a score index to the mempool. + Add TxPriority class and comparator + tidy up CInv::GetCommand + +Daira Hopwood (8): + Improve error message when a block would overfill the Orchard commitment tree. + More precise terminology: "lock free" -> "unlocked" + ZIP 339 support. + Update URL for Boost source download (from dl.bintray.com to boostorg.jfrog.io). + Cargo.toml: use librustzcash after the merge of https://github.com/zcash/librustzcash/pull/424 . + Update unified address test data to take account of HRPs in padding (https://github.com/zcash/librustzcash/pull/419). + Avoid need to cast away const in the C caller of zip339_free_phrase. + Update authors of librustzcash to everyone currently and formerly on Core team (in alphabetical order). + +Eric Lombrozo (2): + Removed ppszTypeName from protocol.cpp + getdata enum issue fix + +Ethan Heilman (1): + Fix typo adddrman to addrman as requested in #8070 + +Ethan Heilman (1): + Remove non-determinism which is breaking net_tests #8069 + +Gregory Maxwell (4): + Eliminate TX trickle bypass, sort TX invs for privacy and priority. + Move bloom and feerate filtering to just prior to tx sending. + Do not use mempool for GETDATA for tx accepted after the last mempool req. + Defer inserting into maprelay until just before relaying. + +Jack Grigg (124): + Re-include reading blocks from disk in block connection benchmark + cargo update + Migrate to latest zcash_* crates + metrics 0.16 and metrics-exporter-prometheus 0.5 + Implement ZIP 216 consensus rules + Extract SpendDescriptionV5 and OutputDescriptionV5 classes + rust: Enable C++ streams to be passed into Rust code + ZIP 225 tx format constants + v5 transaction format parser + contrib: Add BOSL to contrib/debian/copyright + Remove early return logic from transaction parsing + rust: Document read_callback_t and write_callback_t + CTransaction: Make new ZIP 225 fields non-const and private + ZIP 244 transaction digests + ZIP 244 signature digests + ZIP 244 hashAuthDataRoot computation + Fix tests that assume CTxOuts can be "null" + test: Generate valid Sapling types + test: Small fixes to sighash test vector generation + test: Regenerate sighash.json after generator fixes + Throw an exception instead of asserting if Rust tx parser fails + CI: Publish correct book directory + CI: Build book with latest mdbook + rust: Documentation improvements to FFI methods + Implement Orchard authorization batch validator + Implement Orchard signature validation consensus rules + rust: Fix patched dependencies + book: Add dev guide page about Rust dependencies + Rename hashLightClientRoot to hashBlockCommitments in block header + ZIP 244 hashBlockCommitments implementation + test: Check for valid hashBlockCommitments construction post-NU5 + Skip hashBlockCommitments check when testing block templates + test: Check hashBlockCommitments before, at, and after NU5 activation + ConnectBlock: Check NU activation when deriving block commitments + Copy authDigest in CTransaction::operator=(const CTransaction &tx) + rust: Move history tree FFI logic into a module + rust: Migrate to zcash_history with versioned trees + rust: Move history tree FFI declarations into a separate header + test: Use valid consensus branch IDs in history tree tests + Use V2 history trees from NU5 onward + test: Check history trees across Canopy and NU5 activations + rpc: Document getblock RPC finalorchardroot field, omit before NU5 + rust: Document some requirements for history tree FFI methods + test: Add test case for popping from an empty history tree + Implement Orchard pool value tracking + rust: Load Orchard circuit parameters at startup + Check Orchard bundle-specific consensus rules, i.e. proofs + test: Update CCoinsViewTest with changes to CCoinsView interface + Include Orchard bundle in transaction dynamic usage + ZIP 203: Enforce coinbase nExpiryHeight consensus rule from NU5 + test: Check for updated empty-tx reject messages in transaction tests + test: Fix OverwinterExpiryHeight test after ZIP 203 contextual changes + miner: Set coinbase expiry height to block height from NU5 activation + Introduce libzcash::RawAddress type + Use `libzcash::RawAddress` in `CWallet::GetFilteredNotes` + Use a visitor for handling -mineraddress config option + Add support for decoding and encoding Unified Addresses + Pass network type through to UA address handling logic + CI: Add workflow that runs general lints + CI: Check scripted diffs + CI: Add Rust lints + Document why a nested call to ExtractMinerAddress is not recursive + Add constants for UA typecodes + Postpone dependency updates we aren't doing in this release + depends: Update Rust to 1.54.0 + depends: Update Clang / libcxx to LLVM 12 + depends: Update utfcpp to 3.2.1 + depends: Fix issue cross-compiling BDB to Windows with Clang 12 + rust: cargo update + rust: metrics 0.17 + CI: Use Rust 1.54 for lints + cargo fmt + test: Wait for transaction propagation in shorter_block_times RPC test + test: Fix race condition in p2p_txexpiringsoon + Revert "Remove reference to -reindex-chainstate" + test: Flush wallet in WriteCryptedSaplingZkeyDirectToDb before reopening + qa: Bump `sync_mempool` timeout for `prioritisetransaction.py` + ProcessGetData(): Rework IsExpiringSoon check + test: Print reject reason if RPC test block rejected instead of accepted + test: Fix pyflakes warnings + CI: Ignore errors from general lints we don't yet have passing + lint: remove duplicate include + lint: Add missing include guards + test: Add NU5 test cases to existing RPC tests + builder: Generate v5 transactions from NU5 activation + Print `nConsensusBranchId` in `CTransaction::ToString` + Separate the consensus and internal consistency checks for branch ID + Parse consensus branch ID when reading v5 transaction format + test: Use correct field of getnetworkinfo to read protocol version + CI: Add Dependabot config to keep Actions up-to-date + Introduce a WTxId struct + Implement CInv message type MSG_WTX + test: Fix bugs in mininode transaction parser + test: Add v5 tx support to mininode + ProcessGetData: Respond to MSG_WTX requests + Add MSG_WTX support to inv messages + Use wtxid for peer state management + test: Implement CInv.__eq__() for mininode to simplify RPC test + Postpone dependency updates that require CMake + depends: Update Rust to 1.54.0 + test: Fix bug in mininode.SpendDescription.deserialize + Add named constants for legacy tx authDigest placeholder value + qa: Boost 1.77.0 + cargo update + Migrate to latest revisions of Zcash Rust crates + test: Set up mininodes at the start of feature_zip239 + net: Reject unknown CInv message types + make-release.py: Versioning changes for 4.5.0-rc1. + make-release.py: Updated manpages for 4.5.0-rc1. + make-release.py: Updated release notes and changelog for 4.5.0-rc1. + Migrate to latest revisions of orchard and the zcash_* crates + contrib: Add script for generating a graph of our Rust dependencies + cargo update + build: Add primitives/orchard.h to list of header files + build: Ensure that cargo uses vendored dependencies for git repos + build: Add missing source file to zcash_gtest_SOURCES + rust: Move Orchard batch logic into BatchValidator methods + wallet: Batch-validate all Orchard signatures in the wallet on load + rust: Skip running the Orchard batch validator on an empty batch + bench: Add Orchard logic to zcbenchmarks + cargo update + Update halo2 and orchard dependencies with BOSL Zcash exception + make-release.py: Versioning changes for 4.5.0. + make-release.py: Updated manpages for 4.5.0. + +John Newbery (10): + [tests] Remove wallet accounts test + [wallet] GetBalance can take an isminefilter filter. + [wallet] Factor out GetWatchOnlyBalance() + [wallet] deduplicate GetAvailableCredit logic + [wallet] factor out GetAvailableWatchOnlyBalance() + [wallet] GetBalance can take a min_depth argument. + [RPC] [wallet] allow getbalance to use min_conf and watch_only without accounts. + [wallet] Remove wallet account RPCs + [wallet] Kill accounts + [net] split PushInventory() + +Jonas Schnelli (1): + fix locking issue with new mempool limiting + +Kris Nuttycombe (46): + Update transaction auth commitments for pre-v5 transactions. + Move OrchardBundle to its own header file. + Implement the Rust side of the incremental merkle tree FFI. + Orchard changes to coins & consensus. + Return std::optional for GetAnchor + Check nullifiers length against bundle actions length. + Add Orchard bundle commitments to merkle tree. + Add Orchard merkle tree anchor tests. + Documentation cleanup. + Update orchard dependency. + Update to released version of incrementalmerkletree + Apply suggestions from code review + Apply suggestions from code review + Fix Orchard incremental Merkle tree empty root. + Fix header guards for incremental_sinsemilla_tree.h + Apply style suggestions. + Consistently panic on null commitment tree pointers. + Fix implmentation of OrchardMerkleTree.DynamicMemoryUsage + Document source of Orchard merkle tree test data. + Apply suggestions from code review + Add consensus check for duplicate Orchard nullifiers within a single transaction. + Add Orchard nullifiers to nullifiers cache. + Apply suggestions from code review + Ensure Sapling versions are valid after NU5 + Make CTransaction::nConsensusBranchId a std::optional + Add NU5 upper bound check on nSpendsSapling, nOutputsSapling, nActionsOrchard + Check consensus branch ID for V5 transactions. + Rename tx.valueBalance -> tx.valueBalanceSapling + Make valueBalanceSapling a private non-const member of CTransaction. + Add Orchard value balance checks. + Account for Orchard balance in GetValueOut and GetShieldedValueIn. + Retract partial Orchard test support. + Add check that v5 transactions have empty Sprout joinsplits. + Prevent undefined behaviour in `CTransaction::GetValueOut()` + ZIP 213: Add checks to support Orchard shielded coinbase outputs. + Add check for consistency between nActionsOrchard and Orchard flags. + Ensure that the Orchard note commitment tree does not exceed its maximum size. + Update Orchard commitment tree hashes to use total MerkleCRH^Orchard. + Apply suggestions from code review + Make Sapling Spend and Ouput count, and Orchard Action count checks be noncontextual. + Use DOS level 100 for noncontextual checks. + Fix error strings to correctly reflect context. + Remove unused account-related wallet methods. + Use manual serialization for Merkle frontiers rather than bincode. + Fix clippy complaints. + Lock the wallet in SetBestChainINTERNAL + +Larry Ruane (1): + ZIP 225: v5 transaction check rules + +Luke Dashjr (1): + Optimisation: Store transaction list order in memory rather than compute it every need + +Marco Falke (1): + [qa] py2: Unfiddle strings into bytes explicitly + +Matt Corallo (4): + Fix calling mempool directly, instead of pool, in ATMP + Track (and define) ::minRelayTxFee in CTxMemPool + Add CFeeRate += operator + Print mempool size in KB when adding txn + +Patrick Strateman (5): + Fix insanity of CWalletDB::WriteTx and CWalletTx::WriteToDisk + Add CWallet::ListAccountCreditDebit + Add CWallet::ReorderTransactions and use in accounting_tests.cpp + Move CWalletDB::ReorderTransactions to CWallet + Move GetAccountBalance from rpcwallet.cpp into CWallet::GetAccountBalance + +Pieter Wuille (16): + Replace trickle nodes with per-node/message Poisson delays + Change mapRelay to store CTransactions + Make ProcessNewBlock dbp const and update comment + Switch reindexing to AcceptBlock in-loop and ActivateBestChain afterwards + Optimize ActivateBestChain for long chains + Add -reindex-chainstate that does not rebuild block index + Report reindexing progress in GUI + Split up and optimize transaction and block inv queues + Handle mempool requests in send loop, subject to trickle + Return mempool queries in dependency order + Add support for unique_ptr and shared_ptr to memusage + Switch CTransaction storage in mempool to std::shared_ptr + Optimize the relay map to use shared_ptr's + Optimization: don't check the mempool at all if no mempool req ever + Optimization: use usec in expiration and reuse nNow + Get rid of CTxMempool::lookup() entirely + +Russell Yanofsky (2): + [wallet] Add GetLegacyBalance method to simplify getbalance RPC + [wallet] Remove unneeded legacy getbalance code + +Shaul Kfir (1): + Add absurdly high fee message to validation state (for RPC propagation) + +Suhas Daftuar (3): + Use txid as key in mapAlreadyAskedFor + Reverse the sort on the mempool's feerate index + Only use AddInventoryKnown for transactions + +Technetium (1): + add missing aarch64 build deps + +Wladimir J. van der Laan (17): + streams: Add data() method to CDataStream + streams: Remove special cases for ancient MSVC + dbwrapper: Use new .data() method of CDataStream + wallet: Use CDataStream.data() + net: Consistent checksum handling + net: Hardcode protocol sizes and use fixed-size types + protocol.h: Move MESSAGE_START_SIZE into CMessageHeader + protocol.h: Make enums in GetDataMsg concrete values + Add assertion and cast before sending reject code + Add debug message to CValidationState for optional extra information + Introduce REJECT_INTERNAL codes for local AcceptToMempool errors + Add function to convert CValidationState to a human-readable message + Remove most logging from transaction validation + Add information to errors in ConnectBlock, CheckBlock + Move mempool rejections to new debug category + net: Fix sent reject messages for blocks and transactions + test: Add basic test for `reject` code + +hexabot (2): + Update depends/packages/native_clang.mk + Update depends/packages/native_rust.mk + +Marshall Gaucher (5): + Remove sprout funding flow logic + Add fix and note for timing issue + Update funding logic bug + Add usage documentation for manual and faucet driven tests + Update funding logic + +Jack Grigg (12): + Document next_pow2 effects and algorithm source + Improvements to CBlock::BuildAuthDataMerkleTree + rust: Explicitly return null hash for pre-v5 auth digests + book: Note that cargo patches work with absolute paths + Improve docs about setting CBlockIndex hash fields + test: Cleanups to ZIP 221 Python test code + Minor fixes to documentation and formatting + Fix typo in method documentation + Track lengths when copying receiver data from C++ to Rust + depends: Greatly simplify the Clang 12 patch + Adjust code comments to remove topological-sort references + Fix typos + From 872e95a599f5472507ba2667f74136281dc57868 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Sep 2021 05:09:42 +0100 Subject: [PATCH 033/514] Set testnet activation height for NU5 --- src/chainparams.cpp | 3 +-- src/version.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 17ce1ebd548..1cb550ff1d3 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -418,8 +418,7 @@ class CTestNetParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_CANOPY].hashActivationBlock = uint256S("01a4d7c6aada30c87762c1bf33fff5df7266b1fd7616bfdb5227fa59bd79e7a2"); consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170014; - consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = - Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = 1590000; consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF; consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; diff --git a/src/version.h b/src/version.h index d66ae7a5672..4c6fdb6d89f 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 170013; +static const int PROTOCOL_VERSION = 170014; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; From 4fb372efdd8c36210b7277f16bfb480455db5f93 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Sep 2021 05:09:55 +0100 Subject: [PATCH 034/514] Add notable changes to v4.5.0 release notes --- doc/release-notes/release-notes-4.5.0.md | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/doc/release-notes/release-notes-4.5.0.md b/doc/release-notes/release-notes-4.5.0.md index c95b5561d57..94453bd741e 100644 --- a/doc/release-notes/release-notes-4.5.0.md +++ b/doc/release-notes/release-notes-4.5.0.md @@ -1,3 +1,64 @@ +Notable changes +=============== + +Network Upgrade 5 +----------------- + +The code preparations for the Network Upgrade 5 consensus rules are finished and +included in this release. The following ZIPs are being deployed: + +- [ZIP 216: Require Canonical Jubjub Point Encodings](https://zips.z.cash/zip-0216) +- [ZIP 224: Orchard Shielded Protocol](https://zips.z.cash/zip-0224) +- [ZIP 225: Version 5 Transaction Format](https://zips.z.cash/zip-0225) +- [ZIP 239: Relay of Version 5 Transactions](https://zips.z.cash/zip-0239) +- [ZIP 244: Transaction Identifier Non-Malleability](https://zips.z.cash/zip-0244) + +NU5 will activate on testnet at height **1,590,000**, and can also be activated +at a specific height in regtest mode by setting the config option +`-nuparams=f919a198:HEIGHT`. + +The testnet activation of NU5, and `zcashd` v4.5.0 itself, is aimed at enabling +existing Zcash users to test their software and make the necessary changes to be +compatible with the new consensus rules. In particular: + +- Wallets should start adding support for v5 transactions. +- Miners and mining pools should ensure that their software is compatible with + the semantic change to the block header specified in + [ZIP 244](https://zips.z.cash/zip-0244#block-header-changes) + +A subsequent v4.5.1 release in the coming weeks will add support for generating +and using Unified Addresses ([ZIP 316](https://zips.z.cash/zip-0316)), which +will enable `zcashd` wallets to interact with the Orchard shielded pool. + +As with previous network upgrades, it is possible that backwards-incompatible +changes might be made to the consensus rules in this testing phase, prior to +setting the mainnet activation height. In the event that this happens, testnet +will be rolled back in v5.0.0 and a second testnet activation will occur. + +See [ZIP 252](https://zips.z.cash/zip-0252) for additional information about the +deployment process for NU5. + +Rejecting unknown `CInv` message types +-------------------------------------- + +Previously, if `zcashd` received an `inv` or `getdata` message containing +unknown `CInv` message types, it would ignore them and process the remainder of +the message. Starting with v4.5.0, `zcashd` will instead drop the entire `inv` +or `getdata` message and reply with a `reject` message. This will enable node +operators to discover whether their nodes are sending unexpected `CInv` types; +in particular, node operators should ensure their software does not produce the +`MSG_WTX` CInv message type intended for the Bitcoin network, which is +incompatible with the `MSG_WTX` CInv message type defined in ZIP 239 (which will +be used from NU5 activation for advertising v5 transactions). + +Deprecated or removed RPCs +-------------------------- + +- The 'account' API inherited from Bitcoin Core has been disabled since the + launch of Zcash. Following its deprecation in Bitcoin Core v0.17 and removal + in Bitcoin Core v0.18, we have now removed the API from `zcashd`. + + Changelog ========= From 21d6835efa61c3109c96298ceba23972fbcd078a Mon Sep 17 00:00:00 2001 From: Charlie O'Keefe Date: Thu, 23 Sep 2021 14:03:29 -0600 Subject: [PATCH 035/514] Update base image used by Dockerfile from debian 10 to debian 11 I built a docker image with this change and verified that it successfully started zcashd --- contrib/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 2c0a127c4dd..c5fd9513ffd 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:10 +FROM debian:11 RUN apt-get update \ && apt-get install -y gnupg2 apt-transport-https curl From fd49f78042d42857d0206272fa1c2a24c11d53be Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 27 Sep 2021 18:42:02 +0100 Subject: [PATCH 036/514] wallet: Check spent shielded notes in CWalletTx::IsFromMe The "IsFromMe" logic was implemented in several places in the Bitcoin Core wallet. We had correctly updated CWallet::IsFromMe(CTransaction) (which was used in most places in the wallet) to check for shielded notes being spent, but did not notice that CWalletTx::IsFromMe also needed this check. This bug has existed since before Zcash launched. It went unnoticed because CWalletTx::IsFromMe was previously only called from code used to either create purely-transparent transactions, or provide informational output on non-critical RPC methods. Closes zcash/zcash#5325. --- doc/release-notes.md | 16 ++++++ qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/wallet_isfromme.py | 93 +++++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 20 +++++++ src/wallet/wallet.h | 5 +- 5 files changed, 131 insertions(+), 4 deletions(-) create mode 100755 qa/rpc-tests/wallet_isfromme.py diff --git a/doc/release-notes.md b/doc/release-notes.md index a29094b5174..2714bee7b75 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,3 +4,19 @@ release-notes at release time) Notable changes =============== +Fixed regression in `getbalance` RPC method +------------------------------------------- + +`zcashd v4.5.0` removed the account API from the wallet, following its +deprecation and removal in upstream Bitcoin Core. As part of the upstream +changes, the `getbalance` RPC method was altered from using two custom balance +computation methods, to instead relying on `CWallet::GetBalance`. This method +internally relies on `CWalletTx::IsFromMe` as part of identifying "trusted" +zero-confirmation transactions to include in the balance calculation. + +There is an equivalent and closely-named `CWallet::IsFromMe` method, which is +used throughout the wallet, and had been updated before Zcash launched to be +aware of spent shielded notes. The change to `getbalance` exposed a bug: +`CWalletTx::IsFromMe` had not been similarly updated, which caused `getbalance` +to ignore wallet-internal (sent between two addresses in the node's wallet) +unshielding transactions with zero confirmations. This release fixes the bug. diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 159c81620fc..9f36dbc1823 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -69,6 +69,7 @@ 'wallet_anchorfork.py', 'wallet_changeindicator.py', 'wallet_import_export.py', + 'wallet_isfromme.py', 'wallet_nullifiers.py', 'wallet_sapling.py', 'wallet_sendmany_any_taddr.py', diff --git a/qa/rpc-tests/wallet_isfromme.py b/qa/rpc-tests/wallet_isfromme.py new file mode 100755 index 00000000000..aefef06771c --- /dev/null +++ b/qa/rpc-tests/wallet_isfromme.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + BLOSSOM_BRANCH_ID, + CANOPY_BRANCH_ID, + HEARTWOOD_BRANCH_ID, + OVERWINTER_BRANCH_ID, + SAPLING_BRANCH_ID, + assert_equal, + get_coinbase_address, + initialize_chain_clean, + nuparams, + start_nodes, + wait_and_assert_operationid_status, +) + +from decimal import Decimal + +class WalletIsFromMe(BitcoinTestFramework): + def setup_chain(self): + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self, split=False): + self.nodes = start_nodes(1, self.options.tmpdir, extra_args=[[ + nuparams(OVERWINTER_BRANCH_ID, 1), + nuparams(SAPLING_BRANCH_ID, 1), + nuparams(BLOSSOM_BRANCH_ID, 1), + nuparams(HEARTWOOD_BRANCH_ID, 1), + nuparams(CANOPY_BRANCH_ID, 1), + ]]) + self.is_network_split=False + + def run_test (self): + node = self.nodes[0] + + node.generate(101) + assert_equal(node.getbalance('', 0), Decimal('6.25')) + + coinbase_addr = get_coinbase_address(node) + + # Send all available funds to a z-address. + zaddr = node.z_getnewaddress() + wait_and_assert_operationid_status( + node, + node.z_sendmany( + coinbase_addr, + [ + {'address': zaddr, 'amount': Decimal('6.25')}, + ], + 0, + 0, + ), + ) + self.sync_all() + assert_equal(node.getbalance('', 0), 0) + + # Mine the transaction; we get another coinbase output. + self.nodes[0].generate(1) + self.sync_all() + assert_equal(node.getbalance('', 0), Decimal('6.25')) + + # Now send the funds back to a new t-address. + taddr = node.getnewaddress() + wait_and_assert_operationid_status( + node, + node.z_sendmany( + zaddr, + [ + {'address': taddr, 'amount': Decimal('6.25')}, + ], + 1, + 0, + ), + ) + self.sync_all() + + # At this point we have created the conditions for the bug in + # https://github.com/zcash/zcash/issues/5325. + + # listunspent should show the coinbase output, and optionally the + # newly-received unshielding output. + assert_equal(len(node.listunspent()), 1) + assert_equal(len(node.listunspent(0)), 2) + + # "getbalance '' 0" should count both outputs. The bug failed here. + assert_equal(node.getbalance('', 0), Decimal('12.5')) + +if __name__ == '__main__': + WalletIsFromMe().main () diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cc33143919d..3d6fb1fdd0b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3016,6 +3016,26 @@ CAmount CWalletTx::GetChange() const return nChangeCached; } +bool CWalletTx::IsFromMe(const isminefilter& filter) const +{ + if (GetDebit(filter) > 0) { + return true; + } + for (const JSDescription& jsdesc : vJoinSplit) { + for (const uint256& nullifier : jsdesc.nullifiers) { + if (pwallet->IsSproutNullifierFromMe(nullifier)) { + return true; + } + } + } + for (const SpendDescription &spend : vShieldedSpend) { + if (pwallet->IsSaplingNullifierFromMe(spend.nullifier)) { + return true; + } + } + return false; +} + bool CWalletTx::IsTrusted() const { // Quick answer in most cases diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7872ec4b843..423daf99f7b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -610,10 +610,7 @@ class CWalletTx : public CMerkleTx void GetAmounts(std::list& listReceived, std::list& listSent, CAmount& nFee, const isminefilter& filter) const; - bool IsFromMe(const isminefilter& filter) const - { - return (GetDebit(filter) > 0); - } + bool IsFromMe(const isminefilter& filter) const; bool IsTrusted() const; From 6e90c84be741123b36956066dc7de821be6dd1c5 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 28 Sep 2021 00:13:31 +0100 Subject: [PATCH 037/514] Fix bugs in testnet Orchard circuit The consensus branch ID is updated (as the NU5 consensus rules are altered). The testnet NU5 activation height is also reset. --- .cargo/config.offline | 9 +--- Cargo.lock | 23 +++++---- Cargo.toml | 13 +++-- doc/release-notes.md | 50 +++++++++++++++++++ .../feature_zip244_blockcommitments.py | 15 +++--- qa/rpc-tests/mempool_nu_activation.py | 13 +++-- qa/rpc-tests/test_framework/mininode.py | 2 +- qa/rpc-tests/test_framework/util.py | 2 +- src/chainparams.cpp | 8 +-- src/consensus/upgrades.cpp | 2 +- src/test/data/zip0244.json | 20 ++++---- src/version.h | 2 +- 12 files changed, 106 insertions(+), 53 deletions(-) diff --git a/.cargo/config.offline b/.cargo/config.offline index 7cc7a813aa3..260b4c3ae83 100644 --- a/.cargo/config.offline +++ b/.cargo/config.offline @@ -11,11 +11,6 @@ git = "https://github.com/str4d/redjubjub.git" rev = "416a6a8ebf8bd42c114c938883016c04f338de72" replace-with = "vendored-sources" -[source."https://github.com/zcash/halo2.git"] -git = "https://github.com/zcash/halo2.git" -rev = "a7cd600eb60b1528159b92af5e426adcc615de1a" -replace-with = "vendored-sources" - [source."https://github.com/zcash/incrementalmerkletree"] git = "https://github.com/zcash/incrementalmerkletree" rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" @@ -23,12 +18,12 @@ replace-with = "vendored-sources" [source."https://github.com/zcash/librustzcash.git"] git = "https://github.com/zcash/librustzcash.git" -rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" +rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" replace-with = "vendored-sources" [source."https://github.com/zcash/orchard.git"] git = "https://github.com/zcash/orchard.git" -rev = "8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0" +rev = "2c8241f25b943aa05203eacf9905db117c69bd29" replace-with = "vendored-sources" [source.vendored-sources] diff --git a/Cargo.lock b/Cargo.lock index cd905aa2a5d..030631c5b68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,7 +534,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "blake2b_simd", "byteorder", @@ -543,7 +543,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "blake2b_simd", ] @@ -665,8 +665,9 @@ dependencies = [ [[package]] name = "halo2" -version = "0.0.1" -source = "git+https://github.com/zcash/halo2.git?rev=a7cd600eb60b1528159b92af5e426adcc615de1a#a7cd600eb60b1528159b92af5e426adcc615de1a" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f186b85ed81082fb1cf59d52b0111f02915e89a4ac61d292b38d075e570f3a9" dependencies = [ "blake2b_simd", "ff", @@ -1123,7 +1124,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.0.0" -source = "git+https://github.com/zcash/orchard.git?rev=8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0#8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0" +source = "git+https://github.com/zcash/orchard.git?rev=2c8241f25b943aa05203eacf9905db117c69bd29#2c8241f25b943aa05203eacf9905db117c69bd29" dependencies = [ "aes", "arrayvec 0.7.1", @@ -1895,7 +1896,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "bech32", "blake2b_simd", @@ -1907,7 +1908,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "byteorder", "nonempty", @@ -1916,7 +1917,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "bigint", "blake2b_simd", @@ -1926,7 +1927,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "blake2b_simd", "byteorder", @@ -1941,7 +1942,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "aes", "bip0039", @@ -1975,7 +1976,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=bfd083b339e0a21e9663d8c269f79fcc57eb742d#bfd083b339e0a21e9663d8c269f79fcc57eb742d" +source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index bf16d477075..70a5122cbeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,11 +69,10 @@ codegen-units = 1 [patch.crates-io] ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } -halo2 = { git = "https://github.com/zcash/halo2.git", rev = "a7cd600eb60b1528159b92af5e426adcc615de1a" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -orchard = { git = "https://github.com/zcash/orchard.git", rev = "8779ce8f1a638ebbc9b229d4eff3a29ef4de7ac0" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "bfd083b339e0a21e9663d8c269f79fcc57eb742d" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } diff --git a/doc/release-notes.md b/doc/release-notes.md index 2714bee7b75..d376b38c019 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,6 +4,56 @@ release-notes at release time) Notable changes =============== +Fixed bugs in the testnet Orchard circuit +----------------------------------------- + +In the `zcashd v4.5.0` release notes we indicated that a testnet rollback might +occur to update the consensus rules, if we needed to make backwards-incompatible +changes. Shortly after `zcashd v4.5.0` was released, during another internal +review of the Orchard circuit, we identified two bugs that would affect the +upcoming testnet activation of NU5: + +- The diversifier base `g_d_old`, for the note being spent, is required to be a + non-identity point. A note created from a payment address with `g_d` set to + the identity (via collaboration between sender and recipient) could be spent + multiple times with different nullifiers (corresponding to different `ivk`s). + The code outside the circuit correctly enforced the non-identity requirement, + but the circuit did not correctly constrain this, and allowed the prover to + witness the identity. + +- SinsemillaCommit can be modeled as a Pedersen commitment to an output of + SinsemillaHash: `SinsemillaCommit(r, M) = SinsemillaHashToPoint(M) + [r] R`. + The specification used incomplete addition here, matching its use inside + SinsemillaHash. However, unlike in SinsemillaHash, an exceptional case can be + produced here when `r = 0`. The derivations of `rivk` (for computing `ivk`) + and `rcm` (for computing the note commitment) normally ensure that `r = 0` + can only occur with negligible probability, but these derivations are not + checked by the circuit for efficiency; thus SinsemillaCommit needs to use + complete addition. + +These bugs do not affect mainnet, as `zcashd v4.5.0` only set the activation +height for NU5 on testnet for testing purposes. Nevertheless, in the interest of +keeping the testnet environment as close to mainnet as possible, we are fixing +these bugs immediately. This means a change to the NU5 consensus rules, and a +new testnet activation height for NU5. + +To this end, the following changes are made in `zcashd v4.5.1`: + +- The consensus branch ID for NU5 is changed to `0x37519621`. +- The protocol version indicating NU5-aware testnet nodes is set to `170015`. +- The testnet activation height for NU5 is set to **1,599,200**. + +Testnet nodes that upgrade to `zcashd v4.5.1` prior to block height 1,590,000 +will follow the new testnet network upgrade. Testnet nodes that are running +`zcashd v4.5.0` at that height will need to upgrade to `v4.5.1` and then run +with `-reindex`. + +As always, it is possible that further backwards-incompatible changes might be +made to the NU5 consensus rules in this testing phase, prior to setting the +mainnet activation height, as we continue to conduct additional internal review. +In the event that this happens, testnet will be rolled back in (or prior to) +v5.0.0, and a new testnet activation will occur. + Fixed regression in `getbalance` RPC method ------------------------------------------- diff --git a/qa/rpc-tests/feature_zip244_blockcommitments.py b/qa/rpc-tests/feature_zip244_blockcommitments.py index 6113f856a4a..a0a5bde45f8 100755 --- a/qa/rpc-tests/feature_zip244_blockcommitments.py +++ b/qa/rpc-tests/feature_zip244_blockcommitments.py @@ -7,9 +7,14 @@ from test_framework.blocktools import derive_block_commitments_hash from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( + BLOSSOM_BRANCH_ID, + CANOPY_BRANCH_ID, + HEARTWOOD_BRANCH_ID, + NU5_BRANCH_ID, assert_equal, bytes_to_hex_str, hex_str_to_bytes, + nuparams, start_nodes, ) @@ -24,12 +29,10 @@ def __init__(self): def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ - '-nuparams=5ba81b19:1', # Overwinter - '-nuparams=76b809bb:1', # Sapling - '-nuparams=2bb40e60:201', # Blossom - '-nuparams=f5b9230b:201', # Heartwood - '-nuparams=e9ff75a6:201', # Canopy - '-nuparams=f919a198:205', # NU5 + nuparams(BLOSSOM_BRANCH_ID, 201), + nuparams(HEARTWOOD_BRANCH_ID, 201), + nuparams(CANOPY_BRANCH_ID, 201), + nuparams(NU5_BRANCH_ID, 205), '-nurejectoldversions=false', ]] * self.num_nodes) diff --git a/qa/rpc-tests/mempool_nu_activation.py b/qa/rpc-tests/mempool_nu_activation.py index 05e057a8b9f..14d5df2c2a9 100755 --- a/qa/rpc-tests/mempool_nu_activation.py +++ b/qa/rpc-tests/mempool_nu_activation.py @@ -6,7 +6,12 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.mininode import NU5_PROTO_VERSION from test_framework.util import ( + BLOSSOM_BRANCH_ID, + CANOPY_BRANCH_ID, + HEARTWOOD_BRANCH_ID, + NU5_BRANCH_ID, assert_equal, assert_true, + nuparams, start_node, connect_nodes, wait_and_assert_operationid_status, get_coinbase_address ) @@ -25,10 +30,10 @@ def __init__(self): def setup_network(self): args = ["-checkmempool", "-debug=mempool", "-blockmaxsize=4000", - "-nuparams=2bb40e60:200", # Blossom - "-nuparams=f5b9230b:210", # Heartwood - "-nuparams=e9ff75a6:220", # Canopy - "-nuparams=f919a198:230", # NU5 + nuparams(BLOSSOM_BRANCH_ID, 200), + nuparams(HEARTWOOD_BRANCH_ID, 210), + nuparams(CANOPY_BRANCH_ID, 220), + nuparams(NU5_BRANCH_ID, 230), ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 10712aa5eba..1a6b1083718 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -52,7 +52,7 @@ OVERWINTER_PROTO_VERSION = 170003 SAPLING_PROTO_VERSION = 170006 BLOSSOM_PROTO_VERSION = 170008 -NU5_PROTO_VERSION = 170014 +NU5_PROTO_VERSION = 170015 MY_SUBVERSION = b"/python-mininode-tester:0.0.3/" diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index eb058b027a5..4c26ce0e669 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -40,7 +40,7 @@ BLOSSOM_BRANCH_ID = 0x2BB40E60 HEARTWOOD_BRANCH_ID = 0xF5B9230B CANOPY_BRANCH_ID = 0xE9FF75A6 -NU5_BRANCH_ID = 0xF919A198 +NU5_BRANCH_ID = 0x37519621 # The maximum number of nodes a single test can spawn MAX_NODES = 8 diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 1cb550ff1d3..d9a7cb182e1 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -133,7 +133,7 @@ class CMainParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = 1046400; consensus.vUpgrades[Consensus::UPGRADE_CANOPY].hashActivationBlock = uint256S("00000000002038016f976744c369dce7419fca30e7171dfac703af5e5f7ad1d4"); - consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170015; + consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170017; consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF; @@ -417,8 +417,8 @@ class CTestNetParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = 1028500; consensus.vUpgrades[Consensus::UPGRADE_CANOPY].hashActivationBlock = uint256S("01a4d7c6aada30c87762c1bf33fff5df7266b1fd7616bfdb5227fa59bd79e7a2"); - consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170014; - consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = 1590000; + consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170015; + consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = 1599200; consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF; consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; @@ -663,7 +663,7 @@ class CRegTestParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nProtocolVersion = 170012; consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; - consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170014; + consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170015; consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF; diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp index c4f7c708751..1af82ab9adc 100644 --- a/src/consensus/upgrades.cpp +++ b/src/consensus/upgrades.cpp @@ -45,7 +45,7 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { .strInfo = "See https://z.cash/upgrade/canopy/ for details.", }, { - .nBranchId = 0xf919a198, + .nBranchId = 0x37519621, .strName = "NU5", .strInfo = "See https://z.cash/upgrade/nu5/ for details.", }, diff --git a/src/test/data/zip0244.json b/src/test/data/zip0244.json index 829ebd1ac75..6ec6dfa97e3 100644 --- a/src/test/data/zip0244.json +++ b/src/test/data/zip0244.json @@ -1,14 +1,14 @@ [ ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0244.py"], ["tx, txid, auth_digest, transparent_input, script_code, amount, sighash_all, sighash_none, sighash_single, sighash_all_anyone, sighash_none_anyone, sighash_single_anyone"], - ["050000800a27a72698a119f97a8f739a2d6f2c0201e152a8049e294c4d6e66b164939daffa2ef6ee6921481cdd86b3cc4318d9614fc820905d0453516aaca3f2498800019f33bf3a109bdd1b232b47b1646d91e1296634ebde5ccad57288b5b2228186e54b6968912a6381ce3dc166d56a1d62f5a8d7551db5fd9313e8c7203d996af7d41a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a5301466b3da612149df5eda0f14f2efc5c6ac03884428a315dc91f8d7b492ebc57e475a4a6f26572504b192232ecb9f0c02411e52596bc5e90457e745939ffedbd121e37ec1e9dddc31b06dc9576a1738ef73e6ba71648913dbf75a779fdd488d83f857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a5341ec5dd715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f88008e315dc7d8388e76c1782fd2795d18a763624c25fa959cc97489ce75745824b77868c53239cfbdf73caec65604037314faaceb56218c6bd30f8374ac13386793f21a9fb80ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e9596fa825c6bf21aff9e68625a192440ea06828123d97884806f15fa08da52754a1095e3ff1abd5ce4fddfccfc3a6128aef784a64610a89d1a7099216d0814d3a2d452431c32d411ac1cce82ad0229407bbc48985675e3f874a4533f1d63a84dfa3e0f460fe2f57e34fbc75423c3737f5b2a0615f5722db041a3ef66fa483afd3c2e19e59444a64add6df1d963f5dd5b5010d3d025f0287c4cf19c75f33d51ddddba5d657b43ee8da645443814cc7329f3e9b4e54c236c29af3923101756d9fa4bd0f7d2ddaacb6b0f86a2658e0a07a05ac5b950051cd24c47a88d13d659ba2a46ca1830816d09cd7646f76f716abec5de07fe9b523410806ea6f288f8736c23357c85f45791e1708029d9824d90704607f387a03e49bf9836574431345a7877efaa8a08e73081ef8d62cb780ab6883a50a0d470190dfba10a857f82842d3825b3d6da0573d316eb160dc0b716c48fbd467f75b780149ae8808f4e68f50c0536acddf6f1aeab016b6bc1a51ed44cfab70000c7b3534201cfb1cd8dbf69b8250c18ef41294ca97993db546c1fe01f7e9c8e367edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a84b27db59a4a153d1070689f2ccf975b2b176e1c69dbe381340ef1f98fdc4b453abda3a2bfac3069ba7f1cc50a81c2520e412fab4e5d397ecf739f280d5b684533d5d29cfe7e7302ec144b4e553acfd670f77e755fc88e0677e31ba459b44e307768958fe3789d41c2b1ff434cb30e15914f01bc6bc2307b488d2556d7b7380ea4ffd712f6b02fe806b94569cd4059f396bf29b99d0a40e5e1711ca944f72d436a102fca4b97693da0b086fe9d2e7162470d02e0f05d4bec9512bfb3f38327296efaa74328b118c27402c70c3a90b49ad4bbc68e37c0aa7d9b3fe17799d73b841e751713a02943905aae0803fd69442eb7681ec2a05600054e92eed555028f21b6a155268a2dd664052528a5f8ed028f59af985ad1315c2e25aeb9d7f134e4bf478642ab96b15d3b3e13ce2387ac84dc0819e81260e11d392a5f06db8b5633de281a0e9c958c24060297f608af1dc51616562b1ffff6e2a28bab1f7772713a0a4b56fe47fb5a7b73aeee5345566ecf3e95e825f92eb469eb5d69164206a0ea1ce73bfb2a942e73703214d270d80534389b1a1e2bba67481eb3667d6d38254ac4b44559b4708cdd12898972a895bf0fb055cf1fb9b73029d6bfb27da2b5294f5cb354a894322848cc3d35b9554a5f62b44a7dcb25406e5ba07882cb6473714e77a051a7dcd29fea0a943785b325cdab95404fc7aed70525cddb41872cfcc214b13232edc78609753dbff930eb0dc156612b9cb434bc4b693392deb87c530435312edcedc6a961133338d786c4a3e103f60110a16b1337129704bf4754ff6ba9fbe65951e610620f71cda8fc877625f2c5bb04cbe1228b1e886f4050afd8fe94e97d2e9e85c6bb748c0042d3249abb1342bb0eebf62058bf3de080d94611a3750915b5dc6c0b3899d41222bace760ee9c8818ded599e34c56d7372af1eb86852f2a732104bdb750739de6c2c6e0f9eb7cb17f1942bfc9f4fd6ebb6b4cdd4da2bca26fac4578e9f543405acc7d86ff59158bd0cba3aef6f4a8472d144d99f8b8d1dedaa9077d4f01d4bb27bbe31d88fbefac3dcd4797563a26b1d61fcd9a464ab21ed550fe6fa09695ba0b2f10eea6468cc6e20a66f826e3d14c5006f0563887f5e1289be1b2004caca8d3f34d6e84bf59c1e04619a7c23a996941d889e4622a9b9b1d59d5e319094318cd405ba27b7e2c084762d31453ec4549a4d97729d033460fcf89d6494f2ffd789e98082ea5ce9534b3acd60fe49e37e4f666931677319ed89f85588741b3128901a93bd78e4be0225a9e2692c77c969ed0176bdf9555948cbd5a332d045de6ba6bf4490adfe7444cd467a09075417fcc0062e49f008c51ad4227439c1b4476ccd8e97862dab7be1e8d399c05ef27c6e22ee273e15786e394c8f1be31682a30147963ac8da8d41d804258426a3f70289b8ad19d8de13be4eebe3bd4c8a6f55d6e0c373d456851879f5fbc282db9e134806bff71e11bc33ab75dd6ca067fb73a043b646a7cf39cab4928386786d2f24141ee120fdc34d6764eafc66880ee0204f53cc1167ed20b43a52dea3ca7cff8ef35cd8e6d7c111a68ef44bcd0c1513ad47ca61c659cc5d325b440f6b9f59aff66879bb6688fdb462af43582b983f92b5698b87db46e4b02dd8e81eca555a44f2f1aef11d88a0bcee76af9ad3f9c46a67062e1a9ca7ea5c014384af07219c7c0ee7fc7bfc7933d174650f46b4cc000190c19b44c57ae891aa86646c10a177a8626be064409931c37d9e8bdc433b7d79e08a12f738a8f0dbddfef2f2657ef3e47d1b0fd11e6a13654db2854fcbff49aa0dadafec320b6ed2d4b279aee9060c1b221e2eb2f13b0691c4d842406d0ec4282c9526174a09878fe8fdde33a29604e5e5e7b2a025d6650b97dbb52befb59b1d30a57433b0a351474444099daa371046613260cf3354cfcdada663ece824ffd7e44393886a86165ddddf2b4c41773554c86995269408b11e6737a4c447586f69173446d8e48bf84cbc000a807899973eb93c5e819aad669413f8387933ad1584aa35e43f4ecd1e2d0407c0b1b89920ffdfdb9bea51ac95b557af71b89f903f5d9848f14fcbeb1837570f544d6359eb23faf38a0822da36ce426c4a2fbeffeb0a8a2e297a9d19ba15024590e3329d9fa9261f9938a4032dd34606c9cf9f3dd33e576f05cd1dd6811c6298757d77d9e810abdb226afcaa4346a6560f8932b3181fd355d5d391976183f8d99388839632d6354f666d09d3e5629ea19737388613d38a34fd0f6e50ee5a0cc9677177f50028c141378187bd2819403fc534f80076e9380cb4964d3b6b45819d3b8e9caf54f051852d671bf8c1ffde2d1510756418cb4810936aa57e6965d6fb656a760b7f19adf96c173488552193b147ee58858033dac7cd0eb204c06490bbdedf5f7571acb2ebe76acef3f2a01ee987486dfe6c3f0a5e234c127258f97a28fb5d164a8176be946b8097d0e317287f33bf9c16f9a545409ce29b1f4273725fc0df02a04ebae178b3414fb0a82d50deb09fcf4e6ee9d180ff4f56ff3bc1d3601fc2dc90d814c3256f4967d3a8d64c83fea339c51f5a8e5801fbb97835581b602465dee04b5922c2761b54245bec0c9eef2db97d22b2b3556cc969fbb13d06509765a52b3fac54b93f421bf08e18d52ddd52cc1c8ca8adfaccab7e5cc2f4573fbbf8239bb0b8aedbf8dad16282da5c9125dba1c059d0df8abf621078f02d6c4bc86d40845ac1d59710c45f07d585eb48b32fc0167ba256e73ca3b9311c62d1094903570519d4442f0200e6ad11f2452dc9ae85aec01fc56f8cbfda75a7727b75ebbd6bbffb43b63a3b1b871e40feb0db002974a3c3b1a788567231bf6399ff89236981149d423802d2341a3bedb9ddcbac1fe7b6435e1479c72e7089d029e7fbbaf3cf37e9b9a6b776791e4c5e6fda57e8d5f14c8c35a2d270846b9dbe005cda16af4408f3ab06a916eeeb9c9594b70424a4c1d171295b6763b22f47f80b53ccbb904bd68fd65fbd3fbdea1035e98c21a7dba5fe1089f7d1c032f24d36835aa8815266e897ff829403cfac3a715954b9b68958a0111a2c9265633ba2831a2e86b941e569d58d99c1383597fad81193c4c13151f40aedb487b5c04ae3b1ddfbafa26e720099f26d5a7535aee57306fd2c4f30673cd9b698fecf32faf88f62e21c90665859dd26833d21d9bc5452bd19515d3fa5c1e68bc209b9dc2a10ae6b630726a67b33603c691fafc281dd94dc9888a68c4f45155aa7897c045aafd9335be2e0ddcf5f586d7f6b4fe12dad9a17f5db7031", "44953d98d6da0500d389a2632a4ac7873c8b69d0fb6dd9d4b165c5f7197d6cbf", "9e760dec2ee3d4ade905273dc073359e605a91d2a9a7dacd52a64c25d7e0bb1c", 0, "650051", 570688904498311, "6401ef6037a6b71f7d4ffd40f74f25f967e81062399780d8104ef609ee73fff8", "86be75d840f644a28596e3f6d61b3289f55e8c822b60eb233dea83754b1be689", "86be75d840f644a28596e3f6d61b3289f55e8c822b60eb233dea83754b1be689", "237bbe3329b0e24dba8a1038f0531535dd38114f713b10c952f4800465d359f7", "237bbe3329b0e24dba8a1038f0531535dd38114f713b10c952f4800465d359f7", "237bbe3329b0e24dba8a1038f0531535dd38114f713b10c952f4800465d359f7"], - ["050000800a27a72698a119f91fc998c31f4dd20800015058e5754c2104000753ac515300515202f89d3226fb53292bfb5dbbf7ff013af3341ea7a35f553f6d7a0221ae195ea84eb97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f5fe1573f57e0685406994d28264e4d74d5a25fa3fa38fed813c505dd90289f301c75e53a716c5b54a45eb32c165448d4d5d61ca2859585369f53f1a137e9e82b67b8fdaf01bda54a317311896ae10280a032440c420a421e944d1e952b70d5826cd3b08b7db901e5849f96bae6f2056f33ab1e6989d7d264adc97855a990103b4d1e6350d5c31a39c3caf69459e462f141be8b39037ffa255ce27e4ad7b566a29620a9f011ab08fb2ad3050652b3f65b8e34526a2a15fc2ddc5b5113e4882c7cca0dd5577be067ba7a175dae4bbe3ef4863d53708915090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41db908fdab8b18cc7304e94e970568f9421c0dbbbaf84598d972b0534f48a5e52670436aaa776ed2482ad703430201e53443c36dcfd34a0cb6637876105e79bf3bd58ec148cb64970e3223a91f71dfcfd5a04b667fbaf3d4b3b908b9828820dfecdd753750b5f9d2216e56c615272f854464c0ca4b1e85aedd038292c4e1a57744ebba010b9ebfbb011bd6f0b78805025d27f3c17746bae116c15d9f471f0f6288a150647b2afe9df7cccf01f5cde5f04680bbfed87f6cf429fb27ad6babe791766611cf5bc20e48bef119259b9b8a0e39c3df28cb9582ea338601cdc481b32fb82adeebb3dade25d1a3df20c37e712506b5d996c49a9f0f30ddcb91fe9004e1e83294a6c9203d94e8dc2cbb449de4155032604e47997016b304fd437d8235045e255a19b743a0a9f2e336b44cae307bb3987bd3e4e777fbb34c0ab8cc3d67466c0a88dd4ccad18a07a8d1068df5b629e5718d0f6df5c957cf71bb00a5178f175caca944e635c5159f738e2402a2d21aa081e10e456afb00b9f62416c8b9c0f7228f510729e0be3f305313d77f7379dc2af24869c6c74ee4471498861d192f0ff0f508285dab6b6a36ccf7d12256cc76b95503720ac672d08268d2cf7773b6ba2a5f664847bf707f2fc10c98f2f006ec22ccb5a8c8b7c40c7c2d49a6639b9f2ce33c25c04bc461e744dfa536b00d94baddf4f4d14044c695a33881477df124f0fcf206a9fb2e65e304cdbf0c4d2390170c130ab849c2f22b5cdd3921640c8cf1976ae1010b0dfd9cb2543e45f99749cc4d61f2e8aabfe98bd905fa39951b33ea769c45ab9531c57209862ad12fd76ba4807e65417b6cd12fa8ec916f013ebb8706a9a556c762f8850000bdcbd49fe4f85b623c7828c71382e1034ea67bc8ae97404b0c50b2a04f559e4999d9c09901bf39caac48dc11956a8ae905ead86954547c448ae43d315e669c4242da565938f417bf43ce7b2b30b1cd4018388e1a910f0fc41fb0877a5925e466819d375b0a912d4fe843b76ef6f223f0f7c894f38f7ab780dfd75f669c8c06cffa43eb47565a50e3b1fa45ad61ce9a1c4727b7aaa53562f523e73952bbf33d8a4104078ade3eaaa49699a69fdf1c5ac7732146ee5e1d6b6ca9b9180f964cc9d0878ae1373524d7d510e58227df6de9d30d271867640177b0f1856e28d5c8afb0630fe4fd5f22125de840fcc40b98038af11d55be25432597b4b65b9ec1c7a8bbfd052cbf7e1c1785314934b262d5853754f1f17771cfb7503072655753fa3f54ecc587e9f83b581916092df26e63e18994cb0db91a0bbdc7b6119b32222adf5e61d8d8ae89dae4954b54813bb33f08d562ba513fee1b09c0fcd516055419474dd7fda038a89c84ea7b9468287f0eb0c10c4b132520194d3d8d5351fc10d09c15c8cc101aa1663bbf17b84111f38bb439f07353bdea3596d15e713e1e2e7d3f1c93cfcb46238b6e0398b46d76aff2d8f05745c465a94fca4365a93741b2e7516a1d2a00de7d1f80a5d3f791d35058a96380dced302c60c0062f67a331cf71e004ef78407a7c51161c7b8d46aba76714bac170d185e27ce3ebff2446164e0db23c02ea332b6afe11084ac25d73e63e51e75f6da74707bec9b1c2224f11dc18ed0a6effeda06c4be24b04846392e9d1e6930eae01fa21fbd700583fb598b92c8f4eb8a61aa6235db60f2841cf3a1c6ab54c67066844711d091eb931a1bd6281aedf2a0e8fab18817202a9be06402ed9cc720c16bfe881e4df4255e87afb7fc62f38116bbe03cd8a3cb11a27d568414782f47b1a44c97c680467694bc9709d32916c97e8006cbb07ba0e4180a3738038c374c4cce8f32959afb25f303f5815c4533124acf9d18940e77522ac5dc4b9570aae8f47b7f57fd8767bea1a24ae7bed65b409e1dd26b8dddd68858d6f5161f073d90636860a9aaee18629b06330a8ee30591debfcef56a026bb28c3b06ec2cfaf5b79ab72694d1d012a7594dd80ae7dfa0c00", "03b08aa510ac31b7bf2979daa7bfb7fce4a5f84db0a07f7485179b82289c18cb", "be78adfc71515f0f2cf94947fd9725619eb887e6e2a1b83214f196165051ed64", null, null, null, "03b08aa510ac31b7bf2979daa7bfb7fce4a5f84db0a07f7485179b82289c18cb", null, null, null, null, null], - ["050000800a27a72698a119f9c2eb518f68984d020000000000", "cd97299dd6dd625b98cd1bc7ff6bb76a57f9cbb183bb32a2561a4e3779d84d7f", "ed22293620cc26b4e0f108f1d8da60d9f6793d9314fcfce4e81d1164b678da04", null, null, null, "cd97299dd6dd625b98cd1bc7ff6bb76a57f9cbb183bb32a2561a4e3779d84d7f", null, null, null, null, null], - ["050000800a27a72698a119f95e3dbaf7ae12670d0001516cf4adec75070003656500000000", "bfb1c2ca149a1a0565dc789f9cc7f9d54fe3a110e6c315f18b28d96c5f35268b", "ed22293620cc26b4e0f108f1d8da60d9f6793d9314fcfce4e81d1164b678da04", null, null, null, "bfb1c2ca149a1a0565dc789f9cc7f9d54fe3a110e6c315f18b28d96c5f35268b", null, null, null, null, null], - ["050000800a27a72698a119f9ff6acc0ffc2e490d03146b9d49dd8c7835f43a37dca0787e3ec9f6605223d5ba7ae0ab9025b73bc03f7fac36c009636363635100635365bca7e54cc1a12d127b57c8138976e791013b015f06a624f521b6ee04ec980893c7e5e01a3362035904ac000053d7445fe2d09130f63511da54832de9136b39f4599f5aa5dfbb45da60cdceab7eefde89be63f3f7c00452006aace1405def0244fd7f99b67d040004630063ac12f6465073e1020009636a5351520065ac65000000", "1e5ba2486609062c16d33a720e58d4039390f5857007a4ce7c9778362645b31e", "b34ac0688293e11f6d7b8e504aa7e7dbadbb9f87d193b612e9b31fb339687c9e", 1, "ac0000", 693972628630138, "b0431734f9296d596523fdb4b80f34ac82fd10f8e99b139208a690edef08a524", "b64c9c23097103e959193962015311a858a7092c8401950b803d2d8f27de4411", "b2c23fec2e9696fe806d4519183685a023be85509f388f4f4c1d7c463161c2c4", "86617533ef5fc517f7cd26e99ef7d83546dcc00c3db0acd9d72b4eb2fb4b3be1", "6a8767d706027ec535bef6f7f85c630aaa15fccf4b32180464ad41dbc0beae21", "20f661902955f3f8adc6aa940151217c08480d87e8e186382dd9f9d5f6cb0fb6"], - ["050000800a27a72698a119f9dedc5e5f0756fb19000133a490768c1600000851535351516563000001dab9578157ebf9cd8113078866e952d6218c69455fbc9c5548725b189cc216ab58483a5a4d28cce4b2fae6513dc6c094307eb498166ba95999ac5f8464b2ff6b295d6e94b0227b5c43a121a34ac907e8d0d07a2d6d79712a776614b0cdec4de09b2627218f0c292fa66ada945fa55bb23548e33a83a562957a3149a993cc472362298736a8b778d97ce423013d64b32cd172efa551bf7f368f04bdaec6091a3004a757598b801dcf675cb83e43a53ae8b254d333bcda20d4817d3477abfba25bb83df5949c126f149b1d99341e4e6f9120f4d41e629185002c72c012c414d2382a6d47c7b3deaba770c400ca96b2814f6b26c3ef17429f1a98c85d83db20efad48be8996fb1bff591efff360fe1199056c56e5feec61a7b8b9f699d6012c2849232f329fef95c7af370098ffe4918e0ca1df47f275867b739e0a514d3209325e217045927b479c1ce2e5d54f25488cad1513e3f44a21266cfd841633327dee6cf810fbf7393e317d9e53d1be1d5ae7839b66b943b9ed18f2c530e975422332c3439cce49a29f2a336a4851263c5e9bd13d731109e844b7f8c392a5c1dcaa2ae5f50ff63fab9765e016702c35a67cd7364d3fab552fb349e35c15c50250453fd18f7b855992632e2c76c0fbf1ef963ea80e3223de3277bc559251725829ec03f213ba8955cab2822ff21a9b0a4904d668fcd77224bde3dd01f6ffc4828f6b64230b35c6a049873494276ea1d7ed5e92cb4f90ba83a9e49601b194042f2900d99d312d7b70508cf176066d154dbe96ef9d4367e4c840e4a17b5e5122e8ebe2158a3c5f4cbae21ea3fa1ae6c25a9462ebcbb0fd5f14554bc97747c33e34da90c816d8d0d50bfe37618c5812891484fa259322c15092d4155d8696d6f12f24fd364496b3be0871ca3dd9625348a614b59bde45885649bae36de34def8fcec85343475d976ae1e9b27829ce2ac5efd0b399a8b448be6504294ee6b3c1c6a5342d7c01ae9d8ad3070c2b1a91573af5e0c5e4cbbf4acdc6b54c9272200d9970250c17c1036f06085c41858ed3a0c48150bc697e4a695fefc6be7b68d0120200335f7ad07e1a46dc767ff822db70e6669080b9816b2232c81a4c66cc586abfe1eaa8ca6cf41fc3c3e6c7b886fb6dac9f4822b4fc6fff9d0513d61a21c80a377671d135a668a0ae2bb934c82c4142da69d12ca7de9a7df706400ec79878d868e17e8f71ea31495af819a016cc419e07c501aa8309b2e6c85b79b2763733a37bbc0420d42537b871b4294a65d3e055ff718dd9dc8c75e7e5b2efe442637371b7c48f6ee99e3ea38a4b0f2f67fc2b908cda657eae754e037e262e9a9f9bd7ec4267b5420240add20a389cb678706917f97cc8162fb1dcc6c28b6eda930f055ce86bd84ec0f3ffacda10485cce037a43b0780fd591452390c924c04f54924bf6ac0d025b39b01398f37e78067cfa374bb53eb00fb2a95d03dabf6bf6c5f77feeaf651068aae477fce410ac2d5de6095861c111d7feb3e6bb4fbb5a5495549597279835c72c633a4bd01b150be28ec3d843f1bcfc0bf35b772a3c7263dc89016ed4a112ed8406f96980288726599c1238978691ba421df6027de5af1e4745d58681061564d951eb7684dedcd335fb1bd2a6978cdb797e1f3b659d3a557e407735753c8f8a2b7d4385f1c95af937df78dfd8757fab434968b0b57c66574468f160b447ac8221e5060676a842a1c6b7172dd3340f764070ab1fe091c5c74c95a5dc043390723a4c127da14cdde1dc2675a62340b3e6afd0522a31de26e7d1ec3a9c8a091ffdc75b7ecfdc7c12995a5e37ce3488bd29f8629d68f696492448dd526697476dc061346ebe3f677217ff9c60efce943af28dfd3f9e59692598a6047c23c4c01400f1ab5730eac0ae8d5843d5051c376240172af218d7a1ecfe65b4f75100638983c14de4974755dade8018c9b8f4543fb095961513e67c61dbc59c607f9b51f8d09bdcad28bcfb9e5d2744ea8848b2623ac07f8ef61a81a35910b8a1baf39a919a7b60bc604d63185f759221d847cc54a22765a4c33475b5791e9af3271fc8d9350667090d8184ec50522d804f23c4fb44ffa481bc92ae408d1b9f2b131904f9705c59e2f4bde7a3b2c085d93fd2abc5e14d163001a12f51938d021afa92239b873dc6c357eaa8af4ee6d00540657fe32914103b5d98f68bd3e2b5359f08ccd88d0c811e4c31fbb49f3a90bbd05dce62f344e7077593159ae35050b04c9e6b86bc432dc8b048c73c0018ca5b69411297732a4e1aa99a928c71e7a24fd277856aa42501e51b012aea9446a2104e93f815a0b3a29b458314f3d8be2b9823d342f46213e942a7e19a46e970b5c506708430317b1bb3b35df68ae33a4926a03e6bfeb5510416fcbb0524c9ca5074156cc5a5d6fe1c995edc60a2f550411aa41e3da3bdcf64bcf04a0510571b936d47e55cec0330ee8dfe73563404f047d7f3a8a3d7743bc554955210f1eb0d08599ea77d5f974d87176d37d98b9c0ad440407209ed6a9f08464d565593e1a63b938536b49244e97d880173b640f2ddb74d068ecb46cf289b7d891307bba37054cf91b31fc82f74d5fc461fc5e978920e95d2804bbd5be7fb86b1b0961f35386c586bf4890ea6d607ae27b4c2b271061857eecb8fd90fd08eb5c43ceb736b6831e8c110f16cfdb3a42711215ca70517fd02dd25c84236e8de61e7ed8a3f26c83f4beb392cc07fc375af1968a52510744e95f837499abf7d7eaef506f1883a751588c7efa506c3e8d006961b9416af621e21c6787c5cf16ef846090f40f6158484007a6f536f656c52985673ece7fac73a0ed41ab0051753a7caa89be3139afd9793b3e02f27f040046595acd47bf13fd0da27f09eda48036d3ee437f2ee8f8606ea97343c33584657f46dba99db5cfe6ca176fab7b0f3bfa0ab61e340c34eb9f17c7ec2be03b180f0bb6f434c2a6542e00e84373f4f4649cda32bf686666143f622aa480460b5afac518607cd9af8bcd6b58c30127316b25d5ea7bf6b0cab8542ff69d9b2f180be12ed75344a395aa10f852f083ad64ef40e9c0309e9bba54b8cb33c95498a69538d3ae5b25e247098306fa8c74a8ee5bca941531d61aac27aab3dc5617d5606c9577a2a8346e8d85b32b8505775108dc85e2ade2eac1e636e1af4054c8b6f57632df269c3723b320872e4c57b218358dc7e9905bb04edf92edf0df635f3bf361e57a13296e1447af5087872d636e27518a9876e15eb01f5e8ded81892511cc2851b00b832712a6d3ba5666517bcd3567621a7cf8445589653262020c33bf78031b8ee0707de072068c170570327e6d9f5c6ddc335402efc548862f5a07094fd428a7bbc15d7b38d05362c9ca985f58a76647d2be4c2cd6b3d17d6870971d7a098baf72c6f6f1214cf1faae488bd7de259d3415c2f0ddec7457004f35708d1eccccc0df65a04943ad5cbc13f295f000fe056c40b2d88f27dc34cfeb803be3483a9ebf9b5a9026057725d63ead2c0c0ff1fe26ac1e7bdfcd6fad875842d194f331750462c06b8d7982d67995ed5d3ae96a05ae0067f4eb1c7c93231bd39773cbe0a9d66b0c9aa8cff6a376e1f372eac6ac4e46cc0942245d4c2dcf02d7640ffcc5a6ac3a87f5c411551bcc2f26cb94961d53f95ddb19ae930c8d70f031b29a5df99ff36695e802cbcb6b58c1ba7ed5eacfa76414a41ad4a44f71f1b580d34c3a952920b254a145fea517f5b42b2f65ecd0f82595478d80a0147e39a9cf3ef02000860f7bf1778a151c9fa667f5b880e556fa05241b10f5ac9a8408e925b626b323a471fe3bede52bba097b2a99a9ba5a86658c3fd9ec55bfa9b328567254ab36d2c7f44d2c7e13eb54beb70ea8fa94b6c6e012d79e3f53689c2b1a18102fc2334b7d094eb078f666ef96efc00b8f27fe280e8dbaca68558a3f12b8767a075e88291c7fae19d48f858acb292fb0eec645ffcbbe0ca5f8c561b257d1232e20cf850610c5e7f9e837e0cb42b2255e563c9d87140ad39caa233f9e9d200ae7f3ceac6e8fa0e4221925059c0887c2d3b60978d81a678b9ed8e4486b4d1063c0960441070896898bd5c0e8f5f729c872a27325c36fece03058bdb035c4013b4216056762ce3a396becc833feb8aeac0a08b8a11d84d0409b734f452aaf016", "4e70b2f502382bcd80520713898fe312141f8771ddd180f05116c0a6567480ad", "58441502764f68470e6a413f4e01fad4a397f7e0d8e3d6b681f289d36d4a3aee", null, null, null, "4e70b2f502382bcd80520713898fe312141f8771ddd180f05116c0a6567480ad", null, null, null, null, null], - ["050000800a27a72698a119f98f50258683daf61202399fd047eee288bb4585851dc93eccc62322924cd13b5dd4eed66ed8d9972d772629ea640665ac5351510006c062468e4bd8f7dd9af698f52ae814634e81d7f3e0c420317caca9ae4811c6af06fe80a8c02ab7046500ac65aa1ea1b700000000", "cd469ede171beee3f04b805513d0352ddfbc6d76d0bea5a223909f5668ef8a0a", "0f0442d32eb42dfefdcafa11d325ec57c73ecb70bf20f1b688ff2c3e5bf7be2c", 0, "6352516353", 107504874564564, "9d62e1cf7c478731ef8ead447cb572fd71aaca6e2960bca14869a3e34333c296", "9a77f3ff189c75e5643cf4a68121d2568e2608a66d4b5a8f91ffbf78ca4b9627", "9a77f3ff189c75e5643cf4a68121d2568e2608a66d4b5a8f91ffbf78ca4b9627", "c315f517db0007f9f7e89ec32863d46b35750d54ad8a39af9c2b2698e0d513cd", "c315f517db0007f9f7e89ec32863d46b35750d54ad8a39af9c2b2698e0d513cd", "c315f517db0007f9f7e89ec32863d46b35750d54ad8a39af9c2b2698e0d513cd"], - ["050000800a27a72698a119f9025f13ec913c29020001788f2602a2cf0600015300000342f736a0fb38ae3e42ec67b07486f90e60752e0dc6ee0365cbd21db5ae70fdba02691508db301d732272d965c14602e2297db65fa9699ea87ff47099ed36371b2db3811615bbf52dc655da35a9fc03f311be830e28550a71ce289b24fab93c0163a5ca959be63f37f2ba0d432366736d8632fce072b6ae5b6f3fd59d3faff6381e86576581e2af578119dcb6ecddaf2115bded85c81ac2a8136fc8372590f28a3676a8b403ae25ffd772f7081e9a32bcc1c5e2edd4e2a6576b783cce3aae11fa432262548856183ee682d5dc31beb38f061cbdeca7021a444e2dd417df26dcd220f2b731772b439e96d614e1facb486c7a7d5171b1de359f6ad3a96f649c969102a1964fb4b4a1a4279c68e6c372e42187d754e804a61653092069fb9b6d25266890808b015df28c801065da6febdc1a56bfd002625acfaa5373fde149c1cfc3649b4869696d44ecb12479c5ebef995f10029f8b530eeb3fdc2e50e8757fc0bb9e263023db82f878d9ac7ffb0bd4391df1d879899a3ef57bfd0d1f7755648edd85bb052a6edf71cd2628c987429f36dc505ccc43f30e7a869c9e255e2af9fcf30c121796d190000960cb6fe2f1bf246118b498f3247f9d484c73cf09393039e45326b8ffffb3e7e6159c46699f100792d4672950348a90552e45943beeacf03f3216f94e274d63d637d9f190e8a266cdeef153530bee5cb8355260505c2c2e5d990fffdc34ec0ff7f1af81b24ced0efa6213da6c7c60c487f5f7b03f8160a057f46d05bf8218b3add9c06893bd02db9b61191dfb133bfabe4858e47a4cc32e416ec08b8ac7915a43733f4406e9d967c560f344d7e904a28045d99f3af8c82e97e1b9c1b205e585fbebb48faf58f1b65dca2497e09a70aad4865f85715a280e186f3fc1740d8184d33e8322169521cdc132212939c84a108964e2de74b6ea55b4cb8f6f9bee98b10d415109455f48b776082dc30b4bc73477075511700308158ce2f2f9bf0f691b2ce53e61142cb740c15b7b623cf48b3f7bfefa31bcdc665c6d7123e95350811375947b055a43db07e03f33627df5c638bfad956ddc1ea7d7620a20f2792f63817a1cf32580d04274234af2a51b56bb68a29e43a954142ba4ca6823bde9053d72fdadbc61ad5936c53fdd7579441c5b969e08e9f2e1eb20c909b3de6553f5748484d9ddcc4227eec92d780aa51de20e95968a36c4257bb25f3ff75dbc38fff2f2f271eab89c628e18b5fcb43802ce65256d33ee8f5fe037951be9a760734460a2cf026bc8ddaeacef005abcfe0824849a30576d5846d604542a132da9570762c0b1c65855deba8422ca4b88ab2e03998fc6e1223e7c42672656b9d0b387642dff188366b65a3ae49c206b9a0636407fd7da93fd0de6400d3ab8977485cddfbed5932f507b79947adb2fad37615aa717db5f298099f20f263b359a1151a6b75c01365eb154ae42140d6e10342f14f34dc33e07ff0e4d1a6be375b32f84b92e5d81ebb639c4f27e715aa42cc75707d4ebd1bbfbe8f90fc7c953e7a9715e65af8267373d3451674ff084efd92ccf3bcc7aca1467b6327e4f9522b2cc579a7a8fff7ca7cf145dfc13eafc34153b2c3e8afbe53444d0c73b3bd5bc870b01cd457911e356313fd1dafb4c8151634a01aff7cf116d433c3d2b3adda9cebe18f7d172443e5e7b5ac9abe8db2256d7ebe2ff28020939503870597b9a955892c7389650a2d42ec92be723fedf2f2ede5a472aa1e74f33ad41901544edbbe3ac464cf439196015f4f22ac2b8fc01496beab4d45907f479812a259431a2cbc93d4f3b84e4dd366020273a6752e501af6ff1b78ddc817e6ea351d6006becf8d2ffb03990f67774a81e05b7f4bbad8577fa27c9de64e1b11dcf384f5956443748755a9fc6f2a00b10c3657ebac03bfc0b587bef2f45ec8acdaa51c143b0cb25b9142c61bd790a80d7c23f90cc03495b51e4d2843e557f9e2545108c6c6fae359f645c276891c0dcab3faf187700c082dc477740fb3f2cd7bb59fb358554e94c7e678ce01aebf94e515e49722967995aea858d64e7789ff306369577228180326a5b0af475e27a54b207b41f92e376170e3fb005028261c99c2dbd0eedee871c1c0f48b8e9b8e4be77d1b737fe21f0fa5a18ebb52755b5a6cf6130fb56944cfab87527c250d113b29bcac9aaa10c2e7de415edb0806c6da03020a134ca7ecdc8da1bd57a37f55a46940b45b241b1c16ee100927d1bd860d445a9de50d4c384d6e1d00108026c0ea5ebbf0b72fbf5c370bce18d3acbc46599099baae1d802f77333494a7ae130fe86e835cfb9f5db2a721ac646eadf76e426d8e888321827abcbffbfbf52d75e07131cca1e8c78c51ed377cd4afa894bd9bd12e707156da0726f7cf5729fabe3721604347642ba505ec940bab48370845a5bbf483ea8f856e5e28600904b97226eac27557deff7c656406f9f959996093b2cd710d3e1b3299dc9521f8b513badb01029a31b366a370fa1c2eb426c7a9f327e56b3b9b5b32a226b2de14b7f5259bbf525aaba565b84b845e163d1caef2533c3981637204f96a59c8e8024d9041b2029e94c15245f1a958840ba3f380a4d20f1184e77827de3ff8f3d73459afe241f723c084823230e003d3d21e53501ec0499b083a7dad685c57127f4de64733a880c2db28fdaabf1b542d205f664a35135712711dcccd931a50b9c5661882360d4cac0047681bc2e2b3bf6c99760d7cfb4fa21394377a4551c76d1f75ac03c262054dffd79a9ded05e888958199eea4501e2990a53a5cd2a46a401576588fd7d058a26f28438e5782f45ac1d07f6f6f5ed73741d5785837a6b844b474775718c29dd99084e9f88ef153a8329f532a69017dc3a97ed754367723098e5765840b022897244745fbbbb30a7cb54fa0511166e9544122000610bd2aacbd82325a59b95154ecd82c88d23abd1e20770ffb8aabf83fc0734964ccd411d1c935714e24aab566f4f08424014c4eca91b590f082b473f361c87415d37bd20d70fd0b52b6ddf1865f766702e32b05b3cf1630ee8597aae19633f3516a8555ac5be32c675be1817efbffd9369041a089c283f19649968c2498cde56f500434f280d77a9c62e43cbd3f136a4c6a00a43e6ed530cb2e8ae838860adc88aacc7bd6a00ae0c19ff4533a485efde082b5f4d1f7a8ebe7ed82b7b05a8cfe1e373459f1bdcbf9525747e8c9508a555facb798740e0bdf994d9739bbe5538a0ae0f076c582c0f5ba878b99b8249db1d7e95056c98af083d98cb0ed9e3f7436e1c7643766f966b83e999206ebd1393b9b2a7f414480fa017480069f85c7749c435ae2fba2ddc1038d547d84854817ef39635c29827aad86726c9ade3b265b9086c8b5b75ef56fe4bd8b4d62893895b3fd2734fdac464156d7e5ebc7ecf1d83b86f659637e3b142c164963b8cdcf4ba4f4035dffc5a789458847781918ac72fc18b01954ea6bf46500200e2f790fbbac4c45bc32d240ac16391202822330b32d58e6777765f22a4116304fd0e01b65b2ec516393ab3751b5356d2b0c9500c0f3e469181035bc3660f0b8f9fbe6e40b5e89cb79b063714ca75e72e2e100a10d63bf784df0820ef25f8ef40fe5f05fb95683f9105ff3cb2d219ab76605a064f69219f1dc0d00b3b48642f970dc00cca4b8b43308be18286ec5a4288d600a3785cb622d468a4c6969b3792f2485027d0ad9aa4a9c2cc972f9ee5190a95b1eb058dddd8c08e7d753f5e011b2bcfee1d52c1c4f2cacda30bdb6930653c0cc4486e60e89fa849b32083ba9db453fb8df683cd68754c87daa731f570a7a4060af0ce700d31bca7e74b3e3ba3d0e8a6392a062b8e86d9d7d00b21701e7b062e06b1bcd82a01d375626fbf872d27fa4511f5f8cf8c9abcef2a990176ae339325302e42236a1eb1057288ce53221fb608a547f2cfa51216338d4ea1330d6bad847fa88c1e1aa3e1565d647779bbd0f70e85f8c7d3aa5c2082b265249df057011a79535e36c44fe9eae846fa19c2253746bc558925944f898bb2cf4449604bccb23c7404fe995e0035a3d00bb2a246e95bfc60145c6a009687684460271ee1332441ce68db5c8e802e0df35b93bbd7f3873ea00d191612a18042b25520c6e5dc0ea452f3731c8cb65082a622a7c2e0013ea47d0bdd42d6990466649a905c684c3200e477ea165300aaa4cf6adf8e7d13b0fba29f4499016ef53c1043b43891e734b6a4351aece9b0e9297f06403d1978ead2da0dff82cd1f55ebca57b6337c8513", "e64f3d8d3273cbbf899cad5d22345006d92a514351a2a2bbec5b23712d70bb80", "cf5068180f31942410b3ec029c0cc08c5a86040db27d57152c6c547127cf48bb", null, null, null, "e64f3d8d3273cbbf899cad5d22345006d92a514351a2a2bbec5b23712d70bb80", null, null, null, null, null], - ["050000800a27a72698a119f979813d20210c6f10000331efbaa1ccfd050008515300ac5265ac65657c6e390ecf040009516aacac525165656394266fd6496e060001ac000001bbdf186d8c75d0fb191a1b119b2a4ac4a441813f92551d941f6336c87a6ee01ed7992cff3eca24de3e0984e10e68ae387534b96cde3792f135bf5f68787d370ca44e77b3ed5ccd60369c0f4760aa05c0a6f62ff890558d7221f920bf1f1b778a06e57a85302de1d6919719f38dd180e972a15d5cd0fcc57439a4350eaf098d3b85ba3dbeccfcc295fd490051db41e2c4d181443708b19a4d0d0e2efa1d79e00f66e4c0155a9c74a7a57ccf34c483ac7da1588a1b6b9941f11040f94cf78fad89bf11fed69aa0d83105adacdd4e5f04a62424023c9b9e33c4fb7f12bdf21f07f265c537d51c6551f4617b915d21991839c3d0d36393d646e0a8a41509217d0e7d2ca1a0a0d677a3eaca23edeb07b74e652a0bc50c6c083a55d6c7306e74086f4768933aa24873681867a7893d77cb7f29b8c847c583f2d071a686616e206719f761ae39c110442e06163d2b84590360695d4e19849e634f24d9ad396c19ff83ce74f46e645f932e141a41195936c85d514414f112e60b1a2537c38d6dc6c4638305c9bd6c62e366bc63123e3e6dd36eedd3136fce8deeca2aa09a3298a39d83859efc9b2b69cf9a7dee08a98e4be558ac7912fdcb42209075420260f7cad0f2c01f2afe33073f26249d944f7a50dd84839bc3ea7fdee4ed71449cf07533d26e1e27a3efb032c3a3b34bd3092622d2062ae536ef5149c49b5bc9475eafab6e675761008b0daddeecaa604470bbe0fada255d290e92b190c2c2d8c2dee5455d1fa9a9f3db7779b584643464aa8014ba66994de25517f83980e66ee4f62314ae6dbef452d5d38b0a16f3991f36d8a8b39ddc0d5595eed98762878cdf3f4a2edc5cda77d5fe4faf63a15f568a540da57dd9beb6fb1a977ccb91b4d79cb39b28911a29e7bf028ac6103796dfb6b20967239ad373c38c53f6df1823d4950a0283e99b9c06ab2966667c9df677716b0caded818df9e449c072e22f9d98bb0f9b03bd5fd013fcef3ed6a49aeb98720254087ef728e31947ffe8f766e63ee46ff20816d5fa8ff55a26398961490ab9ae366fc5a2d1996ed693ccca82356f600ab099f6eca8bfe645270d3f95edba5b0de7a32819233bcc754a5ce2e5ea07842e5ff2cebe62ad76e8eff8d15ea4c24a5f207868319a5af6b035006989624f8ff70300921a63b5da5f2553b83a947b16424bbf5f7cbc70b4cd7e8e3c951f358572e33787e7d52704a6721b30efc41017ae4d231558c5c82cc7dd7e3356c09dc24906f0438dfcc300856ac2ced8f77fa8015736c661e80248aeeb774874aa79d290b8f5027a0a509537fc7c689b7ad86116cfec2647ccaae1c74b416f3e6ae8f7cc60eaaf7b6a590d51544138e1732945603a53462c60e1f6cb0c9ca0390c488224c313269fcd59fcb611fb2049dd4be68c3cdae9aedc88ad2cc8e37fd895017f6727541a8e35dfaf48781ea3544489b9470b0dc62ee163c05f9412fc9713aaac25b4c26eb03f716646611ad7c2ed9be4c85e42f734b5786a82ce4177a3c4764760355adfbfc3f0c84665bd6a1ebf895f67e11053883449f70695d0c4e951d71305ef33d9737126d0e66210", "ae4fa86bf7aa105d1ba3747876eb2deb54014916e82ef149521daee4eeb72a2f", "c033c713b56957cc19db7af3e6233528ffdb9855a297ab2920ecc9b8642958f1", null, null, null, "ae4fa86bf7aa105d1ba3747876eb2deb54014916e82ef149521daee4eeb72a2f", null, null, null, null, null], - ["050000800a27a72698a119f91250926f6a8e6319038f69ad9a9192b302f26bdda465d90b94b12c57fa3fd6930083f184438d8a889d3f5ecea20252636736f2a0f18e26f4fa45d1be8f3dc4a707137e95d2ad594f6c03d24923067ae47fd6425efb9c1d50086a636a5300ac6551fe806f5756acb562f13c0ca1d803a195c2ebb2ef02ac33e6a88dea075ba996d3c336648e8694d3a10163ca531beb031aa2377fca6c00000700ac6a6aacac53d4e959af4a2e020002acacb75747fc903c05000300ac000275614934b7eb2bef279e31b965e6d33874b4e11b257f8960316c9a17c6a573a58f57015ca402c67d925c99acea3ee8cc4b008c5cb43966e714ef480fd05e07c7b2dda9aa3966113eaa293d3f622b309d64803ce1e6378b6aac4fab527c43cd45dbad57e9d25969bf43d0143b76c85ca095a90dac4ba07817f02631d72ee5dac1e16d0b77f02028da464100fde76d83dd0bb224f7b57a00c02f68ae648fdc529957a10490dce1fddbb0904f0d518bb387544019983b616975a78e74d854fddc4900f753db06f51001007c57ec890aff51a4d1d39ecd0e757f2917c738dd999b5c6e77da0b6b40a87006ed0a3c1a4b9fb18dcccfcdb6ac0c2421639cda0075a20dc5111b8d3d3199495bd9133dbab94541410e4fba92c7b606a5cb122f140cf1a3596f2788f3c8b92660f14cb65af5dd23dfdbac1371ecf4b33712fed2292c44f70834cf96c05d58827e69bfc2e696fa0874869c02f3dca11c3b90cb214e68bc1cae039d7a146cdc1d609d7a6b3fd5d461b0951c82cfb3e763fad2d1bc7678cdf82779f8fd5a1ce22a8d3c4547abd959838a46fb80afe01f8ecc9931513b1962ec540856cb189387cfbfb255167b55ef4bee465668b20ea4118ca569ae480e0f6e5e043a357b36d3ab36c861f2278301dce57674d5073b3a6f5103a0793af1b7d46f957e22d8d2583bf181836c3be9930bac8fa460e968aa7109870bbed17df5f888c8ca1467ae17dbbcde31c1105cb5bda88ac6c627002ce21c02140ffe81ec58bf1e6d1bb7aaada41fba0bb588778a7f65202ad811ea73d26c74550395aff75325107c9b3f9ae9dcdcd86ed081a2e7424719a3d185b7e0a43a472e298ac0afdc5287d7ad124cd9405a655059e183e964e7fc98bb48e367bbd68d8c9ece7b6bd213624856083fbf920ca53ffc8335f072af593bc991e805c3892b939632449554e40ae3c7a996c0ee02fc626603def306fd1ff4118b1c8e0669efe1b10fc16087199815430bf815a718bab91d8cf2950dbf25b42ec49a1f335ddfd5cad4bbafc1dcba2cd1c3d22b5605b9169e4ab21ff81dc36b21ec349b66bd88ffe7ff46ee18ece49e64198758938bc05215dbd8354613a0029489fe7b1dfebda9da980147397e97aa45d09451120803135701b8595c6e0f1764c3336e48e7f774675436502edda9cc00c7f24cb6090a80d0a61949608ba673710e93140630bf8672c4143d8b7acfd74e72c04d89240d22d67c92d7913f9906f0219e84ffd3932f8b4144461d0700cb7ad6cf9417533c26d2050d25b74b0e7b5a54df51157dc9e62d5f6c4abe5ce90a7fe2e52a8d780678cfcbdc0d5d9e43665af0fdbf5c4b7727680f4c534b54f9d5e9a357c836e085e10c1e3fac4058b682c68e54facae0f9c2dd4d64d9046152b4762332939f17e6aaf7d8b9d358e2218d4e0d69a4f119e1c64eec4c8b532809707131f01f55c7ad04cfb63f7c4a3d0a2b0ffb0b05a6be055b8c94ca80bb0a1d13cd4cd69ab98304ae2515d5f7699d4abee5c20be609d873511012f234bd85a7eff5fb634cff2658ba6516048563095ecefb3015ee3f03ca52a177f261ecdc26bc089d34c6404846e9c647fcfe98cc6acdbb464f64278ad8ce9d1ae0d415bc0c05245fddaf4ebc8dc703a85cb270f796ad2d937e2ac0d5e0a34821758000aa59c9d4652485294ee0ab29696b21430fa54dcfbf2b9c49d142064209eeeed4d471ffc017d4e20a796b0927804c061b9f4a7091fe015ada68fd8442e01825c88dfe55cf5de38936f7ce25311b902ba97a3c12a95cfa1c3a591b818f60832709d9e4839e410fb36b84f3ac4f070fc35e161978259e5b8edc744d90919aa770bb36215128e582b59641e23852e958eb8fc3c0aa96152ba4f77f138d6a6712a3ae3226015883f81db23e583c869c4c71143a6fffd65e8dfdc50c99a2f1f314cdcc71359e235f1d7dc2b5f38ef7b970843163c03f9dd40a8015efdc8791956a3f3cedd9ea64f8efa7a0815a70381d71467817bd04ca529aede07ff60d176aed0f855a2eaea89eaeaca89358c081826a0812a5bca28be1373f086dbdba7e43e203212c9fed21474ba19a055ffcc179412e893a744832298c5fe24cc6b18667f49b34dfb12379267419a9cb9403d8167d8d1e91d2811a043b29243b069b37587847dc6fcddb1831bd1cc2567ca033ac40f74ab6955f683b12e4e8254e4ea760d38b3f46791c5c4cb12bc7ccb0ed1865f25d601c303f81fb1fa1db48533d3d6b288e4d9a4dff8ec21c96f578399710c825fe7e32f93a8c0743f9ebd54cc151c761cb6638eba3a1fea6b9a972aa261b137d015861e14c59ec3a6f4c6d8e19e6463fa1e4304f49e43ae065e3fb196f76d9b879c7200862ead18dea5fb6a17acea3338870bde66449891b3eb051da407e7105cda20d73591139e9b2a2c729060c4a9fce5c0b6eacb9362ddcd774a0f2e147c30707a2cb6680a249ea9c7224392cbc0a9d58acdc4ba5623c498c72d7bac4f3013d0938bcda4f45166657e1f0d79d508b177852afd0abb90ade1d682726f42008b46ad7f8abdb18117f72641390f086b6e1498be69548527e6ada2b38b9fe121ef670af7437d32536d5cf5c4ab19dd99771582d038104b7e039a376f7acbbeadb34f945beb9d7ca0e4e3d5c5e4eb1d8526ebd13dacb1ba35735c6d04a4555acf4bf117626500d77b38189dd4888041225acbe3874a4c0f607fe6745f9355b3fa188f1d65c09f389af1b9d6232aa79447919c550f6f31fec35481cb922de2db5b4da2f81948617028e321706a3a778c1938c443bb00e5b0ff06ad8ab9b1ab0c11477673f85df9561dbea45d5f9781ebe317a0710ae5461e34fe6f1b1aa9b4e67b14910984802c2a7e38193bc7bdc8ba3e4e3d1d933bfb580f5b3e87a2a06517051410fe1b4ff1ea0ade824f338515456a57c7a916a74388ee8f1281f9ade0ae2a2613a0612c469df792b8df4cae4fc25c1cadba95a807ce61e5a5303faaf9e14653996b5a8adc34fd475ef1499094babaf1f3f07da9a390b1d9fc9a08327987adfe9564863fbdfa8f6b46a8841583099afb7870118face76347e40b6fd8cd15582ae8e23be9a0219bc3e4e4546a30d3bbbbd1686086876be0e4c859be71fb58f4fab3d28c0b4f7e75ad1edb7f88946fb40cfa5786a0fcba1303c8347ecee93d46d140bb5f69531d666548b109ce764bead7c87bd4c876494de82db6e5073a6c94f7c099a40d7a31c4a04b69c9fccf3c7dd56f5544776c53b4df7953981d55a96a6dcff9904a90842e5bafec8840c2d255bf5ad61c460f98feb82a10fa1c099f62776798236c5ca7f1e46ebdb2b144d8713e56c772f2c3b860ea5b03a8854bc6e6590d63cc0ea54f10b73ba241bf74b635551a2aaca9687ac5269fd368b26d70a737f267685998a3f7d2637914909c746495d24c498635ef97ac66a400894c09f73488eb7cf779237db2ac14e22658be28ae988cec4a671653b575fdea44f7de69cb7e396821e7c661739f9f028a9262bd80ebf9ce8c4a9382c6b03e7d8085e906cf84ca20173fc57be19361a83a7e3775f5b013a9a04b1a506fc59802cfc3cacfb635beb0b76395cba743c36279ba3b4f2c8ba4adb5b8763fb96d7ca333a12de3cefa91c2c96599919a4dfe8408acb99105ee54b79f227b6cb7e337bab04989857fe4f4437ddfabb7b65543b5f39cb2023d46789eb7d989af779e5b8d28385a85b0da2abe07f0c2bb4255fcea03188527a307d409159e90166fac6a070ba05b3e4dbfd3a2bfcc9ee6ed016c0f665be8133b7dc1d86044db0f9db40fb0e9f8bc2e4db5382a8b4f815b4e8434ad0dfbc51a5e9b145e1596cbf4670b7e05dfdafbb0cf3ddee28d76a82428e8aba4364e84bac379298df2932e69bb5d045516efc33ae6cc3947ceb09ed371667212a831b5485eafce8488188ea4e27d0cdf7ddd348abff777f4a13bbc716b6a5944ee727965690e209b49eb962c039975f939ed5c6e4c400d887759433d3ad716da0cb446113c7727a64b58c3f8a0f81189f98005233a81366aee73cec85228ebcfd5ee3c3fb44db76ba243f2842b7b5fc746ae51b0bc4bd4fc9fd833565ea852b92b224f6990318ad8c7d9437e20e2a1f20e818f9057c5abaaa2e5c15b94945cd424c28a5fa385dadfe4907b274d842707db3697a5ae6c8f542e5ecc07fe47350d1014670212efe81fb7c73e8450df814ef6232f7490f63ccf07480f884a66eaffc28fea448d7b401cdae10e7c0c7f9a7b15331969fc8cb36396773de191931c750f6ce5caaf29768ebb27dacc738056a8125b4772bf87ae10a8a309b9bd655043cfc3159494368c5ab8cadb7f671e9626bd263e31181a604b506a03b439a7ffe4355892477e2bdf338c62c3922f7d3c9a56c7103d911948a84b5ae2dbb16a3761add053a0f967e6b5bc94211b6547153267c6ee1cad0d974a71088583735e4f63d33156dadd54c2faf89114a127b97b94cc2a22ef303f459d04fc0b53ace5918d47ff33a558bd71a75f355fbd06bbccf4e02c3c0a4b63d0cc949801d63a64cb2d32373b2c7b274ab2db4682142c8b21d84c481f5ef21e4b5e3603451bf94774d0ef47f63fa6abb78d21c193cbe004d4355231dfd03003da4ea2a4c88bf677cf9754f0c47ef82e20975baaecb0232df880bd7d1de1321549462ec8d5df3e780ffa72eba8a8df7fcf398ec230513ca9d6123f8b9d8178560daf975111955a2bca3423eeefc527be3a8543eb90a5ec02f35a7c64b7dd59a72da0074634e01d2abf3637add77c7350f12b011b2b244613c3f638dfbc036c45541590a6f070534b13f4d4ec0995a082337438d04ea53f492070e201cdbb1dc5bd194d4919f911dfa7241c8d5792d43c457d5de1667c62ec09f08cb58f735e5e35efed184a5001018c10f8cb64b0a7b76814be117fae204a52008dbefcec9fc34c0e5be77fc8b5b5cd077115afde18405054e5d291aa8eb1619c049a60576b87867936b8c9b296888a860348e53c15f7341d8f1acdd0dde230755b4ce860f9f650fe40af6413640b81e4f631c981c11a2e1d18406f1ec93ae097d4c4c159ab6c4e6f527e80a8a7faff91d410de44438db1c7e9d2f17cad242fa9c3179c1a3aa81f7361649572c715c25a1f6cd5ace82c00ab2342b", "aa023b581070dc686fb9da6c155d271507de77c24772a5d95d01d55e05654616", "a64218286f78f8b74f72e7689dca7198e231e86e5f407cb7b18657989dc94687", 0, "", 1405243945822387, "c0147b110da12b4ce208cd4eff72de8797a59d5041f68a8749c549488fc707c7", "4cff8a073f9a517481f25b25f13a5a418025c98693eb83b2f1b4a0e262ccc099", "8609254f0ada09c9876ff63a1e3002da4bdbf20d66010c81cc7ce59222187b06", "9d3d3b2e7713f7930ec2872baba26a1e1b8d2d074b397419341a5a8237b8cbe3", "c35673537db4eb446513b4b62d56f19c21b43a1af830f2e8f57802cde81a36ad", "56ff0b5e624c46ebcdcc43c0c726234f126962dcddbbe03bcafa57e52a5f2100"] + ["050000800a27a726219651377a8f739a2d6f2c0201e152a8049e294c4d6e66b164939daffa2ef6ee6921481cdd86b3cc4318d9614fc820905d0453516aaca3f2498800019f33bf3a109bdd1b232b47b1646d91e1296634ebde5ccad57288b5b2228186e54b6968912a6381ce3dc166d56a1d62f5a8d7551db5fd9313e8c7203d996af7d41a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a5301466b3da612149df5eda0f14f2efc5c6ac03884428a315dc91f8d7b492ebc57e475a4a6f26572504b192232ecb9f0c02411e52596bc5e90457e745939ffedbd121e37ec1e9dddc31b06dc9576a1738ef73e6ba71648913dbf75a779fdd488d83f857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a5341ec5dd715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f88008e315dc7d8388e76c1782fd2795d18a763624c25fa959cc97489ce75745824b77868c53239cfbdf73caec65604037314faaceb56218c6bd30f8374ac13386793f21a9fb80ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e9596fa825c6bf21aff9e68625a192440ea06828123d97884806f15fa08da52754a1095e3ff1abd5ce4fddfccfc3a6128aef784a64610a89d1a7099216d0814d3a2d452431c32d411ac1cce82ad0229407bbc48985675e3f874a4533f1d63a84dfa3e0f460fe2f57e34fbc75423c3737f5b2a0615f5722db041a3ef66fa483afd3c2e19e59444a64add6df1d963f5dd5b5010d3d025f0287c4cf19c75f33d51ddddba5d657b43ee8da645443814cc7329f3e9b4e54c236c29af3923101756d9fa4bd0f7d2ddaacb6b0f86a2658e0a07a05ac5b950051cd24c47a88d13d659ba2a46ca1830816d09cd7646f76f716abec5de07fe9b523410806ea6f288f8736c23357c85f45791e1708029d9824d90704607f387a03e49bf9836574431345a7877efaa8a08e73081ef8d62cb780ab6883a50a0d470190dfba10a857f82842d3825b3d6da0573d316eb160dc0b716c48fbd467f75b780149ae8808f4e68f50c0536acddf6f1aeab016b6bc1a51ed44cfab70000c7b3534201cfb1cd8dbf69b8250c18ef41294ca97993db546c1fe01f7e9c8e367edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a84b27db59a4a153d1070689f2ccf975b2b176e1c69dbe381340ef1f98fdc4b453abda3a2bfac3069ba7f1cc50a81c2520e412fab4e5d397ecf739f280d5b684533d5d29cfe7e7302ec144b4e553acfd670f77e755fc88e0677e31ba459b44e307768958fe3789d41c2b1ff434cb30e15914f01bc6bc2307b488d2556d7b7380ea4ffd712f6b02fe806b94569cd4059f396bf29b99d0a40e5e1711ca944f72d436a102fca4b97693da0b086fe9d2e7162470d02e0f05d4bec9512bfb3f38327296efaa74328b118c27402c70c3a90b49ad4bbc68e37c0aa7d9b3fe17799d73b841e751713a02943905aae0803fd69442eb7681ec2a05600054e92eed555028f21b6a155268a2dd664052528a5f8ed028f59af985ad1315c2e25aeb9d7f134e4bf478642ab96b15d3b3e13ce2387ac84dc0819e81260e11d392a5f06db8b5633de281a0e9c958c24060297f608af1dc51616562b1ffff6e2a28bab1f7772713a0a4b56fe47fb5a7b73aeee5345566ecf3e95e825f92eb469eb5d69164206a0ea1ce73bfb2a942e73703214d270d80534389b1a1e2bba67481eb3667d6d38254ac4b44559b4708cdd12898972a895bf0fb055cf1fb9b73029d6bfb27da2b5294f5cb354a894322848cc3d35b9554a5f62b44a7dcb25406e5ba07882cb6473714e77a051a7dcd29fea0a943785b325cdab95404fc7aed70525cddb41872cfcc214b13232edc78609753dbff930eb0dc156612b9cb434bc4b693392deb87c530435312edcedc6a961133338d786c4a3e103f60110a16b1337129704bf4754ff6ba9fbe65951e610620f71cda8fc877625f2c5bb04cbe1228b1e886f4050afd8fe94e97d2e9e85c6bb748c0042d3249abb1342bb0eebf62058bf3de080d94611a3750915b5dc6c0b3899d41222bace760ee9c8818ded599e34c56d7372af1eb86852f2a732104bdb750739de6c2c6e0f9eb7cb17f1942bfc9f4fd6ebb6b4cdd4da2bca26fac4578e9f543405acc7d86ff59158bd0cba3aef6f4a8472d144d99f8b8d1dedaa9077d4f01d4bb27bbe31d88fbefac3dcd4797563a26b1d61fcd9a464ab21ed550fe6fa09695ba0b2f10eea6468cc6e20a66f826e3d14c5006f0563887f5e1289be1b2004caca8d3f34d6e84bf59c1e04619a7c23a996941d889e4622a9b9b1d59d5e319094318cd405ba27b7e2c084762d31453ec4549a4d97729d033460fcf89d6494f2ffd789e98082ea5ce9534b3acd60fe49e37e4f666931677319ed89f85588741b3128901a93bd78e4be0225a9e2692c77c969ed0176bdf9555948cbd5a332d045de6ba6bf4490adfe7444cd467a09075417fcc0062e49f008c51ad4227439c1b4476ccd8e97862dab7be1e8d399c05ef27c6e22ee273e15786e394c8f1be31682a30147963ac8da8d41d804258426a3f70289b8ad19d8de13be4eebe3bd4c8a6f55d6e0c373d456851879f5fbc282db9e134806bff71e11bc33ab75dd6ca067fb73a043b646a7cf39cab4928386786d2f24141ee120fdc34d6764eafc66880ee0204f53cc1167ed20b43a52dea3ca7cff8ef35cd8e6d7c111a68ef44bcd0c1513ad47ca61c659cc5d325b440f6b9f59aff66879bb6688fdb462af43582b983f92b5698b87db46e4b02dd8e81eca555a44f2f1aef11d88a0bcee76af9ad3f9c46a67062e1a9ca7ea5c014384af07219c7c0ee7fc7bfc7933d174650f46b4cc000190c19b44c57ae891aa86646c10a177a8626be064409931c37d9e8bdc433b7d79e08a12f738a8f0dbddfef2f2657ef3e47d1b0fd11e6a13654db2854fcbff49aa0dadafec320b6ed2d4b279aee9060c1b221e2eb2f13b0691c4d842406d0ec4282c9526174a09878fe8fdde33a29604e5e5e7b2a025d6650b97dbb52befb59b1d30a57433b0a351474444099daa371046613260cf3354cfcdada663ece824ffd7e44393886a86165ddddf2b4c41773554c86995269408b11e6737a4c447586f69173446d8e48bf84cbc000a807899973eb93c5e819aad669413f8387933ad1584aa35e43f4ecd1e2d0407c0b1b89920ffdfdb9bea51ac95b557af71b89f903f5d9848f14fcbeb1837570f544d6359eb23faf38a0822da36ce426c4a2fbeffeb0a8a2e297a9d19ba15024590e3329d9fa9261f9938a4032dd34606c9cf9f3dd33e576f05cd1dd6811c6298757d77d9e810abdb226afcaa4346a6560f8932b3181fd355d5d391976183f8d99388839632d6354f666d09d3e5629ea19737388613d38a34fd0f6e50ee5a0cc9677177f50028c141378187bd2819403fc534f80076e9380cb4964d3b6b45819d3b8e9caf54f051852d671bf8c1ffde2d1510756418cb4810936aa57e6965d6fb656a760b7f19adf96c173488552193b147ee58858033dac7cd0eb204c06490bbdedf5f7571acb2ebe76acef3f2a01ee987486dfe6c3f0a5e234c127258f97a28fb5d164a8176be946b8097d0e317287f33bf9c16f9a545409ce29b1f4273725fc0df02a04ebae178b3414fb0a82d50deb09fcf4e6ee9d180ff4f56ff3bc1d3601fc2dc90d814c3256f4967d3a8d64c83fea339c51f5a8e5801fbb97835581b602465dee04b5922c2761b54245bec0c9eef2db97d22b2b3556cc969fbb13d06509765a52b3fac54b93f421bf08e18d52ddd52cc1c8ca8adfaccab7e5cc2f4573fbbf8239bb0b8aedbf8dad16282da5c9125dba1c059d0df8abf621078f02d6c4bc86d40845ac1d59710c45f07d585eb48b32fc0167ba256e73ca3b9311c62d1094903570519d4442f0200e6ad11f2452dc9ae85aec01fc56f8cbfda75a7727b75ebbd6bbffb43b63a3b1b871e40feb0db002974a3c3b1a788567231bf6399ff89236981149d423802d2341a3bedb9ddcbac1fe7b6435e1479c72e7089d029e7fbbaf3cf37e9b9a6b776791e4c5e6fda57e8d5f14c8c35a2d270846b9dbe005cda16af4408f3ab06a916eeeb9c9594b70424a4c1d171295b6763b22f47f80b53ccbb904bd68fd65fbd3fbdea1035e98c21a7dba5fe1089f7d1c032f24d36835aa8815266e897ff829403cfac3a715954b9b68958a0111a2c9265633ba2831a2e86b941e569d58d99c1383597fad81193c4c13151f40aedb487b5c04ae3b1ddfbafa26e720099f26d5a7535aee57306fd2c4f30673cd9b698fecf32faf88f62e21c90665859dd26833d21d9bc5452bd19515d3fa5c1e68bc209b9dc2a10ae6b630726a67b33603c691fafc281dd94dc9888a68c4f45155aa7897c045aafd9335be2e0ddcf5f586d7f6b4fe12dad9a17f5db7031", "917f2fcf5d1873f7ce8f639545680a90385eacb91c0f143ff6c059102ae97c3c", "6bda3c86c0091d45f240b804ec82b246837fdc212d636566b19df682b646601b", 0, "650051", 570688904498311, "2d1fad3c7a08326d110a037d86adec479077bb93f81ad138e00df39782ab5a12", "e0e80e4add7fe45b91f700bcc16ce27c0a0b859ec92455002b15d4b9657bef08", "e0e80e4add7fe45b91f700bcc16ce27c0a0b859ec92455002b15d4b9657bef08", "c6773d5edfd1ab024c83c0824f4fd15b13dcb1fdd042761ebe28c6ee6ee2ba0e", "c6773d5edfd1ab024c83c0824f4fd15b13dcb1fdd042761ebe28c6ee6ee2ba0e", "c6773d5edfd1ab024c83c0824f4fd15b13dcb1fdd042761ebe28c6ee6ee2ba0e"], + ["050000800a27a726219651371fc998c31f4dd20800015058e5754c2104000753ac515300515202f89d3226fb53292bfb5dbbf7ff013af3341ea7a35f553f6d7a0221ae195ea84eb97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f5fe1573f57e0685406994d28264e4d74d5a25fa3fa38fed813c505dd90289f301c75e53a716c5b54a45eb32c165448d4d5d61ca2859585369f53f1a137e9e82b67b8fdaf01bda54a317311896ae10280a032440c420a421e944d1e952b70d5826cd3b08b7db901e5849f96bae6f2056f33ab1e6989d7d264adc97855a990103b4d1e6350d5c31a39c3caf69459e462f141be8b39037ffa255ce27e4ad7b566a29620a9f011ab08fb2ad3050652b3f65b8e34526a2a15fc2ddc5b5113e4882c7cca0dd5577be067ba7a175dae4bbe3ef4863d53708915090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41db908fdab8b18cc7304e94e970568f9421c0dbbbaf84598d972b0534f48a5e52670436aaa776ed2482ad703430201e53443c36dcfd34a0cb6637876105e79bf3bd58ec148cb64970e3223a91f71dfcfd5a04b667fbaf3d4b3b908b9828820dfecdd753750b5f9d2216e56c615272f854464c0ca4b1e85aedd038292c4e1a57744ebba010b9ebfbb011bd6f0b78805025d27f3c17746bae116c15d9f471f0f6288a150647b2afe9df7cccf01f5cde5f04680bbfed87f6cf429fb27ad6babe791766611cf5bc20e48bef119259b9b8a0e39c3df28cb9582ea338601cdc481b32fb82adeebb3dade25d1a3df20c37e712506b5d996c49a9f0f30ddcb91fe9004e1e83294a6c9203d94e8dc2cbb449de4155032604e47997016b304fd437d8235045e255a19b743a0a9f2e336b44cae307bb3987bd3e4e777fbb34c0ab8cc3d67466c0a88dd4ccad18a07a8d1068df5b629e5718d0f6df5c957cf71bb00a5178f175caca944e635c5159f738e2402a2d21aa081e10e456afb00b9f62416c8b9c0f7228f510729e0be3f305313d77f7379dc2af24869c6c74ee4471498861d192f0ff0f508285dab6b6a36ccf7d12256cc76b95503720ac672d08268d2cf7773b6ba2a5f664847bf707f2fc10c98f2f006ec22ccb5a8c8b7c40c7c2d49a6639b9f2ce33c25c04bc461e744dfa536b00d94baddf4f4d14044c695a33881477df124f0fcf206a9fb2e65e304cdbf0c4d2390170c130ab849c2f22b5cdd3921640c8cf1976ae1010b0dfd9cb2543e45f99749cc4d61f2e8aabfe98bd905fa39951b33ea769c45ab9531c57209862ad12fd76ba4807e65417b6cd12fa8ec916f013ebb8706a9a556c762f8850000bdcbd49fe4f85b623c7828c71382e1034ea67bc8ae97404b0c50b2a04f559e4999d9c09901bf39caac48dc11956a8ae905ead86954547c448ae43d315e669c4242da565938f417bf43ce7b2b30b1cd4018388e1a910f0fc41fb0877a5925e466819d375b0a912d4fe843b76ef6f223f0f7c894f38f7ab780dfd75f669c8c06cffa43eb47565a50e3b1fa45ad61ce9a1c4727b7aaa53562f523e73952bbf33d8a4104078ade3eaaa49699a69fdf1c5ac7732146ee5e1d6b6ca9b9180f964cc9d0878ae1373524d7d510e58227df6de9d30d271867640177b0f1856e28d5c8afb0630fe4fd5f22125de840fcc40b98038af11d55be25432597b4b65b9ec1c7a8bbfd052cbf7e1c1785314934b262d5853754f1f17771cfb7503072655753fa3f54ecc587e9f83b581916092df26e63e18994cb0db91a0bbdc7b6119b32222adf5e61d8d8ae89dae4954b54813bb33f08d562ba513fee1b09c0fcd516055419474dd7fda038a89c84ea7b9468287f0eb0c10c4b132520194d3d8d5351fc10d09c15c8cc101aa1663bbf17b84111f38bb439f07353bdea3596d15e713e1e2e7d3f1c93cfcb46238b6e0398b46d76aff2d8f05745c465a94fca4365a93741b2e7516a1d2a00de7d1f80a5d3f791d35058a96380dced302c60c0062f67a331cf71e004ef78407a7c51161c7b8d46aba76714bac170d185e27ce3ebff2446164e0db23c02ea332b6afe11084ac25d73e63e51e75f6da74707bec9b1c2224f11dc18ed0a6effeda06c4be24b04846392e9d1e6930eae01fa21fbd700583fb598b92c8f4eb8a61aa6235db60f2841cf3a1c6ab54c67066844711d091eb931a1bd6281aedf2a0e8fab18817202a9be06402ed9cc720c16bfe881e4df4255e87afb7fc62f38116bbe03cd8a3cb11a27d568414782f47b1a44c97c680467694bc9709d32916c97e8006cbb07ba0e4180a3738038c374c4cce8f32959afb25f303f5815c4533124acf9d18940e77522ac5dc4b9570aae8f47b7f57fd8767bea1a24ae7bed65b409e1dd26b8dddd68858d6f5161f073d90636860a9aaee18629b06330a8ee30591debfcef56a026bb28c3b06ec2cfaf5b79ab72694d1d012a7594dd80ae7dfa0c00", "a09b04de6a186492976e849bd727a344a6f4469a66fed1270a9e205da9e5952f", "b14e10849868f413ce7b246081464c3b0c1bca960fcfdc26a6b4d46bbfa04e36", null, null, null, "a09b04de6a186492976e849bd727a344a6f4469a66fed1270a9e205da9e5952f", null, null, null, null, null], + ["050000800a27a72621965137c2eb518f68984d020000000000", "f540b62cb4f80c03c0e4cae79511e24328e3d4c75443b79d103d7943f0922fa2", "38b79770f572fdd077843cb2594cae07ba43a178ff05677f9bee4ad1170afbd4", null, null, null, "f540b62cb4f80c03c0e4cae79511e24328e3d4c75443b79d103d7943f0922fa2", null, null, null, null, null], + ["050000800a27a726219651375e3dbaf7ae12670d0001516cf4adec75070003656500000000", "74245199dc164aa8c04b890c614069c6b553bab9f0f6bbe8db8bf10441e3c883", "38b79770f572fdd077843cb2594cae07ba43a178ff05677f9bee4ad1170afbd4", null, null, null, "74245199dc164aa8c04b890c614069c6b553bab9f0f6bbe8db8bf10441e3c883", null, null, null, null, null], + ["050000800a27a72621965137ff6acc0ffc2e490d03146b9d49dd8c7835f43a37dca0787e3ec9f6605223d5ba7ae0ab9025b73bc03f7fac36c009636363635100635365bca7e54cc1a12d127b57c8138976e791013b015f06a624f521b6ee04ec980893c7e5e01a3362035904ac000053d7445fe2d09130f63511da54832de9136b39f4599f5aa5dfbb45da60cdceab7eefde89be63f3f7c00452006aace1405def0244fd7f99b67d040004630063ac12f6465073e1020009636a5351520065ac65000000", "2b26e72130e401b146d2fd73430eb98ad90ef47ae9eb9cc8b6cde010d680f442", "f745ed405535760999edd6c0815682c69d2b14b4162827249aa20e8d5588584b", 1, "ac0000", 693972628630138, "4f3a41a6b7623522a8c9ae5cec17bad3af53f2040ca2214912b04a4afd9bac2c", "c37050589340027b5eed40a87f7d781a732a4dbb66b9424f4c5c5be90cf76d98", "0ce32aaf459fa555f19cf99f53e15a5aef51dcb0422843ba2c94a377ce6b0d5c", "526ede6d87426e53f028f6a0ce5716461c8999cfa7bf9854ab700e24d47752d7", "289ba674a4582b6c3b9e79dec8838019d9e58ece245357d8a4a886288cf59be6", "5d988441dcb0422b6c9caf0dd0edee928d2733ecfff263e9f5ae52363d5b539e"], + ["050000800a27a72621965137dedc5e5f0756fb19000133a490768c1600000851535351516563000001dab9578157ebf9cd8113078866e952d6218c69455fbc9c5548725b189cc216ab58483a5a4d28cce4b2fae6513dc6c094307eb498166ba95999ac5f8464b2ff6b295d6e94b0227b5c43a121a34ac907e8d0d07a2d6d79712a776614b0cdec4de09b2627218f0c292fa66ada945fa55bb23548e33a83a562957a3149a993cc472362298736a8b778d97ce423013d64b32cd172efa551bf7f368f04bdaec6091a3004a757598b801dcf675cb83e43a53ae8b254d333bcda20d4817d3477abfba25bb83df5949c126f149b1d99341e4e6f9120f4d41e629185002c72c012c414d2382a6d47c7b3deaba770c400ca96b2814f6b26c3ef17429f1a98c85d83db20efad48be8996fb1bff591efff360fe1199056c56e5feec61a7b8b9f699d6012c2849232f329fef95c7af370098ffe4918e0ca1df47f275867b739e0a514d3209325e217045927b479c1ce2e5d54f25488cad1513e3f44a21266cfd841633327dee6cf810fbf7393e317d9e53d1be1d5ae7839b66b943b9ed18f2c530e975422332c3439cce49a29f2a336a4851263c5e9bd13d731109e844b7f8c392a5c1dcaa2ae5f50ff63fab9765e016702c35a67cd7364d3fab552fb349e35c15c50250453fd18f7b855992632e2c76c0fbf1ef963ea80e3223de3277bc559251725829ec03f213ba8955cab2822ff21a9b0a4904d668fcd77224bde3dd01f6ffc4828f6b64230b35c6a049873494276ea1d7ed5e92cb4f90ba83a9e49601b194042f2900d99d312d7b70508cf176066d154dbe96ef9d4367e4c840e4a17b5e5122e8ebe2158a3c5f4cbae21ea3fa1ae6c25a9462ebcbb0fd5f14554bc97747c33e34da90c816d8d0d50bfe37618c5812891484fa259322c15092d4155d8696d6f12f24fd364496b3be0871ca3dd9625348a614b59bde45885649bae36de34def8fcec85343475d976ae1e9b27829ce2ac5efd0b399a8b448be6504294ee6b3c1c6a5342d7c01ae9d8ad3070c2b1a91573af5e0c5e4cbbf4acdc6b54c9272200d9970250c17c1036f06085c41858ed3a0c48150bc697e4a695fefc6be7b68d0120200335f7ad07e1a46dc767ff822db70e6669080b9816b2232c81a4c66cc586abfe1eaa8ca6cf41fc3c3e6c7b886fb6dac9f4822b4fc6fff9d0513d61a21c80a377671d135a668a0ae2bb934c82c4142da69d12ca7de9a7df706400ec79878d868e17e8f71ea31495af819a016cc419e07c501aa8309b2e6c85b79b2763733a37bbc0420d42537b871b4294a65d3e055ff718dd9dc8c75e7e5b2efe442637371b7c48f6ee99e3ea38a4b0f2f67fc2b908cda657eae754e037e262e9a9f9bd7ec4267b5420240add20a389cb678706917f97cc8162fb1dcc6c28b6eda930f055ce86bd84ec0f3ffacda10485cce037a43b0780fd591452390c924c04f54924bf6ac0d025b39b01398f37e78067cfa374bb53eb00fb2a95d03dabf6bf6c5f77feeaf651068aae477fce410ac2d5de6095861c111d7feb3e6bb4fbb5a5495549597279835c72c633a4bd01b150be28ec3d843f1bcfc0bf35b772a3c7263dc89016ed4a112ed8406f96980288726599c1238978691ba421df6027de5af1e4745d58681061564d951eb7684dedcd335fb1bd2a6978cdb797e1f3b659d3a557e407735753c8f8a2b7d4385f1c95af937df78dfd8757fab434968b0b57c66574468f160b447ac8221e5060676a842a1c6b7172dd3340f764070ab1fe091c5c74c95a5dc043390723a4c127da14cdde1dc2675a62340b3e6afd0522a31de26e7d1ec3a9c8a091ffdc75b7ecfdc7c12995a5e37ce3488bd29f8629d68f696492448dd526697476dc061346ebe3f677217ff9c60efce943af28dfd3f9e59692598a6047c23c4c01400f1ab5730eac0ae8d5843d5051c376240172af218d7a1ecfe65b4f75100638983c14de4974755dade8018c9b8f4543fb095961513e67c61dbc59c607f9b51f8d09bdcad28bcfb9e5d2744ea8848b2623ac07f8ef61a81a35910b8a1baf39a919a7b60bc604d63185f759221d847cc54a22765a4c33475b5791e9af3271fc8d9350667090d8184ec50522d804f23c4fb44ffa481bc92ae408d1b9f2b131904f9705c59e2f4bde7a3b2c085d93fd2abc5e14d163001a12f51938d021afa92239b873dc6c357eaa8af4ee6d00540657fe32914103b5d98f68bd3e2b5359f08ccd88d0c811e4c31fbb49f3a90bbd05dce62f344e7077593159ae35050b04c9e6b86bc432dc8b048c73c0018ca5b69411297732a4e1aa99a928c71e7a24fd277856aa42501e51b012aea9446a2104e93f815a0b3a29b458314f3d8be2b9823d342f46213e942a7e19a46e970b5c506708430317b1bb3b35df68ae33a4926a03e6bfeb5510416fcbb0524c9ca5074156cc5a5d6fe1c995edc60a2f550411aa41e3da3bdcf64bcf04a0510571b936d47e55cec0330ee8dfe73563404f047d7f3a8a3d7743bc554955210f1eb0d08599ea77d5f974d87176d37d98b9c0ad440407209ed6a9f08464d565593e1a63b938536b49244e97d880173b640f2ddb74d068ecb46cf289b7d891307bba37054cf91b31fc82f74d5fc461fc5e978920e95d2804bbd5be7fb86b1b0961f35386c586bf4890ea6d607ae27b4c2b271061857eecb8fd90fd08eb5c43ceb736b6831e8c110f16cfdb3a42711215ca70517fd02dd25c84236e8de61e7ed8a3f26c83f4beb392cc07fc375af1968a52510744e95f837499abf7d7eaef506f1883a751588c7efa506c3e8d006961b9416af621e21c6787c5cf16ef846090f40f6158484007a6f536f656c52985673ece7fac73a0ed41ab0051753a7caa89be3139afd9793b3e02f27f040046595acd47bf13fd0da27f09eda48036d3ee437f2ee8f8606ea97343c33584657f46dba99db5cfe6ca176fab7b0f3bfa0ab61e340c34eb9f17c7ec2be03b180f0bb6f434c2a6542e00e84373f4f4649cda32bf686666143f622aa480460b5afac518607cd9af8bcd6b58c30127316b25d5ea7bf6b0cab8542ff69d9b2f180be12ed75344a395aa10f852f083ad64ef40e9c0309e9bba54b8cb33c95498a69538d3ae5b25e247098306fa8c74a8ee5bca941531d61aac27aab3dc5617d5606c9577a2a8346e8d85b32b8505775108dc85e2ade2eac1e636e1af4054c8b6f57632df269c3723b320872e4c57b218358dc7e9905bb04edf92edf0df635f3bf361e57a13296e1447af5087872d636e27518a9876e15eb01f5e8ded81892511cc2851b00b832712a6d3ba5666517bcd3567621a7cf8445589653262020c33bf78031b8ee0707de072068c170570327e6d9f5c6ddc335402efc548862f5a07094fd428a7bbc15d7b38d05362c9ca985f58a76647d2be4c2cd6b3d17d6870971d7a098baf72c6f6f1214cf1faae488bd7de259d3415c2f0ddec7457004f35708d1eccccc0df65a04943ad5cbc13f295f000fe056c40b2d88f27dc34cfeb803be3483a9ebf9b5a9026057725d63ead2c0c0ff1fe26ac1e7bdfcd6fad875842d194f331750462c06b8d7982d67995ed5d3ae96a05ae0067f4eb1c7c93231bd39773cbe0a9d66b0c9aa8cff6a376e1f372eac6ac4e46cc0942245d4c2dcf02d7640ffcc5a6ac3a87f5c411551bcc2f26cb94961d53f95ddb19ae930c8d70f031b29a5df99ff36695e802cbcb6b58c1ba7ed5eacfa76414a41ad4a44f71f1b580d34c3a952920b254a145fea517f5b42b2f65ecd0f82595478d80a0147e39a9cf3ef02000860f7bf1778a151c9fa667f5b880e556fa05241b10f5ac9a8408e925b626b323a471fe3bede52bba097b2a99a9ba5a86658c3fd9ec55bfa9b328567254ab36d2c7f44d2c7e13eb54beb70ea8fa94b6c6e012d79e3f53689c2b1a18102fc2334b7d094eb078f666ef96efc00b8f27fe280e8dbaca68558a3f12b8767a075e88291c7fae19d48f858acb292fb0eec645ffcbbe0ca5f8c561b257d1232e20cf850610c5e7f9e837e0cb42b2255e563c9d87140ad39caa233f9e9d200ae7f3ceac6e8fa0e4221925059c0887c2d3b60978d81a678b9ed8e4486b4d1063c0960441070896898bd5c0e8f5f729c872a27325c36fece03058bdb035c4013b4216056762ce3a396becc833feb8aeac0a08b8a11d84d0409b734f452aaf016", "1a0181107ab15ab8fd5af75d55147271e15604288aa9e66f43026e5ab2ad1f44", "a54fb6614b3ba0f8ae349eb49f36122cd252ff4c64de1e2d00fb6b4021cfce37", null, null, null, "1a0181107ab15ab8fd5af75d55147271e15604288aa9e66f43026e5ab2ad1f44", null, null, null, null, null], + ["050000800a27a726219651378f50258683daf61202399fd047eee288bb4585851dc93eccc62322924cd13b5dd4eed66ed8d9972d772629ea640665ac5351510006c062468e4bd8f7dd9af698f52ae814634e81d7f3e0c420317caca9ae4811c6af06fe80a8c02ab7046500ac65aa1ea1b700000000", "7788d45d79fb61550ace6c24817c39e602b1f2df3e021bd864b099569c52716b", "e29e8659b5da9051d956484a7e55dbdd35f20874fcf7cb8d1577f7c6f02dec07", 0, "6352516353", 107504874564564, "1eb5be41c626f1301ce49caee6ccfaa05608abd89999d75a3f19ef657f566d20", "b2e2aaed42f57dfd724d104337d98f546e3dc85c42a17897fd7c560d6f3f16df", "b2e2aaed42f57dfd724d104337d98f546e3dc85c42a17897fd7c560d6f3f16df", "e6c150f4699656448a0c20fd7c1931921289732bad0e657db83db6b68f5bb92a", "e6c150f4699656448a0c20fd7c1931921289732bad0e657db83db6b68f5bb92a", "e6c150f4699656448a0c20fd7c1931921289732bad0e657db83db6b68f5bb92a"], + ["050000800a27a72621965137025f13ec913c29020001788f2602a2cf0600015300000342f736a0fb38ae3e42ec67b07486f90e60752e0dc6ee0365cbd21db5ae70fdba02691508db301d732272d965c14602e2297db65fa9699ea87ff47099ed36371b2db3811615bbf52dc655da35a9fc03f311be830e28550a71ce289b24fab93c0163a5ca959be63f37f2ba0d432366736d8632fce072b6ae5b6f3fd59d3faff6381e86576581e2af578119dcb6ecddaf2115bded85c81ac2a8136fc8372590f28a3676a8b403ae25ffd772f7081e9a32bcc1c5e2edd4e2a6576b783cce3aae11fa432262548856183ee682d5dc31beb38f061cbdeca7021a444e2dd417df26dcd220f2b731772b439e96d614e1facb486c7a7d5171b1de359f6ad3a96f649c969102a1964fb4b4a1a4279c68e6c372e42187d754e804a61653092069fb9b6d25266890808b015df28c801065da6febdc1a56bfd002625acfaa5373fde149c1cfc3649b4869696d44ecb12479c5ebef995f10029f8b530eeb3fdc2e50e8757fc0bb9e263023db82f878d9ac7ffb0bd4391df1d879899a3ef57bfd0d1f7755648edd85bb052a6edf71cd2628c987429f36dc505ccc43f30e7a869c9e255e2af9fcf30c121796d190000960cb6fe2f1bf246118b498f3247f9d484c73cf09393039e45326b8ffffb3e7e6159c46699f100792d4672950348a90552e45943beeacf03f3216f94e274d63d637d9f190e8a266cdeef153530bee5cb8355260505c2c2e5d990fffdc34ec0ff7f1af81b24ced0efa6213da6c7c60c487f5f7b03f8160a057f46d05bf8218b3add9c06893bd02db9b61191dfb133bfabe4858e47a4cc32e416ec08b8ac7915a43733f4406e9d967c560f344d7e904a28045d99f3af8c82e97e1b9c1b205e585fbebb48faf58f1b65dca2497e09a70aad4865f85715a280e186f3fc1740d8184d33e8322169521cdc132212939c84a108964e2de74b6ea55b4cb8f6f9bee98b10d415109455f48b776082dc30b4bc73477075511700308158ce2f2f9bf0f691b2ce53e61142cb740c15b7b623cf48b3f7bfefa31bcdc665c6d7123e95350811375947b055a43db07e03f33627df5c638bfad956ddc1ea7d7620a20f2792f63817a1cf32580d04274234af2a51b56bb68a29e43a954142ba4ca6823bde9053d72fdadbc61ad5936c53fdd7579441c5b969e08e9f2e1eb20c909b3de6553f5748484d9ddcc4227eec92d780aa51de20e95968a36c4257bb25f3ff75dbc38fff2f2f271eab89c628e18b5fcb43802ce65256d33ee8f5fe037951be9a760734460a2cf026bc8ddaeacef005abcfe0824849a30576d5846d604542a132da9570762c0b1c65855deba8422ca4b88ab2e03998fc6e1223e7c42672656b9d0b387642dff188366b65a3ae49c206b9a0636407fd7da93fd0de6400d3ab8977485cddfbed5932f507b79947adb2fad37615aa717db5f298099f20f263b359a1151a6b75c01365eb154ae42140d6e10342f14f34dc33e07ff0e4d1a6be375b32f84b92e5d81ebb639c4f27e715aa42cc75707d4ebd1bbfbe8f90fc7c953e7a9715e65af8267373d3451674ff084efd92ccf3bcc7aca1467b6327e4f9522b2cc579a7a8fff7ca7cf145dfc13eafc34153b2c3e8afbe53444d0c73b3bd5bc870b01cd457911e356313fd1dafb4c8151634a01aff7cf116d433c3d2b3adda9cebe18f7d172443e5e7b5ac9abe8db2256d7ebe2ff28020939503870597b9a955892c7389650a2d42ec92be723fedf2f2ede5a472aa1e74f33ad41901544edbbe3ac464cf439196015f4f22ac2b8fc01496beab4d45907f479812a259431a2cbc93d4f3b84e4dd366020273a6752e501af6ff1b78ddc817e6ea351d6006becf8d2ffb03990f67774a81e05b7f4bbad8577fa27c9de64e1b11dcf384f5956443748755a9fc6f2a00b10c3657ebac03bfc0b587bef2f45ec8acdaa51c143b0cb25b9142c61bd790a80d7c23f90cc03495b51e4d2843e557f9e2545108c6c6fae359f645c276891c0dcab3faf187700c082dc477740fb3f2cd7bb59fb358554e94c7e678ce01aebf94e515e49722967995aea858d64e7789ff306369577228180326a5b0af475e27a54b207b41f92e376170e3fb005028261c99c2dbd0eedee871c1c0f48b8e9b8e4be77d1b737fe21f0fa5a18ebb52755b5a6cf6130fb56944cfab87527c250d113b29bcac9aaa10c2e7de415edb0806c6da03020a134ca7ecdc8da1bd57a37f55a46940b45b241b1c16ee100927d1bd860d445a9de50d4c384d6e1d00108026c0ea5ebbf0b72fbf5c370bce18d3acbc46599099baae1d802f77333494a7ae130fe86e835cfb9f5db2a721ac646eadf76e426d8e888321827abcbffbfbf52d75e07131cca1e8c78c51ed377cd4afa894bd9bd12e707156da0726f7cf5729fabe3721604347642ba505ec940bab48370845a5bbf483ea8f856e5e28600904b97226eac27557deff7c656406f9f959996093b2cd710d3e1b3299dc9521f8b513badb01029a31b366a370fa1c2eb426c7a9f327e56b3b9b5b32a226b2de14b7f5259bbf525aaba565b84b845e163d1caef2533c3981637204f96a59c8e8024d9041b2029e94c15245f1a958840ba3f380a4d20f1184e77827de3ff8f3d73459afe241f723c084823230e003d3d21e53501ec0499b083a7dad685c57127f4de64733a880c2db28fdaabf1b542d205f664a35135712711dcccd931a50b9c5661882360d4cac0047681bc2e2b3bf6c99760d7cfb4fa21394377a4551c76d1f75ac03c262054dffd79a9ded05e888958199eea4501e2990a53a5cd2a46a401576588fd7d058a26f28438e5782f45ac1d07f6f6f5ed73741d5785837a6b844b474775718c29dd99084e9f88ef153a8329f532a69017dc3a97ed754367723098e5765840b022897244745fbbbb30a7cb54fa0511166e9544122000610bd2aacbd82325a59b95154ecd82c88d23abd1e20770ffb8aabf83fc0734964ccd411d1c935714e24aab566f4f08424014c4eca91b590f082b473f361c87415d37bd20d70fd0b52b6ddf1865f766702e32b05b3cf1630ee8597aae19633f3516a8555ac5be32c675be1817efbffd9369041a089c283f19649968c2498cde56f500434f280d77a9c62e43cbd3f136a4c6a00a43e6ed530cb2e8ae838860adc88aacc7bd6a00ae0c19ff4533a485efde082b5f4d1f7a8ebe7ed82b7b05a8cfe1e373459f1bdcbf9525747e8c9508a555facb798740e0bdf994d9739bbe5538a0ae0f076c582c0f5ba878b99b8249db1d7e95056c98af083d98cb0ed9e3f7436e1c7643766f966b83e999206ebd1393b9b2a7f414480fa017480069f85c7749c435ae2fba2ddc1038d547d84854817ef39635c29827aad86726c9ade3b265b9086c8b5b75ef56fe4bd8b4d62893895b3fd2734fdac464156d7e5ebc7ecf1d83b86f659637e3b142c164963b8cdcf4ba4f4035dffc5a789458847781918ac72fc18b01954ea6bf46500200e2f790fbbac4c45bc32d240ac16391202822330b32d58e6777765f22a4116304fd0e01b65b2ec516393ab3751b5356d2b0c9500c0f3e469181035bc3660f0b8f9fbe6e40b5e89cb79b063714ca75e72e2e100a10d63bf784df0820ef25f8ef40fe5f05fb95683f9105ff3cb2d219ab76605a064f69219f1dc0d00b3b48642f970dc00cca4b8b43308be18286ec5a4288d600a3785cb622d468a4c6969b3792f2485027d0ad9aa4a9c2cc972f9ee5190a95b1eb058dddd8c08e7d753f5e011b2bcfee1d52c1c4f2cacda30bdb6930653c0cc4486e60e89fa849b32083ba9db453fb8df683cd68754c87daa731f570a7a4060af0ce700d31bca7e74b3e3ba3d0e8a6392a062b8e86d9d7d00b21701e7b062e06b1bcd82a01d375626fbf872d27fa4511f5f8cf8c9abcef2a990176ae339325302e42236a1eb1057288ce53221fb608a547f2cfa51216338d4ea1330d6bad847fa88c1e1aa3e1565d647779bbd0f70e85f8c7d3aa5c2082b265249df057011a79535e36c44fe9eae846fa19c2253746bc558925944f898bb2cf4449604bccb23c7404fe995e0035a3d00bb2a246e95bfc60145c6a009687684460271ee1332441ce68db5c8e802e0df35b93bbd7f3873ea00d191612a18042b25520c6e5dc0ea452f3731c8cb65082a622a7c2e0013ea47d0bdd42d6990466649a905c684c3200e477ea165300aaa4cf6adf8e7d13b0fba29f4499016ef53c1043b43891e734b6a4351aece9b0e9297f06403d1978ead2da0dff82cd1f55ebca57b6337c8513", "5c54aaa94342609f04b4318917ad4662934e5f0bd9ab877b9feb7ed8f88a721b", "27058e12e3af3da6acb4871a16fa258121e5483e2a51a069f1018c5d27092300", null, null, null, "5c54aaa94342609f04b4318917ad4662934e5f0bd9ab877b9feb7ed8f88a721b", null, null, null, null, null], + ["050000800a27a7262196513779813d20210c6f10000331efbaa1ccfd050008515300ac5265ac65657c6e390ecf040009516aacac525165656394266fd6496e060001ac000001bbdf186d8c75d0fb191a1b119b2a4ac4a441813f92551d941f6336c87a6ee01ed7992cff3eca24de3e0984e10e68ae387534b96cde3792f135bf5f68787d370ca44e77b3ed5ccd60369c0f4760aa05c0a6f62ff890558d7221f920bf1f1b778a06e57a85302de1d6919719f38dd180e972a15d5cd0fcc57439a4350eaf098d3b85ba3dbeccfcc295fd490051db41e2c4d181443708b19a4d0d0e2efa1d79e00f66e4c0155a9c74a7a57ccf34c483ac7da1588a1b6b9941f11040f94cf78fad89bf11fed69aa0d83105adacdd4e5f04a62424023c9b9e33c4fb7f12bdf21f07f265c537d51c6551f4617b915d21991839c3d0d36393d646e0a8a41509217d0e7d2ca1a0a0d677a3eaca23edeb07b74e652a0bc50c6c083a55d6c7306e74086f4768933aa24873681867a7893d77cb7f29b8c847c583f2d071a686616e206719f761ae39c110442e06163d2b84590360695d4e19849e634f24d9ad396c19ff83ce74f46e645f932e141a41195936c85d514414f112e60b1a2537c38d6dc6c4638305c9bd6c62e366bc63123e3e6dd36eedd3136fce8deeca2aa09a3298a39d83859efc9b2b69cf9a7dee08a98e4be558ac7912fdcb42209075420260f7cad0f2c01f2afe33073f26249d944f7a50dd84839bc3ea7fdee4ed71449cf07533d26e1e27a3efb032c3a3b34bd3092622d2062ae536ef5149c49b5bc9475eafab6e675761008b0daddeecaa604470bbe0fada255d290e92b190c2c2d8c2dee5455d1fa9a9f3db7779b584643464aa8014ba66994de25517f83980e66ee4f62314ae6dbef452d5d38b0a16f3991f36d8a8b39ddc0d5595eed98762878cdf3f4a2edc5cda77d5fe4faf63a15f568a540da57dd9beb6fb1a977ccb91b4d79cb39b28911a29e7bf028ac6103796dfb6b20967239ad373c38c53f6df1823d4950a0283e99b9c06ab2966667c9df677716b0caded818df9e449c072e22f9d98bb0f9b03bd5fd013fcef3ed6a49aeb98720254087ef728e31947ffe8f766e63ee46ff20816d5fa8ff55a26398961490ab9ae366fc5a2d1996ed693ccca82356f600ab099f6eca8bfe645270d3f95edba5b0de7a32819233bcc754a5ce2e5ea07842e5ff2cebe62ad76e8eff8d15ea4c24a5f207868319a5af6b035006989624f8ff70300921a63b5da5f2553b83a947b16424bbf5f7cbc70b4cd7e8e3c951f358572e33787e7d52704a6721b30efc41017ae4d231558c5c82cc7dd7e3356c09dc24906f0438dfcc300856ac2ced8f77fa8015736c661e80248aeeb774874aa79d290b8f5027a0a509537fc7c689b7ad86116cfec2647ccaae1c74b416f3e6ae8f7cc60eaaf7b6a590d51544138e1732945603a53462c60e1f6cb0c9ca0390c488224c313269fcd59fcb611fb2049dd4be68c3cdae9aedc88ad2cc8e37fd895017f6727541a8e35dfaf48781ea3544489b9470b0dc62ee163c05f9412fc9713aaac25b4c26eb03f716646611ad7c2ed9be4c85e42f734b5786a82ce4177a3c4764760355adfbfc3f0c84665bd6a1ebf895f67e11053883449f70695d0c4e951d71305ef33d9737126d0e66210", "57cfd23dcf797ffe6c57dcf1298234f1cead32cf5b6dc6495b863ead11866743", "db39cf4f6f67ad1157f3702298b0d6effcd3ad8420b89e754555c00567403747", null, null, null, "57cfd23dcf797ffe6c57dcf1298234f1cead32cf5b6dc6495b863ead11866743", null, null, null, null, null], + ["050000800a27a726219651371250926f6a8e6319038f69ad9a9192b302f26bdda465d90b94b12c57fa3fd6930083f184438d8a889d3f5ecea20252636736f2a0f18e26f4fa45d1be8f3dc4a707137e95d2ad594f6c03d24923067ae47fd6425efb9c1d50086a636a5300ac6551fe806f5756acb562f13c0ca1d803a195c2ebb2ef02ac33e6a88dea075ba996d3c336648e8694d3a10163ca531beb031aa2377fca6c00000700ac6a6aacac53d4e959af4a2e020002acacb75747fc903c05000300ac000275614934b7eb2bef279e31b965e6d33874b4e11b257f8960316c9a17c6a573a58f57015ca402c67d925c99acea3ee8cc4b008c5cb43966e714ef480fd05e07c7b2dda9aa3966113eaa293d3f622b309d64803ce1e6378b6aac4fab527c43cd45dbad57e9d25969bf43d0143b76c85ca095a90dac4ba07817f02631d72ee5dac1e16d0b77f02028da464100fde76d83dd0bb224f7b57a00c02f68ae648fdc529957a10490dce1fddbb0904f0d518bb387544019983b616975a78e74d854fddc4900f753db06f51001007c57ec890aff51a4d1d39ecd0e757f2917c738dd999b5c6e77da0b6b40a87006ed0a3c1a4b9fb18dcccfcdb6ac0c2421639cda0075a20dc5111b8d3d3199495bd9133dbab94541410e4fba92c7b606a5cb122f140cf1a3596f2788f3c8b92660f14cb65af5dd23dfdbac1371ecf4b33712fed2292c44f70834cf96c05d58827e69bfc2e696fa0874869c02f3dca11c3b90cb214e68bc1cae039d7a146cdc1d609d7a6b3fd5d461b0951c82cfb3e763fad2d1bc7678cdf82779f8fd5a1ce22a8d3c4547abd959838a46fb80afe01f8ecc9931513b1962ec540856cb189387cfbfb255167b55ef4bee465668b20ea4118ca569ae480e0f6e5e043a357b36d3ab36c861f2278301dce57674d5073b3a6f5103a0793af1b7d46f957e22d8d2583bf181836c3be9930bac8fa460e968aa7109870bbed17df5f888c8ca1467ae17dbbcde31c1105cb5bda88ac6c627002ce21c02140ffe81ec58bf1e6d1bb7aaada41fba0bb588778a7f65202ad811ea73d26c74550395aff75325107c9b3f9ae9dcdcd86ed081a2e7424719a3d185b7e0a43a472e298ac0afdc5287d7ad124cd9405a655059e183e964e7fc98bb48e367bbd68d8c9ece7b6bd213624856083fbf920ca53ffc8335f072af593bc991e805c3892b939632449554e40ae3c7a996c0ee02fc626603def306fd1ff4118b1c8e0669efe1b10fc16087199815430bf815a718bab91d8cf2950dbf25b42ec49a1f335ddfd5cad4bbafc1dcba2cd1c3d22b5605b9169e4ab21ff81dc36b21ec349b66bd88ffe7ff46ee18ece49e64198758938bc05215dbd8354613a0029489fe7b1dfebda9da980147397e97aa45d09451120803135701b8595c6e0f1764c3336e48e7f774675436502edda9cc00c7f24cb6090a80d0a61949608ba673710e93140630bf8672c4143d8b7acfd74e72c04d89240d22d67c92d7913f9906f0219e84ffd3932f8b4144461d0700cb7ad6cf9417533c26d2050d25b74b0e7b5a54df51157dc9e62d5f6c4abe5ce90a7fe2e52a8d780678cfcbdc0d5d9e43665af0fdbf5c4b7727680f4c534b54f9d5e9a357c836e085e10c1e3fac4058b682c68e54facae0f9c2dd4d64d9046152b4762332939f17e6aaf7d8b9d358e2218d4e0d69a4f119e1c64eec4c8b532809707131f01f55c7ad04cfb63f7c4a3d0a2b0ffb0b05a6be055b8c94ca80bb0a1d13cd4cd69ab98304ae2515d5f7699d4abee5c20be609d873511012f234bd85a7eff5fb634cff2658ba6516048563095ecefb3015ee3f03ca52a177f261ecdc26bc089d34c6404846e9c647fcfe98cc6acdbb464f64278ad8ce9d1ae0d415bc0c05245fddaf4ebc8dc703a85cb270f796ad2d937e2ac0d5e0a34821758000aa59c9d4652485294ee0ab29696b21430fa54dcfbf2b9c49d142064209eeeed4d471ffc017d4e20a796b0927804c061b9f4a7091fe015ada68fd8442e01825c88dfe55cf5de38936f7ce25311b902ba97a3c12a95cfa1c3a591b818f60832709d9e4839e410fb36b84f3ac4f070fc35e161978259e5b8edc744d90919aa770bb36215128e582b59641e23852e958eb8fc3c0aa96152ba4f77f138d6a6712a3ae3226015883f81db23e583c869c4c71143a6fffd65e8dfdc50c99a2f1f314cdcc71359e235f1d7dc2b5f38ef7b970843163c03f9dd40a8015efdc8791956a3f3cedd9ea64f8efa7a0815a70381d71467817bd04ca529aede07ff60d176aed0f855a2eaea89eaeaca89358c081826a0812a5bca28be1373f086dbdba7e43e203212c9fed21474ba19a055ffcc179412e893a744832298c5fe24cc6b18667f49b34dfb12379267419a9cb9403d8167d8d1e91d2811a043b29243b069b37587847dc6fcddb1831bd1cc2567ca033ac40f74ab6955f683b12e4e8254e4ea760d38b3f46791c5c4cb12bc7ccb0ed1865f25d601c303f81fb1fa1db48533d3d6b288e4d9a4dff8ec21c96f578399710c825fe7e32f93a8c0743f9ebd54cc151c761cb6638eba3a1fea6b9a972aa261b137d015861e14c59ec3a6f4c6d8e19e6463fa1e4304f49e43ae065e3fb196f76d9b879c7200862ead18dea5fb6a17acea3338870bde66449891b3eb051da407e7105cda20d73591139e9b2a2c729060c4a9fce5c0b6eacb9362ddcd774a0f2e147c30707a2cb6680a249ea9c7224392cbc0a9d58acdc4ba5623c498c72d7bac4f3013d0938bcda4f45166657e1f0d79d508b177852afd0abb90ade1d682726f42008b46ad7f8abdb18117f72641390f086b6e1498be69548527e6ada2b38b9fe121ef670af7437d32536d5cf5c4ab19dd99771582d038104b7e039a376f7acbbeadb34f945beb9d7ca0e4e3d5c5e4eb1d8526ebd13dacb1ba35735c6d04a4555acf4bf117626500d77b38189dd4888041225acbe3874a4c0f607fe6745f9355b3fa188f1d65c09f389af1b9d6232aa79447919c550f6f31fec35481cb922de2db5b4da2f81948617028e321706a3a778c1938c443bb00e5b0ff06ad8ab9b1ab0c11477673f85df9561dbea45d5f9781ebe317a0710ae5461e34fe6f1b1aa9b4e67b14910984802c2a7e38193bc7bdc8ba3e4e3d1d933bfb580f5b3e87a2a06517051410fe1b4ff1ea0ade824f338515456a57c7a916a74388ee8f1281f9ade0ae2a2613a0612c469df792b8df4cae4fc25c1cadba95a807ce61e5a5303faaf9e14653996b5a8adc34fd475ef1499094babaf1f3f07da9a390b1d9fc9a08327987adfe9564863fbdfa8f6b46a8841583099afb7870118face76347e40b6fd8cd15582ae8e23be9a0219bc3e4e4546a30d3bbbbd1686086876be0e4c859be71fb58f4fab3d28c0b4f7e75ad1edb7f88946fb40cfa5786a0fcba1303c8347ecee93d46d140bb5f69531d666548b109ce764bead7c87bd4c876494de82db6e5073a6c94f7c099a40d7a31c4a04b69c9fccf3c7dd56f5544776c53b4df7953981d55a96a6dcff9904a90842e5bafec8840c2d255bf5ad61c460f98feb82a10fa1c099f62776798236c5ca7f1e46ebdb2b144d8713e56c772f2c3b860ea5b03a8854bc6e6590d63cc0ea54f10b73ba241bf74b635551a2aaca9687ac5269fd368b26d70a737f267685998a3f7d2637914909c746495d24c498635ef97ac66a400894c09f73488eb7cf779237db2ac14e22658be28ae988cec4a671653b575fdea44f7de69cb7e396821e7c661739f9f028a9262bd80ebf9ce8c4a9382c6b03e7d8085e906cf84ca20173fc57be19361a83a7e3775f5b013a9a04b1a506fc59802cfc3cacfb635beb0b76395cba743c36279ba3b4f2c8ba4adb5b8763fb96d7ca333a12de3cefa91c2c96599919a4dfe8408acb99105ee54b79f227b6cb7e337bab04989857fe4f4437ddfabb7b65543b5f39cb2023d46789eb7d989af779e5b8d28385a85b0da2abe07f0c2bb4255fcea03188527a307d409159e90166fac6a070ba05b3e4dbfd3a2bfcc9ee6ed016c0f665be8133b7dc1d86044db0f9db40fb0e9f8bc2e4db5382a8b4f815b4e8434ad0dfbc51a5e9b145e1596cbf4670b7e05dfdafbb0cf3ddee28d76a82428e8aba4364e84bac379298df2932e69bb5d045516efc33ae6cc3947ceb09ed371667212a831b5485eafce8488188ea4e27d0cdf7ddd348abff777f4a13bbc716b6a5944ee727965690e209b49eb962c039975f939ed5c6e4c400d887759433d3ad716da0cb446113c7727a64b58c3f8a0f81189f98005233a81366aee73cec85228ebcfd5ee3c3fb44db76ba243f2842b7b5fc746ae51b0bc4bd4fc9fd833565ea852b92b224f6990318ad8c7d9437e20e2a1f20e818f9057c5abaaa2e5c15b94945cd424c28a5fa385dadfe4907b274d842707db3697a5ae6c8f542e5ecc07fe47350d1014670212efe81fb7c73e8450df814ef6232f7490f63ccf07480f884a66eaffc28fea448d7b401cdae10e7c0c7f9a7b15331969fc8cb36396773de191931c750f6ce5caaf29768ebb27dacc738056a8125b4772bf87ae10a8a309b9bd655043cfc3159494368c5ab8cadb7f671e9626bd263e31181a604b506a03b439a7ffe4355892477e2bdf338c62c3922f7d3c9a56c7103d911948a84b5ae2dbb16a3761add053a0f967e6b5bc94211b6547153267c6ee1cad0d974a71088583735e4f63d33156dadd54c2faf89114a127b97b94cc2a22ef303f459d04fc0b53ace5918d47ff33a558bd71a75f355fbd06bbccf4e02c3c0a4b63d0cc949801d63a64cb2d32373b2c7b274ab2db4682142c8b21d84c481f5ef21e4b5e3603451bf94774d0ef47f63fa6abb78d21c193cbe004d4355231dfd03003da4ea2a4c88bf677cf9754f0c47ef82e20975baaecb0232df880bd7d1de1321549462ec8d5df3e780ffa72eba8a8df7fcf398ec230513ca9d6123f8b9d8178560daf975111955a2bca3423eeefc527be3a8543eb90a5ec02f35a7c64b7dd59a72da0074634e01d2abf3637add77c7350f12b011b2b244613c3f638dfbc036c45541590a6f070534b13f4d4ec0995a082337438d04ea53f492070e201cdbb1dc5bd194d4919f911dfa7241c8d5792d43c457d5de1667c62ec09f08cb58f735e5e35efed184a5001018c10f8cb64b0a7b76814be117fae204a52008dbefcec9fc34c0e5be77fc8b5b5cd077115afde18405054e5d291aa8eb1619c049a60576b87867936b8c9b296888a860348e53c15f7341d8f1acdd0dde230755b4ce860f9f650fe40af6413640b81e4f631c981c11a2e1d18406f1ec93ae097d4c4c159ab6c4e6f527e80a8a7faff91d410de44438db1c7e9d2f17cad242fa9c3179c1a3aa81f7361649572c715c25a1f6cd5ace82c00ab2342b", "03a035938eb02c1b060c7442f4e029a2ea4ed6c5773a5dee3e61d7e4c30e09f2", "c6962319dcdb240d41901812185904eaacda42caf02c1d518d9f5285f9879cb0", 0, "", 1405243945822387, "91f8423fc57709d07552f64dd3ec296edf561ffc359a4acea010bb6a1bbf8384", "1761831c3fab98effcd6eb6e088e4a1d9af8c432e4ee220fb678962019390ac2", "5181ef8175c3dd3af6acbfdb08b566c2fefcdba50a0498dfad39ae0246d89853", "bd5b017be66d21dc40d06f820dce81cd226ea7f7deb6a71a128e0aa5efce9153", "827774bbdc4701eebe2ad342d1af17616c38f64fc401b8cf81001144e03b9fe0", "fb0a1e19636fd2e919cf66d63e0b5b9b26e0f364ccf4aa7dacf630a52c3ef00b"] ] diff --git a/src/version.h b/src/version.h index 4c6fdb6d89f..2ebe88c0398 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 170014; +static const int PROTOCOL_VERSION = 170015; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; From a7342cff71564e694e24aa109a4325cda9241144 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Sep 2021 01:36:35 +0100 Subject: [PATCH 038/514] depends: Postpone native_ccache 4.4.2 --- qa/zcash/postponed-updates.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index 45f0db1bc68..6ec3162f89d 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -12,6 +12,7 @@ native_ccache 4.2.1 2021-10-01 native_ccache 4.3 2021-10-01 native_ccache 4.4 2021-10-01 native_ccache 4.4.1 2021-10-01 +native_ccache 4.4.2 2021-10-01 # Clang is currently pinned to LLVM 12 From 3822c716fda64961e1c2edc6548709364a259ad3 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Sep 2021 01:40:44 +0100 Subject: [PATCH 039/514] make-release.py: Versioning changes for 4.5.1. --- README.md | 2 +- configure.ac | 2 +- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 40c59bf2ece..4fd336db6c3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.5.0 +Zcash 4.5.1 =========== diff --git a/configure.ac b/configure.ac index 3f80dfd829f..3667cb55863 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) define(_CLIENT_VERSION_MINOR, 5) -define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_REVISION, 1) define(_CLIENT_VERSION_BUILD, 50) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index ae9a8ca561f..84960bba576 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.5.0" +name: "zcash-4.5.1" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index 03338b98a94..9cdb0826812 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -17,7 +17,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 4 #define CLIENT_VERSION_MINOR 5 -#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_REVISION 1 #define CLIENT_VERSION_BUILD 50 //! Set to true for release, false for prerelease or test build From 283fd6feda99527ba45bc789c14338b38dc4408e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Sep 2021 01:49:50 +0100 Subject: [PATCH 040/514] make-release.py: Updated manpages for 4.5.1. --- doc/man/zcash-cli.1 | 6 +++--- doc/man/zcash-tx.1 | 6 +++--- doc/man/zcashd.1 | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 4bd9021120b..0fbd58d3af6 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "September 2021" "zcash-cli v4.5.0" "User Commands" +.TH ZCASH-CLI "1" "September 2021" "zcash-cli v4.5.1" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.5.0 +zcash-cli \- manual page for zcash-cli v4.5.1 .SH DESCRIPTION -Zcash RPC client version v4.5.0 +Zcash RPC client version v4.5.1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index fe1099d93a7..0f77ffb0bee 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "September 2021" "zcash-tx v4.5.0" "User Commands" +.TH ZCASH-TX "1" "September 2021" "zcash-tx v4.5.1" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.5.0 +zcash-tx \- manual page for zcash-tx v4.5.1 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.5.0 +Zcash zcash\-tx utility version v4.5.1 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index 94b17e5aa3c..750334f9d27 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "September 2021" "zcashd v4.5.0" "User Commands" +.TH ZCASHD "1" "September 2021" "zcashd v4.5.1" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.5.0 +zcashd \- manual page for zcashd v4.5.1 .SH DESCRIPTION -Zcash Daemon version v4.5.0 +Zcash Daemon version v4.5.1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . From 4bfbdfa4ef91adb122b020cc4bdd96fc0c7b64c9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Sep 2021 01:49:51 +0100 Subject: [PATCH 041/514] make-release.py: Updated release notes and changelog for 4.5.1. --- contrib/debian/changelog | 6 ++ doc/authors.md | 2 +- doc/release-notes.md | 66 ------------------- doc/release-notes/release-notes-4.5.1.md | 80 ++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 67 deletions(-) create mode 100644 doc/release-notes/release-notes-4.5.1.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 4863c22c6ee..6cde32c45d1 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.5.1) stable; urgency=medium + + * 4.5.1 release. + + -- Electric Coin Company Wed, 29 Sep 2021 01:49:51 +0100 + zcash (4.5.0) stable; urgency=medium * 4.5.0 release. diff --git a/doc/authors.md b/doc/authors.md index 0bbe37da462..bac62175b70 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,7 +1,7 @@ Zcash Contributors ================== -Jack Grigg (1112) +Jack Grigg (1117) Simon Liu (460) Sean Bowe (367) Daira Hopwood (270) diff --git a/doc/release-notes.md b/doc/release-notes.md index d376b38c019..a29094b5174 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,69 +4,3 @@ release-notes at release time) Notable changes =============== -Fixed bugs in the testnet Orchard circuit ------------------------------------------ - -In the `zcashd v4.5.0` release notes we indicated that a testnet rollback might -occur to update the consensus rules, if we needed to make backwards-incompatible -changes. Shortly after `zcashd v4.5.0` was released, during another internal -review of the Orchard circuit, we identified two bugs that would affect the -upcoming testnet activation of NU5: - -- The diversifier base `g_d_old`, for the note being spent, is required to be a - non-identity point. A note created from a payment address with `g_d` set to - the identity (via collaboration between sender and recipient) could be spent - multiple times with different nullifiers (corresponding to different `ivk`s). - The code outside the circuit correctly enforced the non-identity requirement, - but the circuit did not correctly constrain this, and allowed the prover to - witness the identity. - -- SinsemillaCommit can be modeled as a Pedersen commitment to an output of - SinsemillaHash: `SinsemillaCommit(r, M) = SinsemillaHashToPoint(M) + [r] R`. - The specification used incomplete addition here, matching its use inside - SinsemillaHash. However, unlike in SinsemillaHash, an exceptional case can be - produced here when `r = 0`. The derivations of `rivk` (for computing `ivk`) - and `rcm` (for computing the note commitment) normally ensure that `r = 0` - can only occur with negligible probability, but these derivations are not - checked by the circuit for efficiency; thus SinsemillaCommit needs to use - complete addition. - -These bugs do not affect mainnet, as `zcashd v4.5.0` only set the activation -height for NU5 on testnet for testing purposes. Nevertheless, in the interest of -keeping the testnet environment as close to mainnet as possible, we are fixing -these bugs immediately. This means a change to the NU5 consensus rules, and a -new testnet activation height for NU5. - -To this end, the following changes are made in `zcashd v4.5.1`: - -- The consensus branch ID for NU5 is changed to `0x37519621`. -- The protocol version indicating NU5-aware testnet nodes is set to `170015`. -- The testnet activation height for NU5 is set to **1,599,200**. - -Testnet nodes that upgrade to `zcashd v4.5.1` prior to block height 1,590,000 -will follow the new testnet network upgrade. Testnet nodes that are running -`zcashd v4.5.0` at that height will need to upgrade to `v4.5.1` and then run -with `-reindex`. - -As always, it is possible that further backwards-incompatible changes might be -made to the NU5 consensus rules in this testing phase, prior to setting the -mainnet activation height, as we continue to conduct additional internal review. -In the event that this happens, testnet will be rolled back in (or prior to) -v5.0.0, and a new testnet activation will occur. - -Fixed regression in `getbalance` RPC method -------------------------------------------- - -`zcashd v4.5.0` removed the account API from the wallet, following its -deprecation and removal in upstream Bitcoin Core. As part of the upstream -changes, the `getbalance` RPC method was altered from using two custom balance -computation methods, to instead relying on `CWallet::GetBalance`. This method -internally relies on `CWalletTx::IsFromMe` as part of identifying "trusted" -zero-confirmation transactions to include in the balance calculation. - -There is an equivalent and closely-named `CWallet::IsFromMe` method, which is -used throughout the wallet, and had been updated before Zcash launched to be -aware of spent shielded notes. The change to `getbalance` exposed a bug: -`CWalletTx::IsFromMe` had not been similarly updated, which caused `getbalance` -to ignore wallet-internal (sent between two addresses in the node's wallet) -unshielding transactions with zero confirmations. This release fixes the bug. diff --git a/doc/release-notes/release-notes-4.5.1.md b/doc/release-notes/release-notes-4.5.1.md new file mode 100644 index 00000000000..c0229a5e75b --- /dev/null +++ b/doc/release-notes/release-notes-4.5.1.md @@ -0,0 +1,80 @@ +Notable changes +=============== + +Fixed bugs in the testnet Orchard circuit +----------------------------------------- + +In the `zcashd v4.5.0` release notes we indicated that a testnet rollback might +occur to update the consensus rules, if we needed to make backwards-incompatible +changes. Shortly after `zcashd v4.5.0` was released, during another internal +review of the Orchard circuit, we identified two bugs that would affect the +upcoming testnet activation of NU5: + +- The diversifier base `g_d_old`, for the note being spent, is required to be a + non-identity point. A note created from a payment address with `g_d` set to + the identity (via collaboration between sender and recipient) could be spent + multiple times with different nullifiers (corresponding to different `ivk`s). + The code outside the circuit correctly enforced the non-identity requirement, + but the circuit did not correctly constrain this, and allowed the prover to + witness the identity. + +- SinsemillaCommit can be modeled as a Pedersen commitment to an output of + SinsemillaHash: `SinsemillaCommit(r, M) = SinsemillaHashToPoint(M) + [r] R`. + The specification used incomplete addition here, matching its use inside + SinsemillaHash. However, unlike in SinsemillaHash, an exceptional case can be + produced here when `r = 0`. The derivations of `rivk` (for computing `ivk`) + and `rcm` (for computing the note commitment) normally ensure that `r = 0` + can only occur with negligible probability, but these derivations are not + checked by the circuit for efficiency; thus SinsemillaCommit needs to use + complete addition. + +These bugs do not affect mainnet, as `zcashd v4.5.0` only set the activation +height for NU5 on testnet for testing purposes. Nevertheless, in the interest of +keeping the testnet environment as close to mainnet as possible, we are fixing +these bugs immediately. This means a change to the NU5 consensus rules, and a +new testnet activation height for NU5. + +To this end, the following changes are made in `zcashd v4.5.1`: + +- The consensus branch ID for NU5 is changed to `0x37519621`. +- The protocol version indicating NU5-aware testnet nodes is set to `170015`. +- The testnet activation height for NU5 is set to **1,599,200**. + +Testnet nodes that upgrade to `zcashd v4.5.1` prior to block height 1,590,000 +will follow the new testnet network upgrade. Testnet nodes that are running +`zcashd v4.5.0` at that height will need to upgrade to `v4.5.1` and then run +with `-reindex`. + +As always, it is possible that further backwards-incompatible changes might be +made to the NU5 consensus rules in this testing phase, prior to setting the +mainnet activation height, as we continue to conduct additional internal review. +In the event that this happens, testnet will be rolled back in (or prior to) +v5.0.0, and a new testnet activation will occur. + +Fixed regression in `getbalance` RPC method +------------------------------------------- + +`zcashd v4.5.0` removed the account API from the wallet, following its +deprecation and removal in upstream Bitcoin Core. As part of the upstream +changes, the `getbalance` RPC method was altered from using two custom balance +computation methods, to instead relying on `CWallet::GetBalance`. This method +internally relies on `CWalletTx::IsFromMe` as part of identifying "trusted" +zero-confirmation transactions to include in the balance calculation. + +There is an equivalent and closely-named `CWallet::IsFromMe` method, which is +used throughout the wallet, and had been updated before Zcash launched to be +aware of spent shielded notes. The change to `getbalance` exposed a bug: +`CWalletTx::IsFromMe` had not been similarly updated, which caused `getbalance` +to ignore wallet-internal (sent between two addresses in the node's wallet) +unshielding transactions with zero confirmations. This release fixes the bug. + +Changelog +========= + +Jack Grigg (5): + wallet: Check spent shielded notes in CWalletTx::IsFromMe + Fix bugs in testnet Orchard circuit + depends: Postpone native_ccache 4.4.2 + make-release.py: Versioning changes for 4.5.1. + make-release.py: Updated manpages for 4.5.1. + From a672318b4d9341a2ac3938f2327a3f218ad88ae8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Sep 2021 13:34:18 +0100 Subject: [PATCH 042/514] contrib: Update Debian copyright file to follow the v1 format https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ --- contrib/debian/copyright | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index e1970d6eedb..e7302ff4c42 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -71,18 +71,18 @@ Files: build-aux/m4/ax_check_compile_flag.m4 build-aux/m4/ax_check_preproc_flag.m4 Copyright: 2008, Guido U. Draheim 2011, Maarten Bosmans -License: GPLv3-or-later-with-Autoconf-exception +License: GPL-3+ with Autoconf exception Files: build-aux/m4/ax_pthread.m4 Copyright: 2008, Steven G. Johnson 2011, Daniel Richard G. -License: GPLv3-or-later-with-Autoconf-exception +License: GPL-3+ with Autoconf exception Files: qa/zcash/checksec.sh Copyright: 2014-2015, Brian Davis 2013, Robin David 2009-2011, Tobias Klein -License: BSD-3clause-Tobias-Klein +License: BSD-3-clause-Tobias-Klein Files: depends/sources/libsodium-*.tar.gz Copyright: 2013-2016 Frank Denis @@ -110,11 +110,11 @@ Copyright: 2013 Ericsson AB 2014 AppDynamics Inc. 2015-2016 Brocade Communications Systems Inc. -License: LGPL-with-ZeroMQ-exception +License: LGPL-3+ with ZeroMQ exception Files: depends/sources/google*.tar.gz Copyright: 2008 Google Inc. -License: BSD-3clause-Google +License: BSD-3-clause-Google Files: depends/sources/utfcpp-*.tar.gz Copyright: 2006 Nemanja Trifunovic @@ -123,7 +123,7 @@ License: Boost-Software-License-1.0 Files: depends/*/vendored-sources/halo2/* depends/*/vendored-sources/orchard/* Copyright: 2020-2021 The Electric Coin Company -License: BOSL-1.0-or-later-with-Zcash-exception +License: BOSL-1+ with Zcash exception Files: src/crypto/ctaes/* Copyright: Copyright (c) 2016 Pieter Wuille @@ -157,7 +157,7 @@ License: GNU-All-permissive-License Files src/leveldb/* Copyright: 2011, The LevelDB Authors -License: BSD-3clause-Google +License: BSD-3-clause-Google Comment: The LevelDB Authors are listed in src/leveldb/AUTHORS. License: Boost-Software-License-1.0 @@ -942,7 +942,7 @@ License: BDB * THE POSSIBILITY OF SUCH DAMAGE. */ -License: BSD-3clause +License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . @@ -986,7 +986,7 @@ License: Expat TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -License: BSD-3clause-Google +License: BSD-3-clause-Google Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -1013,7 +1013,7 @@ License: BSD-3clause-Google (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -License: BSD-3clause-Tobias-Klein +License: BSD-3-clause-Tobias-Klein Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . @@ -1067,7 +1067,7 @@ Comment: You should have received a copy of the GNU General Public License along with this program. If not, see . -License: LGPL-with-ZeroMQ-exception +License: LGPL-3+ with ZeroMQ exception GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 . @@ -1102,7 +1102,7 @@ License: GNU-All-permissive-License and this notice are preserved. This file is offered as-is, without any warranty. -License: GPLv3-or-later-with-Autoconf-exception +License: GPL-3+ with Autoconf exception This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your @@ -1116,6 +1116,9 @@ License: GPLv3-or-later-with-Autoconf-exception You should have received a copy of the GNU General Public License along with this program. If not, see . . + On Debian systems, the full text of the GNU General Public License + version 3 can be found in the file `/usr/share/common-licenses/GPL-3'. + . As a special exception, the respective Autoconf Macro's copyright owner gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf when processing the Macro. You @@ -1157,7 +1160,7 @@ License: Expat-with-advertising-clause promote the sale, use or other dealings in this Software without prior written authorization from the authors. -License: BOSL-1.0-or-later-with-Zcash-exception +License: BOSL-1+ with Zcash exception This package ("Original Work") is licensed under the terms of the Bootstrap Open Source License, version 1.0, or at your option, any later version ("BOSL"). See the file ./LICENSE-BOSL for the terms of the Bootstrap Open Source Licence, @@ -1192,7 +1195,7 @@ License: BOSL-1.0-or-later-with-Zcash-exception to your version, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. -License: Bootstrap-Open-Source-Licence-1.0 +License: BOSL-1 ======================================================= Bootstrap Open Source Licence ("BOSL") v. 1.0 ======================================================= From f5740473876c95cf844f06ee93c764f9f829d188 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Sep 2021 13:42:56 +0100 Subject: [PATCH 043/514] contrib: Add license information for libc++ and libevent Closes zcash/zcash#5302. --- contrib/debian/copyright | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index e7302ff4c42..7164656be9f 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -92,6 +92,12 @@ Files: depends/sources/boost_*.tar.gz Copyright: 2008 Beman Dawes License: Boost-Software-License-1.0 +Files: depends/sources/clang-llvm-*.tar.xz +Copyright: The libc++ Authors +License: Apache-2 with LLVM exception +Comment: This entry is specifically for the libc++ library. The libc++ Authors + are listed in https://github.com/llvm/llvm-project/blob/main/libcxx/CREDITS.TXT. + Files: depends/sources/db-*.tar.gz Copyright: 1990, 2016 Oracle and/or its affiliates; 1990, 1993, 1994, 1995 The Regents of the University of California; @@ -99,6 +105,18 @@ Copyright: 1990, 2016 Oracle and/or its affiliates; 2000-2005 INRIA, France Telecom License: BDB +Files: depends/sources/libevent-*.tar.gz +Copyright: 2000-2007 Niels Provos + 2007-2012 Niels Provos and Nick Mathewson + 2000 Dug Song + 1993 The Regents of the University of California. + 1998 Todd C. Miller + 2003 Michael A. Davis + 2007 Sun Microsystems + 2002 Christopher Clark + 2006 Maxim Yegorushkin +License: BSD-3-clause + Files: depends/sources/zeromq-*.tar.gz Copyright: 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. @@ -160,6 +178,30 @@ Copyright: 2011, The LevelDB Authors License: BSD-3-clause-Google Comment: The LevelDB Authors are listed in src/leveldb/AUTHORS. +License: Apache-2 with LLVM exception + ============================================================================== + The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: + ============================================================================== + . + On Debian systems, the full text of the Apache License v2.0 can be found in the + file `/usr/share/common-licenses/Apache-2.0`. + . + ---- LLVM Exceptions to the Apache 2.0 License ---- + . + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into an Object form of such source code, you + may redistribute such embedded portions in such Object form without complying + with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + . + In addition, if you combine or link compiled forms of this Software with + software that is licensed under the GPLv2 ("Combined Software") and if a + court of competent jurisdiction determines that the patent provision (Section + 3), the indemnity provision (Section 9) or other Section of the License + conflicts with the conditions of the GPLv2, you may retroactively and + prospectively choose to deem waived or otherwise exclude such Section(s) of + the License, but only in their entirety and only with respect to the Combined + Software. + License: Boost-Software-License-1.0 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by From 68c3bd8eaa14c11e0ad2a33701e8935f301d92e6 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 21 Sep 2021 13:23:47 -0600 Subject: [PATCH 044/514] Add BIP 44 coin type to persisted wallet state. --- src/init.cpp | 4 +- src/wallet/gtest/test_wallet.cpp | 61 ++++++++++---------- src/wallet/gtest/test_wallet_zkeys.cpp | 34 +++++------ src/wallet/test/wallet_test_fixture.cpp | 2 +- src/wallet/test/wallet_tests.cpp | 77 ++++++++++++------------- src/wallet/wallet.cpp | 27 ++++++--- src/wallet/wallet.h | 20 ++++--- src/wallet/walletdb.cpp | 13 ++++- src/wallet/walletdb.h | 1 + src/zcbenchmarks.cpp | 10 ++-- 10 files changed, 138 insertions(+), 111 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index cd93d290f43..ab58e98aac1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1077,7 +1077,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #ifdef ENABLE_WALLET - if (!CWallet::ParameterInteraction()) + if (!CWallet::ParameterInteraction(chainparams)) return false; #endif // ENABLE_WALLET @@ -1633,7 +1633,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); } else { - CWallet::InitLoadWallet(clearWitnessCaches); + CWallet::InitLoadWallet(chainparams, clearWitnessCaches); if (!pwalletMain) return false; } diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index d4a1249cd8f..af1ef1bea51 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -41,7 +41,7 @@ template void CWallet::SetBestChainINTERNAL( class TestWallet : public CWallet { public: - TestWallet() : CWallet() { } + TestWallet(const CChainParams& params) : CWallet(params) { } bool EncryptKeys(CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::EncryptKeys(vMasterKeyIn); @@ -154,7 +154,7 @@ TEST(WalletTests, SproutNoteDataSerialisation) { TEST(WalletTests, FindUnspentSproutNotes) { auto consensusParams = RegtestActivateSapling(); - CWallet wallet; + CWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); wallet.AddSproutSpendingKey(sk); @@ -355,7 +355,7 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) { for (int ver = 0; ver < zip_212_enabled.size(); ver++) { auto consensusParams = (*activations[ver])(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = GetTestMasterSaplingSpendingKey(); @@ -437,7 +437,7 @@ TEST(WalletTests, SetInvalidSaplingNoteDataInCWalletTx) { } TEST(WalletTests, CheckSproutNoteCommitmentAgainstNotePlaintext) { - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -461,7 +461,7 @@ TEST(WalletTests, CheckSproutNoteCommitmentAgainstNotePlaintext) { } TEST(WalletTests, GetSproutNoteNullifier) { - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -497,7 +497,7 @@ TEST(WalletTests, GetSproutNoteNullifier) { TEST(WalletTests, FindMySaplingNotes) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); // Generate dummy Sapling address @@ -531,7 +531,7 @@ TEST(WalletTests, FindMySaplingNotes) { } TEST(WalletTests, FindMySproutNotes) { - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -557,7 +557,7 @@ TEST(WalletTests, FindMySproutNotes) { } TEST(WalletTests, FindMySproutNotesInEncryptedWallet) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); uint256 r {GetRandHash()}; CKeyingMaterial vMasterKey (r.begin(), r.end()); @@ -588,7 +588,7 @@ TEST(WalletTests, FindMySproutNotesInEncryptedWallet) { } TEST(WalletTests, GetConflictedSproutNotes) { - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -630,7 +630,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) { for (int ver = 0; ver < zip_212_enabled.size(); ver++) { auto consensusParams = (*activations[ver])(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); // Generate Sapling address @@ -748,7 +748,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) { } TEST(WalletTests, SproutNullifierIsSpent) { - CWallet wallet; + CWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -791,7 +791,7 @@ TEST(WalletTests, SproutNullifierIsSpent) { TEST(WalletTests, SaplingNullifierIsSpent) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); // Generate dummy Sapling address @@ -847,7 +847,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) { } TEST(WalletTests, NavigateFromSproutNullifierToNote) { - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -876,7 +876,7 @@ TEST(WalletTests, NavigateFromSproutNullifierToNote) { TEST(WalletTests, NavigateFromSaplingNullifierToNote) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); // Generate dummy Sapling address @@ -967,7 +967,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) { } TEST(WalletTests, SpentSproutNoteIsFromMe) { - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -1006,7 +1006,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { for (int ver = 0; ver < zip_212_enabled.size(); ver++) { auto consensusParams = (*activations[ver])(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); // Generate Sapling address @@ -1147,7 +1147,8 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { } TEST(WalletTests, CachedWitnessesEmptyChain) { - TestWallet wallet; + SelectParams(CBaseChainParams::REGTEST); + TestWallet wallet(Params()); auto sk = libzcash::SproutSpendingKey::random(); wallet.AddSproutSpendingKey(sk); @@ -1206,7 +1207,7 @@ TEST(WalletTests, CachedWitnessesEmptyChain) { } TEST(WalletTests, CachedWitnessesChainTip) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); std::pair anchors1; CBlock block1; @@ -1309,7 +1310,7 @@ TEST(WalletTests, CachedWitnessesChainTip) { } TEST(WalletTests, CachedWitnessesDecrementFirst) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; @@ -1390,7 +1391,7 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) { } TEST(WalletTests, CachedWitnessesCleanIndex) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); std::vector blocks; std::vector indices; @@ -1478,7 +1479,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) { } TEST(WalletTests, ClearNoteWitnessCache) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -1545,7 +1546,7 @@ TEST(WalletTests, ClearNoteWitnessCache) { } TEST(WalletTests, WriteWitnessCache) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); MockWalletDB walletdb; CBlockLocator loc; @@ -1633,7 +1634,7 @@ TEST(WalletTests, WriteWitnessCache) { TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) { SelectParams(CBaseChainParams::REGTEST); - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); MockWalletDB walletdb; CBlockLocator loc; @@ -1714,7 +1715,7 @@ TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) { } TEST(WalletTests, UpdateSproutNullifierNoteMap) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); uint256 r {GetRandHash()}; CKeyingMaterial vMasterKey (r.begin(), r.end()); @@ -1750,7 +1751,7 @@ TEST(WalletTests, UpdateSproutNullifierNoteMap) { } TEST(WalletTests, UpdatedSproutNoteData) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -1800,7 +1801,7 @@ TEST(WalletTests, UpdatedSproutNoteData) { TEST(WalletTests, UpdatedSaplingNoteData) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); auto m = GetTestMasterSaplingSpendingKey(); @@ -1909,7 +1910,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) { } TEST(WalletTests, MarkAffectedSproutTransactionsDirty) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -1943,7 +1944,7 @@ TEST(WalletTests, MarkAffectedSproutTransactionsDirty) { TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet; + TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); // Generate Sapling address @@ -2052,7 +2053,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { } TEST(WalletTests, SproutNoteLocking) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); auto sk = libzcash::SproutSpendingKey::random(); @@ -2086,7 +2087,7 @@ TEST(WalletTests, SproutNoteLocking) { } TEST(WalletTests, SaplingNoteLocking) { - TestWallet wallet; + TestWallet wallet(Params()); LOCK(wallet.cs_wallet); SaplingOutPoint sop1 {uint256(), 1}; SaplingOutPoint sop2 {uint256(), 2}; diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 92b26574bf0..4f59ab35016 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -20,7 +20,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { SelectParams(CBaseChainParams::MAIN); - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); // wallet should be empty @@ -42,10 +42,10 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // wallet should have one key wallet.GetSaplingPaymentAddresses(addrs); ASSERT_EQ(1, addrs.size()); - + // verify wallet has incoming viewing key for the address ASSERT_TRUE(wallet.HaveSaplingIncomingViewingKey(address)); - + // manually add new spending key to wallet auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); auto sk = m.Derive(0); @@ -115,7 +115,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { TEST(WalletZkeysTest, StoreAndLoadZkeys) { SelectParams(CBaseChainParams::MAIN); - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); // wallet should be empty @@ -173,7 +173,7 @@ TEST(WalletZkeysTest, StoreAndLoadZkeys) { TEST(WalletZkeysTest, StoreAndLoadViewingKeys) { SelectParams(CBaseChainParams::MAIN); - CWallet wallet; + CWallet wallet(Params()); LOCK(wallet.cs_wallet); // wallet should be empty @@ -226,7 +226,7 @@ TEST(WalletZkeysTest, WriteZkeyDirectToDb) { mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; - CWallet wallet("wallet.dat"); + CWallet wallet(Params(), "wallet.dat"); LOCK(wallet.cs_wallet); ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); @@ -299,7 +299,7 @@ TEST(WalletZkeysTest, WriteViewingKeyDirectToDB) { mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; - CWallet wallet("wallet-vkey.dat"); + CWallet wallet(Params(), "wallet-vkey.dat"); LOCK(wallet.cs_wallet); ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); @@ -346,7 +346,7 @@ TEST(WalletZkeysTest, WriteCryptedzkeyDirectToDb) { mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; - CWallet wallet("wallet_crypted.dat"); + CWallet wallet(Params(), "wallet_crypted.dat"); LOCK(wallet.cs_wallet); ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); @@ -370,25 +370,25 @@ TEST(WalletZkeysTest, WriteCryptedzkeyDirectToDb) { strWalletPass.reserve(100); strWalletPass = "hello"; ASSERT_TRUE(wallet.EncryptWallet(strWalletPass)); - + // adding a new key will fail as the wallet is locked EXPECT_ANY_THROW(wallet.GenerateNewSproutZKey()); - + // unlock wallet and then add wallet.Unlock(strWalletPass); auto paymentAddress2 = wallet.GenerateNewSproutZKey(); // Create a new wallet from the existing wallet path - CWallet wallet2("wallet_crypted.dat"); + CWallet wallet2(Params(), "wallet_crypted.dat"); ASSERT_EQ(DB_LOAD_OK, wallet2.LoadWallet(fFirstRun)); // Confirm it's not the same as the other wallet ASSERT_TRUE(&wallet != &wallet2); - + // wallet should have two keys wallet2.GetSproutPaymentAddresses(addrs); ASSERT_EQ(2, addrs.size()); - + // check we have entries for our payment addresses ASSERT_TRUE(addrs.count(paymentAddress)); ASSERT_TRUE(addrs.count(paymentAddress2)); @@ -397,13 +397,13 @@ TEST(WalletZkeysTest, WriteCryptedzkeyDirectToDb) { libzcash::SproutSpendingKey keyOut; wallet2.GetSproutSpendingKey(paymentAddress, keyOut); ASSERT_FALSE(paymentAddress == keyOut.address()); - + // unlock wallet to get spending keys and verify payment addresses wallet2.Unlock(strWalletPass); wallet2.GetSproutSpendingKey(paymentAddress, keyOut); ASSERT_EQ(paymentAddress, keyOut.address()); - + wallet2.GetSproutSpendingKey(paymentAddress2, keyOut); ASSERT_EQ(paymentAddress2, keyOut.address()); } @@ -421,7 +421,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; - CWallet wallet("wallet_crypted_sapling.dat"); + CWallet wallet(Params(), "wallet_crypted_sapling.dat"); LOCK(wallet.cs_wallet); ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); @@ -472,7 +472,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { wallet.Flush(); // Create a new wallet from the existing wallet path - CWallet wallet2("wallet_crypted_sapling.dat"); + CWallet wallet2(Params(), "wallet_crypted_sapling.dat"); ASSERT_EQ(DB_LOAD_OK, wallet2.LoadWallet(fFirstRun)); // Confirm it's not the same as the other wallet diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 31e790988cd..806bcec9354 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -9,7 +9,7 @@ WalletTestingSetup::WalletTestingSetup(): TestingSetup() bitdb.MakeMock(); bool fFirstRun; - pwalletMain = new CWallet("wallet_test.dat"); + pwalletMain = new CWallet(Params(), "wallet_test.dat"); pwalletMain->LoadWallet(fFirstRun); RegisterValidationInterface(pwalletMain); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 7693aab01a1..09a7aadb76b 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -26,7 +26,6 @@ typedef set > CoinSet; BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) -static CWallet wallet; static vector vCoins; static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) @@ -41,7 +40,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - CWalletTx* wtx = new CWalletTx(&wallet, tx); + CWalletTx* wtx = new CWalletTx(pwalletMain, tx); if (fIsFromMe) { wtx->fDebitCached = true; @@ -69,32 +68,30 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) CoinSet setCoinsRet, setCoinsRet2; CAmount nValueRet; - LOCK(wallet.cs_wallet); - // test multiple times to allow for differences in the shuffle order for (int i = 0; i < RUN_TESTS; i++) { empty_wallet(); // with an empty wallet we can't even pay one cent - BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!CWallet::SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); add_coin(1*CENT, 4); // add a new 1 cent coin // with a new 1 cent coin, we still can't find a mature 1 cent - BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!CWallet::SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); // but we can find a new 1 cent - BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); add_coin(2*CENT); // add a mature 2 cent coin // we can't make 3 cents of mature coins - BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!CWallet::SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); // we can make 3 cents of new coins - BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 3 * CENT); add_coin(5*CENT); // add a mature 5 cent coin, @@ -104,33 +101,33 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38 // we can't make 38 cents only if we disallow new coins: - BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!CWallet::SelectCoinsMinConf(38 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); // we can't even make 37 cents if we don't allow new coins even if they're from us - BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!CWallet::SelectCoinsMinConf(38 * CENT, 6, 6, vCoins, setCoinsRet, nValueRet)); // but we can make 37 cents if we accept new coins from ourself - BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(37 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 37 * CENT); // and we can make 38 cents if we accept all new coins - BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(38 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 38 * CENT); // try making 34 cents from 1,2,5,10,20 - we can't do it exactly - BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible) // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5 - BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 7 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough. - BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK(nValueRet == 8 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10) - BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 10 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -144,30 +141,30 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total // check that we have 71 and not 72 - BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!CWallet::SelectCoinsMinConf(72 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 - BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 - BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 - BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins // now try making 11 cents. we should get 5+6 - BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -176,11 +173,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin( 2*COIN); add_coin( 3*COIN); add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents - BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); - BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -195,14 +192,14 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly - BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // but if we add a bigger coin, small change is avoided add_coin(1111*MIN_CHANGE); // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 - BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // if we add more small coins: @@ -210,7 +207,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(0.7*MIN_CHANGE); // and try again to make 1.0 * MIN_CHANGE - BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // run the 'mtgox' test (see https://blockchair.com/bitcoin/transaction/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf) @@ -219,7 +216,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) for (int i = 0; i < 20; i++) add_coin(50000 * COIN); - BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins @@ -232,7 +229,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(0.6 * MIN_CHANGE); add_coin(0.7 * MIN_CHANGE); add_coin(1111 * MIN_CHANGE); - BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -242,7 +239,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(0.6 * MIN_CHANGE); add_coin(0.8 * MIN_CHANGE); add_coin(1111 * MIN_CHANGE); - BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6 @@ -253,12 +250,12 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(100 * MIN_CHANGE); // trying to make 100.01 from these three coins - BOOST_CHECK( wallet.SelectCoinsMinConf(100.01 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(100.01 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 101.05 * MIN_CHANGE); // we should get all coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change - BOOST_CHECK( wallet.SelectCoinsMinConf(99.9 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( CWallet::SelectCoinsMinConf(99.9 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -268,7 +265,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // Create 676 inputs (= MAX_STANDARD_TX_SIZE / 148 bytes per input) for (uint16_t j = 0; j < 676; j++) add_coin(amt); - BOOST_CHECK(wallet.SelectCoinsMinConf(2000, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(2000, 1, 1, vCoins, setCoinsRet, nValueRet)); if (amt - 2000 < MIN_CHANGE) { // needs more than one input: uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt); @@ -290,8 +287,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // picking 50 from 100 coins doesn't depend on the shuffle, // but does depend on randomness in the stochastic approximation code - BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet , nValueRet)); - BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet2, nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet , nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet2, nValueRet)); BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2)); int fails = 0; @@ -299,8 +296,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) { // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time // run the test RANDOM_REPEATS times and only complain if all of them fail - BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet , nValueRet)); - BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet2, nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet , nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet2, nValueRet)); if (equal_sets(setCoinsRet, setCoinsRet2)) fails++; } @@ -316,8 +313,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) { // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time // run the test RANDOM_REPEATS times and only complain if all of them fail - BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet , nValueRet)); - BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet2, nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet , nValueRet)); + BOOST_CHECK(CWallet::SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet2, nValueRet)); if (equal_sets(setCoinsRet, setCoinsRet2)) fails++; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3d6fb1fdd0b..2e98c2f6109 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -125,7 +125,6 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): HD seed not found"); auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); - uint32_t bip44CoinType = Params().BIP44CoinType(); // We use a fixed keypath scheme of m/32'/coin_type'/account' // Derive m/32' @@ -2251,6 +2250,7 @@ bool CWallet::SetHDSeed(const HDSeed& seed) { LOCK(cs_wallet); + CWalletDB(strWalletFile).WriteBIP44CoinType(bip44CoinType); if (!IsCrypted()) { return CWalletDB(strWalletFile).WriteHDSeed(seed); } @@ -2295,6 +2295,19 @@ void CWallet::SetHDChain(const CHDChain& chain, bool memonly) hdChain = chain; } +void CWallet::CheckBIP44CoinType(uint32_t coinType) +{ + LOCK(cs_wallet); + if (bip44CoinType != coinType) + throw std::runtime_error( + strprintf("%s: this wallet is for a different BIP 44 coin type (%i) than the node is configured for (%i)", + std::string(__func__), + coinType, + bip44CoinType) + ); +} + + bool CWallet::LoadHDSeed(const HDSeed& seed) { return CBasicKeyStore::SetHDSeed(seed); @@ -3347,7 +3360,7 @@ static void ApproximateBestSubset(vector vCoins, - set >& setCoinsRet, CAmount& nValueRet) const + set >& setCoinsRet, CAmount& nValueRet) { setCoinsRet.clear(); nValueRet = 0; @@ -4712,7 +4725,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) return strUsage; } -bool CWallet::InitLoadWallet(bool clearWitnessCaches) +bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches) { std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); @@ -4722,7 +4735,7 @@ bool CWallet::InitLoadWallet(bool clearWitnessCaches) if (GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); - CWallet *tempWallet = new CWallet(walletFile); + CWallet *tempWallet = new CWallet(params, walletFile); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { return UIError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); @@ -4736,7 +4749,7 @@ bool CWallet::InitLoadWallet(bool clearWitnessCaches) int64_t nStart = GetTimeMillis(); bool fFirstRun = true; - CWallet *walletInstance = new CWallet(walletFile); + CWallet *walletInstance = new CWallet(params, walletFile); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); if (nLoadWalletRet != DB_LOAD_OK) { @@ -4869,7 +4882,7 @@ bool CWallet::InitLoadWallet(bool clearWitnessCaches) return true; } -bool CWallet::ParameterInteraction() +bool CWallet::ParameterInteraction(const CChainParams& params) { if (mapArgs.count("-mintxfee")) { @@ -4919,7 +4932,7 @@ bool CWallet::ParameterInteraction() bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); - KeyIO keyIO(Params()); + KeyIO keyIO(params); // Check Sapling migration address if set and is a valid Sapling address if (mapArgs.count("-migrationdestaddress")) { std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 423daf99f7b..3641970ece1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -806,6 +806,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* the hd chain data model (chain counters) */ CHDChain hdChain; + uint32_t bip44CoinType; public: /* @@ -829,14 +830,14 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; - CWallet() + CWallet(const CChainParams& params) { - SetNull(); + SetNull(params); } - CWallet(const std::string& strWalletFileIn) + CWallet(const CChainParams& params, const std::string& strWalletFileIn) { - SetNull(); + SetNull(params); strWalletFile = strWalletFileIn; fFileBacked = true; @@ -848,7 +849,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface pwalletdbEncryption = NULL; } - void SetNull() + void SetNull(const CChainParams& params) { nWalletVersion = FEATURE_BASE; nWalletMaxVersion = FEATURE_BASE; @@ -863,6 +864,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nTimeFirstKey = 0; fBroadcastTransactions = false; nWitnessCacheSize = 0; + bip44CoinType = params.BIP44CoinType(); } /** @@ -958,7 +960,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * completion the coin set and corresponding actual target value is * assembled */ - bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; + static bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet); bool IsSpent(const uint256& hash, unsigned int n) const; bool IsSproutSpent(const uint256& nullifier) const; @@ -1296,6 +1298,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SetHDChain(const CHDChain& chain, bool memonly); const CHDChain& GetHDChain() const { return hdChain; } + void CheckBIP44CoinType(uint32_t coinType); + /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ bool LoadHDSeed(const HDSeed& key); /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ @@ -1324,10 +1328,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface static std::string GetWalletHelpString(bool showDebug); /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ - static bool InitLoadWallet(bool clearWitnessCaches); + static bool InitLoadWallet(const CChainParams& params, bool clearWitnessCaches); /* Wallets parameter interaction */ - static bool ParameterInteraction(); + static bool ParameterInteraction(const CChainParams& params); }; /** A key allocated from the key pool. */ diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index b3d74b0acf9..f07eae4c199 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -724,6 +724,12 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> chain; pwallet->SetHDChain(chain, true); } + else if (strType == "bip44cointype") + { + uint32_t bip44CoinType; + ssValue >> bip44CoinType; + pwallet->CheckBIP44CoinType(bip44CoinType); + } } catch (...) { return false; @@ -1087,7 +1093,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe LogPrintf("Cannot create database file %s\n", filename); return false; } - CWallet dummyWallet; + CWallet dummyWallet(Params()); CWalletScanState wss; DbTxn* ptxn = dbenv.TxnBegin(); @@ -1142,6 +1148,11 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } +bool CWalletDB::WriteBIP44CoinType(uint32_t bip44CoinType) +{ + nWalletDBUpdateCounter++; + return Write(std::string("bip44cointype"), bip44CoinType); +} bool CWalletDB::WriteHDSeed(const HDSeed& seed) { diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 6c03c14edb3..825297cbdaf 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -168,6 +168,7 @@ class CWalletDB : public CDB static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); + bool WriteBIP44CoinType(uint32_t bip44CoinType); bool WriteHDSeed(const HDSeed& seed); bool WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); //! write the hdchain model (external chain child index counter) diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index ba23c666699..7a864af7e59 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -271,7 +271,7 @@ double benchmark_large_tx(size_t nInputs) // wallet. We call assert(...) to ensure that this is true. double benchmark_try_decrypt_sprout_notes(size_t nKeys) { - CWallet wallet; + CWallet wallet(Params()); for (int i = 0; i < nKeys; i++) { auto sk = libzcash::SproutSpendingKey::random(); wallet.AddSproutSpendingKey(sk); @@ -295,7 +295,7 @@ double benchmark_try_decrypt_sapling_notes(size_t nKeys) auto masterKey = GetTestMasterSaplingSpendingKey(); - CWallet wallet; + CWallet wallet(Params()); for (int i = 0; i < nKeys; i++) { auto sk = masterKey.Derive(i); @@ -332,7 +332,7 @@ double benchmark_increment_sprout_note_witnesses(size_t nTxs) { const Consensus::Params& consensusParams = Params().GetConsensus(); - CWallet wallet; + CWallet wallet(Params()); SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; @@ -394,7 +394,7 @@ double benchmark_increment_sapling_note_witnesses(size_t nTxs) { const Consensus::Params& consensusParams = Params().GetConsensus(); - CWallet wallet; + CWallet wallet(Params()); SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; @@ -587,7 +587,7 @@ double benchmark_loadwallet() struct timeval tv_start; bool fFirstRunRet=true; timer_start(tv_start); - pwalletMain = new CWallet("wallet.dat"); + pwalletMain = new CWallet(Params(), "wallet.dat"); DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRunRet); auto res = timer_stop(tv_start); post_wallet_load(); From 7132b27723602c202254f3d82f96e082365b3007 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 21 Sep 2021 18:24:39 -0600 Subject: [PATCH 045/514] Persist network id string instead of bip44 coin type. --- src/wallet/wallet.cpp | 21 +++++++++++++-------- src/wallet/wallet.h | 7 ++++--- src/wallet/walletdb.cpp | 13 +++++++------ src/wallet/walletdb.h | 2 +- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2e98c2f6109..7d26262c43f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -130,14 +130,14 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() // Derive m/32' auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h_cth = m_32h.Derive(BIP44CoinType() | ZIP32_HARDENED_KEY_LIMIT); // Derive account key at next index, skip keys already known to the wallet libzcash::SaplingExtendedSpendingKey xsk; do { xsk = m_32h_cth.Derive(hdChain.saplingAccountCounter | ZIP32_HARDENED_KEY_LIMIT); - metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(hdChain.saplingAccountCounter) + "'"; + metadata.hdKeypath = "m/32'/" + std::to_string(BIP44CoinType()) + "'/" + std::to_string(hdChain.saplingAccountCounter) + "'"; metadata.seedFp = hdChain.seedFp; // Increment childkey index hdChain.saplingAccountCounter++; @@ -2250,7 +2250,7 @@ bool CWallet::SetHDSeed(const HDSeed& seed) { LOCK(cs_wallet); - CWalletDB(strWalletFile).WriteBIP44CoinType(bip44CoinType); + CWalletDB(strWalletFile).WriteNetworkInfo(networkIdString); if (!IsCrypted()) { return CWalletDB(strWalletFile).WriteHDSeed(seed); } @@ -2295,18 +2295,23 @@ void CWallet::SetHDChain(const CHDChain& chain, bool memonly) hdChain = chain; } -void CWallet::CheckBIP44CoinType(uint32_t coinType) +void CWallet::CheckNetworkInfo(std::pair readNetworkInfo) { LOCK(cs_wallet); - if (bip44CoinType != coinType) + std::pair networkInfo(PACKAGE_NAME, networkIdString); + if (readNetworkInfo != networkInfo) throw std::runtime_error( - strprintf("%s: this wallet is for a different BIP 44 coin type (%i) than the node is configured for (%i)", + strprintf("%s: this wallet is for a different network (%s, %s) than the node is configured for (%s, %s)", std::string(__func__), - coinType, - bip44CoinType) + readNetworkInfo.first, readNetworkInfo.second, + networkInfo.first, networkInfo.second) ); } +uint32_t CWallet::BIP44CoinType() { + return Params(networkIdString).BIP44CoinType(); +} + bool CWallet::LoadHDSeed(const HDSeed& seed) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3641970ece1..1bd9f16825a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -806,7 +806,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* the hd chain data model (chain counters) */ CHDChain hdChain; - uint32_t bip44CoinType; + std::string networkIdString; public: /* @@ -864,7 +864,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nTimeFirstKey = 0; fBroadcastTransactions = false; nWitnessCacheSize = 0; - bip44CoinType = params.BIP44CoinType(); + networkIdString = params.NetworkIDString(); } /** @@ -1298,7 +1298,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SetHDChain(const CHDChain& chain, bool memonly); const CHDChain& GetHDChain() const { return hdChain; } - void CheckBIP44CoinType(uint32_t coinType); + void CheckNetworkInfo(std::pair networkInfo); + uint32_t BIP44CoinType(); /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ bool LoadHDSeed(const HDSeed& key); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index f07eae4c199..7ac13f32af9 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -724,11 +724,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> chain; pwallet->SetHDChain(chain, true); } - else if (strType == "bip44cointype") + else if (strType == "networkinfo") { - uint32_t bip44CoinType; - ssValue >> bip44CoinType; - pwallet->CheckBIP44CoinType(bip44CoinType); + std::pair networkInfo; + ssValue >> networkInfo; + pwallet->CheckNetworkInfo(networkInfo); } } catch (...) { @@ -1148,10 +1148,11 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } -bool CWalletDB::WriteBIP44CoinType(uint32_t bip44CoinType) +bool CWalletDB::WriteNetworkInfo(const std::string& networkId) { nWalletDBUpdateCounter++; - return Write(std::string("bip44cointype"), bip44CoinType); + std::pair networkInfo(PACKAGE_NAME, networkId); + return Write(std::string("networkinfo"), networkInfo); } bool CWalletDB::WriteHDSeed(const HDSeed& seed) diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 825297cbdaf..5d0a2d0debf 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -168,7 +168,7 @@ class CWalletDB : public CDB static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); - bool WriteBIP44CoinType(uint32_t bip44CoinType); + bool WriteNetworkInfo(const std::string& networkId); bool WriteHDSeed(const HDSeed& seed); bool WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); //! write the hdchain model (external chain child index counter) From f9656e26376c5eae5c7f2090f495a63ac5080e16 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 29 Sep 2021 12:52:41 -0600 Subject: [PATCH 046/514] Add a check to test that wallet load fails if we're on the wrong network. --- src/wallet/gtest/test_wallet.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index af1ef1bea51..4a47804b31c 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -126,6 +126,31 @@ TEST(WalletTests, SetupDatadirLocationRunAsFirstTest) { mapArgs["-datadir"] = pathTemp.string(); } +TEST(WalletTests, WalletNetworkSerialization) { + SelectParams(CBaseChainParams::TESTNET); + + // Get temporary and unique path for file. + // Note: / operator to append paths + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); + mapArgs["-datadir"] = pathTemp.string(); + + // create a new testnet wallet and generate a seed + CWallet wallet(Params(), "wallet.dat"); + wallet.InitLoadWallet(Params(), true); + wallet.GenerateNewSeed(); + wallet.Flush(); + + // now, switch to mainnet and attempt to restore the wallet + // using the same wallet.dat + SelectParams(CBaseChainParams::MAIN); + CWallet restored(Params(), "wallet.dat"); + + // load should fail due to being associated with the wrong network + bool fFirstRunRet; + EXPECT_NE(restored.LoadWallet(fFirstRunRet), DB_LOAD_OK); +} + TEST(WalletTests, SproutNoteDataSerialisation) { auto sk = libzcash::SproutSpendingKey::random(); auto wtx = GetValidSproutReceive(sk, 10, true); From af6b18b3ba7efe69b69da6e989ecabad4de6d3ab Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Thu, 30 Sep 2021 08:53:17 -0600 Subject: [PATCH 047/514] add TestSetIBD(bool) for testing --- src/main.cpp | 15 +++++++++++---- src/main.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f1b27f8ef91..14d3fb4d73f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2239,16 +2239,23 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) } } +static std::atomic IBDLatchToFalse{false}; +// testing-only, allow initial block down state to be set or reset +bool TestSetIBD(bool ibd) { + bool ret = !IBDLatchToFalse; + IBDLatchToFalse.store(!ibd, std::memory_order_relaxed); + return ret; +} + bool IsInitialBlockDownload(const Consensus::Params& params) { // Once this function has returned false, it must remain false. - static std::atomic latchToFalse{false}; // Optimization: pre-test latch before taking the lock. - if (latchToFalse.load(std::memory_order_relaxed)) + if (IBDLatchToFalse.load(std::memory_order_relaxed)) return false; LOCK(cs_main); - if (latchToFalse.load(std::memory_order_relaxed)) + if (IBDLatchToFalse.load(std::memory_order_relaxed)) return false; if (fImporting || fReindex) return true; @@ -2310,7 +2317,7 @@ bool IsInitialBlockDownload(const Consensus::Params& params) if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) return true; LogPrintf("Leaving InitialBlockDownload (latching to false)\n"); - latchToFalse.store(true, std::memory_order_relaxed); + IBDLatchToFalse.store(true, std::memory_order_relaxed); return false; } diff --git a/src/main.h b/src/main.h index 85ca9af29d8..ab2cb54b85f 100644 --- a/src/main.h +++ b/src/main.h @@ -273,6 +273,8 @@ bool SendMessages(const Consensus::Params& params, CNode* pto); void ThreadScriptCheck(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(const Consensus::Params& params); +/** testing-only, set or reset initial block down (IBD) state, return previous */ +bool TestSetIBD(bool); /** Format a string that describes several potential problems detected by the core */ std::pair GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ From 5ad669bacacc517ff2d29b7dce75766c08d0f139 Mon Sep 17 00:00:00 2001 From: sgmoore Date: Sat, 2 Oct 2021 07:52:45 -0700 Subject: [PATCH 048/514] Update reduce-traffic.md - add one word Added one word "a" at line 49 --- doc/reduce-traffic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md index d9cb6195b9f..12b5c792b32 100644 --- a/doc/reduce-traffic.md +++ b/doc/reduce-traffic.md @@ -46,7 +46,7 @@ Be reminded of the effects of this setting. - Fee estimation will no longer work. - It sets the flag "-walletbroadcast" to be "0", only if it is currently unset. - Doing so disables the automatic broadcasting of transactions from wallet. Not + Doing so disables the automatic broadcasting of transactions from a wallet. Not relaying other's transactions could hurt your privacy if used while a wallet is loaded or if you use the node to broadcast transactions. - If a peer is whitelisted and "-whitelistforcerelay" is set to "1" (which will From 92d36b5a432cf77492d2bfda477517dafb936f23 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Thu, 30 Sep 2021 14:28:10 -0600 Subject: [PATCH 049/514] Disable IBD for all boost tests Disable IBD for all the boost unit tests, because that's the more common (default) mode of operation. The full boost test suite passes with this commit, both when run all together or run separately. Any future tests that need IBD to be active can always call TestSetIBD(true). --- src/test/test_bitcoin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 301aa93b141..df5d0a06ee7 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -78,6 +78,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) fCheckBlockIndex = true; SelectParams(chainName); noui_connect(); + TestSetIBD(false); } BasicTestingSetup::~BasicTestingSetup() From 64ecf700fc40eec17d01c51c2d410588849d789b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 5 Oct 2021 07:41:08 -0600 Subject: [PATCH 050/514] Add `listaddresses` RPC method. Closes #5328 Fixes #5338 --- src/wallet/rpcwallet.cpp | 95 ++++++++++++++++++++++++++++ src/wallet/test/rpc_wallet_tests.cpp | 24 +++++++ 2 files changed, 119 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 23fae933e56..03027de3c97 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -305,6 +305,100 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) return wtx.GetHash().GetHex(); } +UniValue listaddresses(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp) + throw runtime_error( + "listaddresses\n" + "\nLists the addresses tracked by this wallet.\n" + "\nResult:\n" + "[\n" + " [\n" + " {\n" + " \"transparent\": { \"address\": \"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"watchonly\": false },\n" + " }\n" + " {\n" + " \"sprout\": { \"address\": \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\"},\n" + " }\n" + " {\n" + " \"sapling\": { \"address\": \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\"},\n" + " }\n" + " ,...\n" + " ]\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listaddresses", "") + + HelpExampleRpc("listaddresses", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + KeyIO keyIO(Params()); + + UniValue ret(UniValue::VARR); + + { + std::set transparentDests; + // Get the CTxDestination values for all the entries in the transparent address book. + // This will include any address that has been generated by this wallet. + for (const std::pair& item : pwalletMain->mapAddressBook) { + transparentDests.insert(item.first); + } + + // Ensure we have every address that holds a balance. While this is likely to be redundant + // with respect to the entries in the address book for addresses generated by this wallet, + // there is not a guarantee that an externally generated address (such as one associated with + // a future unified incoming viewing key) will have been added to the address book. + for (const std::pair& item : pwalletMain->GetAddressBalances()) { + transparentDests.insert(item.first); + } + + for (const CTxDestination& dest : transparentDests) { + UniValue payload(UniValue::VOBJ); + payload.pushKV("address", keyIO.EncodeDestination(dest)); + + auto script = GetScriptForDestination(dest); + payload.pushKV("watchonly", pwalletMain->HaveWatchOnly(script)); + + UniValue entry(UniValue::VOBJ); + entry.pushKV("transparent", payload); + ret.push_back(entry); + } + } + { + std::set sproutAddresses; + pwalletMain->GetSproutPaymentAddresses(sproutAddresses); + for (const SproutPaymentAddress& addr : sproutAddresses) { + UniValue payload(UniValue::VOBJ); + payload.pushKV("address", keyIO.EncodePaymentAddress(addr)); + payload.pushKV("watchonly", HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)); + + UniValue entry(UniValue::VOBJ); + entry.pushKV("sprout", payload); + ret.push_back(entry); + } + } + { + std::set saplingAddresses; + pwalletMain->GetSaplingPaymentAddresses(saplingAddresses); + for (const SaplingPaymentAddress& addr : saplingAddresses) { + UniValue payload(UniValue::VOBJ); + payload.pushKV("address", keyIO.EncodePaymentAddress(addr)); + payload.pushKV("watchonly", HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)); + + UniValue entry(UniValue::VOBJ); + entry.pushKV("sapling", payload); + ret.push_back(entry); + } + } + + return ret; +} + UniValue listaddressgroupings(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -4608,6 +4702,7 @@ static const CRPCCommand commands[] = { "wallet", "importaddress", &importaddress, true }, { "wallet", "importpubkey", &importpubkey, true }, { "wallet", "keypoolrefill", &keypoolrefill, true }, + { "wallet", "listaddresses", &listaddresses, true }, { "wallet", "listaddressgroupings", &listaddressgroupings, false }, { "wallet", "listlockunspent", &listlockunspent, false }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false }, diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 1181e39605f..a69f72701d6 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -218,6 +218,20 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) /* Correct address, message and signature*/ BOOST_CHECK(CallRPC("verifymessage " + keyIO.EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true); + /********************************* + * listaddresses + *********************************/ + BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); + UniValue arr = retValue.get_array(); + BOOST_CHECK_EQUAL(2, arr.size()); + bool notFound = true; + for (auto a : arr.getValues()) { + auto t_obj = find_value(a.get_obj(), "transparent"); + auto addr = find_value(t_obj, "address").get_str(); + notFound &= keyIO.DecodeDestination(addr) != demoAddress; + } + BOOST_CHECK(!notFound); + /********************************* * fundrawtransaction *********************************/ @@ -662,6 +676,11 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) myaddrs.insert(element.get_str()); } + // Verify that the keys imported are also available from listaddresses + BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); + arr = retValue.get_array(); + BOOST_CHECK(arr.size() == (2 * n1)); + // Make new addresses for the set for (int i=0; iGenerateNewSproutZKey())); @@ -685,6 +704,11 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) listaddrs.insert(element.get_str()); } + // Verify that the keys imported are also available from listaddresses + BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); + arr = retValue.get_array(); + BOOST_CHECK(arr.size() == numAddrs); + // Verify the two sets of addresses are the same BOOST_CHECK(listaddrs.size() == numAddrs); BOOST_CHECK(myaddrs == listaddrs); From b608a6af4876f4693c76f8937b8293345fc666c4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 5 Oct 2021 16:31:37 -0600 Subject: [PATCH 051/514] Remove unused `AddDestData` method. --- src/wallet/wallet.cpp | 12 ------------ src/wallet/wallet.h | 2 -- 2 files changed, 14 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7d26262c43f..c15002670c9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4650,18 +4650,6 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { } } -bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) -{ - if (std::get_if(&dest)) - return false; - - mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); - if (!fFileBacked) - return true; - KeyIO keyIO(Params()); - return CWalletDB(strWalletFile).WriteDestData(keyIO.EncodeDestination(dest), key, value); -} - bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) { if (!mapAddressBook[dest].destdata.erase(key)) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1bd9f16825a..79566bc415e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1005,8 +1005,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool AddCScript(const CScript& redeemScript); bool LoadCScript(const CScript& redeemScript); - //! Adds a destination data tuple to the store, and saves it to disk - bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value); //! Erases a destination data tuple in the store and on disk bool EraseDestData(const CTxDestination &dest, const std::string &key); //! Adds a destination data tuple to the store, without saving it to disk From 9a11ac73a45168d2750a105b258adc164d354b26 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 6 Oct 2021 13:48:15 -0600 Subject: [PATCH 052/514] Categorize listaddresses result by source type. Addresses managed by the zcashd wallet may be generated using multiple different sources of randomness and/or the wallet's HD seeds. Depending upon how addresses are generated, they may divided into multiple sets, each associated with a separate spend authority and treated as an independent pool of funds. In the future, the root spend authority for a wallet will be a mnemonic seed phrase; the API represented by this PR anticipates that future and attempts to provide functionality that will serve both current and future needs, as well as the transition between them. --- src/wallet/rpcwallet.cpp | 201 +++++++++++++++++++++++++++++---------- 1 file changed, 150 insertions(+), 51 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 03027de3c97..5362c0bbb3d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -313,22 +313,37 @@ UniValue listaddresses(const UniValue& params, bool fHelp) if (fHelp) throw runtime_error( "listaddresses\n" - "\nLists the addresses tracked by this wallet.\n" + "\nLists the addresses managed by this wallet by source, including \n" + "those generated from randomness by this wallet, Sapling addresses \n" + "generated from the legacy HD seed, imported watchonly transparent \n" + "addresses, shielded addresses tracked using imported viewing keys, \n" + "and addresses derived from the wallet's mnemonic seed for releases \n" + "version 5.0.0 and above. \n" + "\nREMINDER: It is recommended that you back up your wallet.dat file \n" + "regularly!\n" "\nResult:\n" "[\n" - " [\n" - " {\n" - " \"transparent\": { \"address\": \"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"watchonly\": false },\n" - " }\n" - " {\n" - " \"sprout\": { \"address\": \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\"},\n" - " }\n" - " {\n" - " \"sapling\": { \"address\": \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\"},\n" - " }\n" - " ,...\n" - " ]\n" - " ,...\n" + " {\n" + " \"source\": \"imported|imported_watchonly|keypool|legacy_seed|mnemnoic_seed\"\n" + " \"transparent\": {\n" + " \"addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...],\n" + " \"change_addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...]\n" + " },\n" + " \"sprout\": {\n" + " \"addresses\": [\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\", ...]\n" + " },\n" + " \"sapling\": [\n" + " {\n" + " \"zip32_account_id\": 0, -- optional field, not present for imported/watchonly sources,\n" + " \"addresses\": [\n" + " \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\",\n" + " ...\n" + " ]\n" + " }\n" + " ,...\n" + " ]\n" + " },\n" + " ...\n" "]\n" "\nExamples:\n" + HelpExampleCli("listaddresses", "") @@ -341,59 +356,143 @@ UniValue listaddresses(const UniValue& params, bool fHelp) UniValue ret(UniValue::VARR); + // keypool-derived and imported/watchonly transparent addresses + std::set t_generated_dests; + std::set t_change_dests; + std::set t_watchonly_dests; + // Get the CTxDestination values for all the entries in the transparent address book. + // This will include any address that has been generated by this wallet. + for (const std::pair& item : pwalletMain->mapAddressBook) { + t_generated_dests.insert(item.first); + } + + // Ensure we have every address that holds a balance. While this is likely to be redundant + // with respect to the entries in the address book for addresses generated by this wallet, + // there is not a guarantee that an externally generated address (such as one associated with + // a future unified incoming viewing key) will have been added to the address book. + for (const std::pair& item : pwalletMain->GetAddressBalances()) { + auto script = GetScriptForDestination(item.first); + if (pwalletMain->HaveWatchOnly(script)) { + t_watchonly_dests.insert(item.first); + } else if (t_generated_dests.count(item.first) == 0) { + // assume that if we didn't add the address to the addrbook + // that it's a change address. Ideally we'd have a better way + // of checking this by exploring the transaction graph; + t_change_dests.insert(item.first); + } else { + // already accounted for in the address book + } + } + + /// sprout addresses + std::set sproutAddresses; + pwalletMain->GetSproutPaymentAddresses(sproutAddresses); + + /// sapling addresses + std::set saplingAddresses; + pwalletMain->GetSaplingPaymentAddresses(saplingAddresses); + + // legacy_random source { - std::set transparentDests; - // Get the CTxDestination values for all the entries in the transparent address book. - // This will include any address that has been generated by this wallet. - for (const std::pair& item : pwalletMain->mapAddressBook) { - transparentDests.insert(item.first); + // Add legacy randomly generated address records to the result. + // This includes transparent addresses generated by the wallet via + // the keypool and Sprout addresses for which we have the + // spending key. + UniValue random_t_addrs(UniValue::VARR); + for (const CTxDestination& dest : t_generated_dests) { + random_t_addrs.push_back(keyIO.EncodeDestination(dest)); } - // Ensure we have every address that holds a balance. While this is likely to be redundant - // with respect to the entries in the address book for addresses generated by this wallet, - // there is not a guarantee that an externally generated address (such as one associated with - // a future unified incoming viewing key) will have been added to the address book. - for (const std::pair& item : pwalletMain->GetAddressBalances()) { - transparentDests.insert(item.first); + UniValue random_t_change_addrs(UniValue::VARR); + for (const CTxDestination& dest : t_change_dests) { + random_t_change_addrs.push_back(keyIO.EncodeDestination(dest)); } - for (const CTxDestination& dest : transparentDests) { - UniValue payload(UniValue::VOBJ); - payload.pushKV("address", keyIO.EncodeDestination(dest)); + UniValue random_sprout_addrs(UniValue::VARR); + for (const SproutPaymentAddress& addr : sproutAddresses) { + if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + random_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + } - auto script = GetScriptForDestination(dest); - payload.pushKV("watchonly", pwalletMain->HaveWatchOnly(script)); + /// keypool source only applies to transparent transactions + UniValue random_t(UniValue::VOBJ); + random_t.pushKV("addresses", random_t_addrs); + random_t.pushKV("change_addresses", random_t_change_addrs); - UniValue entry(UniValue::VOBJ); - entry.pushKV("transparent", payload); - ret.push_back(entry); - } + UniValue random_sprout(UniValue::VOBJ); + random_sprout.pushKV("addresses", random_sprout_addrs); + + UniValue entry(UniValue::VOBJ); + entry.pushKV("source", "legacy_random"); + entry.pushKV("transparent", random_t); + entry.pushKV("sprout", random_sprout); + + ret.push_back(entry); } + + /// imported_watchonly source { - std::set sproutAddresses; - pwalletMain->GetSproutPaymentAddresses(sproutAddresses); + UniValue watchonly_t_addrs(UniValue::VARR); + for (const CTxDestination& dest: t_watchonly_dests) { + watchonly_t_addrs.push_back(keyIO.EncodeDestination(dest)); + } + + UniValue watchonly_sprout_addrs(UniValue::VARR); for (const SproutPaymentAddress& addr : sproutAddresses) { - UniValue payload(UniValue::VOBJ); - payload.pushKV("address", keyIO.EncodePaymentAddress(addr)); - payload.pushKV("watchonly", HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)); + if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + watchonly_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + } - UniValue entry(UniValue::VOBJ); - entry.pushKV("sprout", payload); - ret.push_back(entry); + UniValue watchonly_sapling_addrs(UniValue::VARR); + for (const SaplingPaymentAddress& addr : saplingAddresses) { + if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + watchonly_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } } + + UniValue watchonly_t(UniValue::VOBJ); + watchonly_t.pushKV("addresses", watchonly_t_addrs); + + UniValue watchonly_sprout(UniValue::VOBJ); + watchonly_sprout.pushKV("addresses", watchonly_sprout_addrs); + + UniValue watchonly_sapling_obj(UniValue::VOBJ); + watchonly_sapling_obj.pushKV("addresses", watchonly_sapling_addrs); + UniValue watchonly_sapling(UniValue::VARR); + watchonly_sapling.push_back(watchonly_sapling_obj); + + UniValue entry(UniValue::VOBJ); + entry.pushKV("source", "imported_watchonly"); + entry.pushKV("transparent", watchonly_t); + entry.pushKV("sprout", watchonly_sprout); + entry.pushKV("sapling", watchonly_sapling); + + ret.push_back(entry); } + + /// legacy_hdseed source { - std::set saplingAddresses; - pwalletMain->GetSaplingPaymentAddresses(saplingAddresses); + // TODO: split up by zip32 account id + UniValue legacy_sapling_addrs(UniValue::VARR); for (const SaplingPaymentAddress& addr : saplingAddresses) { - UniValue payload(UniValue::VOBJ); - payload.pushKV("address", keyIO.EncodePaymentAddress(addr)); - payload.pushKV("watchonly", HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)); - - UniValue entry(UniValue::VOBJ); - entry.pushKV("sapling", payload); - ret.push_back(entry); + if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } } + + UniValue legacy_sapling_obj(UniValue::VOBJ); + legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs); + + UniValue legacy_sapling(UniValue::VARR); + legacy_sapling.push_back(legacy_sapling_obj); + + UniValue entry(UniValue::VOBJ); + entry.pushKV("source", "legacy_hdseed"); + entry.pushKV("sapling", legacy_sapling); + + ret.push_back(entry); } return ret; From a1657333721b632213a5a36ef500bad0287b43c4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 6 Oct 2021 13:02:08 +0000 Subject: [PATCH 053/514] Mark v5 transaction format as standard for NU5 Without this change, mainnet and testnet nodes following standard rules will not accept v5 transactions into their mempools, causing them to be ignored. --- src/policy/policy.cpp | 9 ++++++++- src/primitives/transaction.h | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 5f524895688..8c187292223 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -59,8 +59,15 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const CChainParam { bool overwinterActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_OVERWINTER); bool saplingActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING); + bool nu5Active = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5); - if (saplingActive) { + if (nu5Active) { + // NU5 standard rules apply + if (tx.nVersion > CTransaction::NU5_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::NU5_MIN_CURRENT_VERSION) { + reason = "nu5-version"; + return false; + } + } else if (saplingActive) { // Sapling standard rules apply if (tx.nVersion > CTransaction::SAPLING_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::SAPLING_MIN_CURRENT_VERSION) { reason = "sapling-version"; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index e005cd8279b..b2180372088 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -719,6 +719,8 @@ class CTransaction static const int32_t OVERWINTER_MAX_CURRENT_VERSION = 3; static const int32_t SAPLING_MIN_CURRENT_VERSION = 4; static const int32_t SAPLING_MAX_CURRENT_VERSION = 4; + static const int32_t NU5_MIN_CURRENT_VERSION = 4; + static const int32_t NU5_MAX_CURRENT_VERSION = 5; static_assert(SPROUT_MIN_CURRENT_VERSION >= SPROUT_MIN_TX_VERSION, "standard rule for tx version should be consistent with network rule"); @@ -737,6 +739,13 @@ class CTransaction SAPLING_MAX_CURRENT_VERSION >= SAPLING_MIN_CURRENT_VERSION), "standard rule for tx version should be consistent with network rule"); + static_assert(NU5_MIN_CURRENT_VERSION >= SAPLING_MIN_TX_VERSION, + "standard rule for tx version should be consistent with network rule"); + + static_assert( (NU5_MAX_CURRENT_VERSION <= ZIP225_MAX_TX_VERSION && + NU5_MAX_CURRENT_VERSION >= NU5_MIN_CURRENT_VERSION), + "standard rule for tx version should be consistent with network rule"); + // The local variables are made const to prevent unintended modification // without updating the cached hash value. However, CTransaction is not // actually immutable; deserialization and assignment are implemented, From eb85b06c5a4ba1e57f27724d5d49df1005368d97 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 6 Oct 2021 13:05:40 +0000 Subject: [PATCH 054/514] Fix comment We switched testnet to follow standard rules in zcash/zcash#1604, in order to find bugs such as the one fixed in the previous commit. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f1b27f8ef91..8b4d2feb605 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1787,7 +1787,7 @@ bool AcceptToMemoryPool( if (tx.IsCoinBase()) return state.DoS(100, false, REJECT_INVALID, "coinbase"); - // Rather not work on nonstandard transactions (unless -testnet/-regtest) + // Rather not work on nonstandard transactions (unless -regtest) string reason; if (chainparams.RequireStandard() && !IsStandardTx(tx, reason, chainparams, nextBlockHeight)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); From ad35b89e051075dd0e12a13957859b797fea8948 Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 7 Oct 2021 11:38:37 +1300 Subject: [PATCH 055/514] contrib: Add space between URL and period Co-authored-by: Daira Hopwood --- contrib/debian/copyright | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 7164656be9f..dda7a253469 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -96,7 +96,7 @@ Files: depends/sources/clang-llvm-*.tar.xz Copyright: The libc++ Authors License: Apache-2 with LLVM exception Comment: This entry is specifically for the libc++ library. The libc++ Authors - are listed in https://github.com/llvm/llvm-project/blob/main/libcxx/CREDITS.TXT. + are listed in https://github.com/llvm/llvm-project/blob/main/libcxx/CREDITS.TXT . Files: depends/sources/db-*.tar.gz Copyright: 1990, 2016 Oracle and/or its affiliates; From 6be880fe34c9b599752f181facea3f337f4f0fd3 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 6 Oct 2021 20:39:52 -0600 Subject: [PATCH 056/514] Correctly handle imported Sapling addresses Update the tests to reflect the new listaddresses result structure. --- src/wallet/rpcwallet.cpp | 180 ++++++++++++++++++--------- src/wallet/test/rpc_wallet_tests.cpp | 69 +++++++--- src/wallet/wallet.cpp | 46 ++++++- src/wallet/wallet.h | 21 ++++ 4 files changed, 242 insertions(+), 74 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5362c0bbb3d..8e0fb403c20 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -398,101 +398,167 @@ UniValue listaddresses(const UniValue& params, bool fHelp) // This includes transparent addresses generated by the wallet via // the keypool and Sprout addresses for which we have the // spending key. - UniValue random_t_addrs(UniValue::VARR); - for (const CTxDestination& dest : t_generated_dests) { - random_t_addrs.push_back(keyIO.EncodeDestination(dest)); - } + UniValue entry(UniValue::VOBJ); + entry.pushKV("source", "legacy_random"); + bool hasData = false; - UniValue random_t_change_addrs(UniValue::VARR); - for (const CTxDestination& dest : t_change_dests) { - random_t_change_addrs.push_back(keyIO.EncodeDestination(dest)); + UniValue random_t(UniValue::VOBJ); + + if (!t_generated_dests.empty()) { + UniValue random_t_addrs(UniValue::VARR); + for (const CTxDestination& dest : t_generated_dests) { + random_t_addrs.push_back(keyIO.EncodeDestination(dest)); + } + random_t.pushKV("addresses", random_t_addrs); + hasData = true; } - UniValue random_sprout_addrs(UniValue::VARR); - for (const SproutPaymentAddress& addr : sproutAddresses) { - if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { - random_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + if (!t_change_dests.empty()) { + UniValue random_t_change_addrs(UniValue::VARR); + for (const CTxDestination& dest : t_change_dests) { + random_t_change_addrs.push_back(keyIO.EncodeDestination(dest)); } + random_t.pushKV("change_addresses", random_t_change_addrs); + hasData = true; } - /// keypool source only applies to transparent transactions - UniValue random_t(UniValue::VOBJ); - random_t.pushKV("addresses", random_t_addrs); - random_t.pushKV("change_addresses", random_t_change_addrs); + if (hasData) { + entry.pushKV("transparent", random_t); + } + + if (!sproutAddresses.empty()) { + UniValue random_sprout_addrs(UniValue::VARR); + for (const SproutPaymentAddress& addr : sproutAddresses) { + if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + random_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + } + + UniValue random_sprout(UniValue::VOBJ); + random_sprout.pushKV("addresses", random_sprout_addrs); + + entry.pushKV("sprout", random_sprout); + hasData = true; + } - UniValue random_sprout(UniValue::VOBJ); - random_sprout.pushKV("addresses", random_sprout_addrs); + if (hasData) { + ret.push_back(entry); + } + } + /// imported source + { UniValue entry(UniValue::VOBJ); - entry.pushKV("source", "legacy_random"); - entry.pushKV("transparent", random_t); - entry.pushKV("sprout", random_sprout); + entry.pushKV("source", "imported"); + + bool hasData = false; + { + UniValue imported_sapling_addrs(UniValue::VARR); + for (const SaplingPaymentAddress& addr : saplingAddresses) { + if (GetSourceForPaymentAddress(pwalletMain)(addr) == Imported) { + imported_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + } - ret.push_back(entry); + if (!imported_sapling_addrs.empty()) { + UniValue imported_sapling_obj(UniValue::VOBJ); + imported_sapling_obj.pushKV("addresses", imported_sapling_addrs); + UniValue imported_sapling(UniValue::VARR); + imported_sapling.push_back(imported_sapling_obj); + + entry.pushKV("sapling", imported_sapling); + hasData = true; + } + } + + if (hasData) { + ret.push_back(entry); + } } /// imported_watchonly source { - UniValue watchonly_t_addrs(UniValue::VARR); - for (const CTxDestination& dest: t_watchonly_dests) { - watchonly_t_addrs.push_back(keyIO.EncodeDestination(dest)); - } + UniValue entry(UniValue::VOBJ); + entry.pushKV("source", "imported_watchonly"); + bool hasData = false; - UniValue watchonly_sprout_addrs(UniValue::VARR); - for (const SproutPaymentAddress& addr : sproutAddresses) { - if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { - watchonly_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + if (!t_watchonly_dests.empty()) { + UniValue watchonly_t_addrs(UniValue::VARR); + for (const CTxDestination& dest: t_watchonly_dests) { + watchonly_t_addrs.push_back(keyIO.EncodeDestination(dest)); } + + UniValue watchonly_t(UniValue::VOBJ); + watchonly_t.pushKV("addresses", watchonly_t_addrs); + + entry.pushKV("transparent", watchonly_t); + hasData = true; } - UniValue watchonly_sapling_addrs(UniValue::VARR); - for (const SaplingPaymentAddress& addr : saplingAddresses) { - if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { - watchonly_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + { + UniValue watchonly_sprout_addrs(UniValue::VARR); + for (const SproutPaymentAddress& addr : sproutAddresses) { + if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + watchonly_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } } - } - UniValue watchonly_t(UniValue::VOBJ); - watchonly_t.pushKV("addresses", watchonly_t_addrs); + if (!watchonly_sprout_addrs.empty()) { + UniValue watchonly_sprout(UniValue::VOBJ); + watchonly_sprout.pushKV("addresses", watchonly_sprout_addrs); + entry.pushKV("sprout", watchonly_sprout); + hasData = true; + } + } - UniValue watchonly_sprout(UniValue::VOBJ); - watchonly_sprout.pushKV("addresses", watchonly_sprout_addrs); + { + UniValue watchonly_sapling_addrs(UniValue::VARR); + for (const SaplingPaymentAddress& addr : saplingAddresses) { + if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + watchonly_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + } - UniValue watchonly_sapling_obj(UniValue::VOBJ); - watchonly_sapling_obj.pushKV("addresses", watchonly_sapling_addrs); - UniValue watchonly_sapling(UniValue::VARR); - watchonly_sapling.push_back(watchonly_sapling_obj); + if (!watchonly_sapling_addrs.empty()) { + UniValue watchonly_sapling_obj(UniValue::VOBJ); + watchonly_sapling_obj.pushKV("addresses", watchonly_sapling_addrs); + UniValue watchonly_sapling(UniValue::VARR); + watchonly_sapling.push_back(watchonly_sapling_obj); - UniValue entry(UniValue::VOBJ); - entry.pushKV("source", "imported_watchonly"); - entry.pushKV("transparent", watchonly_t); - entry.pushKV("sprout", watchonly_sprout); - entry.pushKV("sapling", watchonly_sapling); + entry.pushKV("sapling", watchonly_sapling); + hasData = true; + } + } - ret.push_back(entry); + if (hasData) { + ret.push_back(entry); + } } /// legacy_hdseed source { + UniValue entry(UniValue::VOBJ); + entry.pushKV("source", "legacy_hdseed"); + // TODO: split up by zip32 account id UniValue legacy_sapling_addrs(UniValue::VARR); for (const SaplingPaymentAddress& addr : saplingAddresses) { - if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + if (GetSourceForPaymentAddress(pwalletMain)(addr) == LegacyHDSeed) { legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); } } - UniValue legacy_sapling_obj(UniValue::VOBJ); - legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs); + if (!legacy_sapling_addrs.empty()) { + UniValue legacy_sapling_obj(UniValue::VOBJ); + legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs); - UniValue legacy_sapling(UniValue::VARR); - legacy_sapling.push_back(legacy_sapling_obj); + UniValue legacy_sapling(UniValue::VARR); + legacy_sapling.push_back(legacy_sapling_obj); - UniValue entry(UniValue::VOBJ); - entry.pushKV("source", "legacy_hdseed"); - entry.pushKV("sapling", legacy_sapling); + entry.pushKV("sapling", legacy_sapling); - ret.push_back(entry); + ret.push_back(entry); + } } return ret; diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index a69f72701d6..968cd9503bb 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -223,14 +223,22 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) *********************************/ BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); UniValue arr = retValue.get_array(); - BOOST_CHECK_EQUAL(2, arr.size()); - bool notFound = true; - for (auto a : arr.getValues()) { - auto t_obj = find_value(a.get_obj(), "transparent"); - auto addr = find_value(t_obj, "address").get_str(); - notFound &= keyIO.DecodeDestination(addr) != demoAddress; + { + BOOST_CHECK_EQUAL(1, arr.size()); + bool notFound = true; + for (auto a : arr.getValues()) { + auto source = find_value(a.get_obj(), "source"); + if (source.get_str() == "legacy_random") { + auto t_obj = find_value(a.get_obj(), "transparent"); + auto addrs = find_value(t_obj, "addresses").get_array(); + BOOST_CHECK_EQUAL(2, addrs.size()); + for (auto addr : addrs.getValues()) { + notFound &= keyIO.DecodeDestination(addr.get_str()) != demoAddress; + } + } + } + BOOST_CHECK(!notFound); } - BOOST_CHECK(!notFound); /********************************* * fundrawtransaction @@ -670,17 +678,35 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) UniValue arr = retValue.get_array(); BOOST_CHECK(arr.size() == (2 * n1)); + // Verify that the keys imported are also available from listaddresses + { + BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); + auto listarr = retValue.get_array(); + bool sproutCountMatch = false; + bool saplingCountMatch = false; + for (auto a : listarr.getValues()) { + auto source = find_value(a.get_obj(), "source"); + if (source.get_str() == "legacy_random") { + auto sprout_obj = find_value(a.get_obj(), "sprout").get_obj(); + auto sprout_addrs = find_value(sprout_obj, "addresses").get_array(); + sproutCountMatch = (sprout_addrs.size() == n1); + } + if (source.get_str() == "imported") { + auto sapling_obj = find_value(a.get_obj(), "sapling").get_array()[0]; + auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); + saplingCountMatch = (sapling_addrs.size() == n1); + } + } + BOOST_CHECK(sproutCountMatch); + BOOST_CHECK(saplingCountMatch); + } + // Put addresses into a set std::unordered_set myaddrs; for (UniValue element : arr.getValues()) { myaddrs.insert(element.get_str()); } - // Verify that the keys imported are also available from listaddresses - BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); - arr = retValue.get_array(); - BOOST_CHECK(arr.size() == (2 * n1)); - // Make new addresses for the set for (int i=0; iGenerateNewSproutZKey())); @@ -704,10 +730,21 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) listaddrs.insert(element.get_str()); } - // Verify that the keys imported are also available from listaddresses - BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); - arr = retValue.get_array(); - BOOST_CHECK(arr.size() == numAddrs); + // Verify that the newly added sprout keys imported are also available from listaddresses + { + BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); + auto listarr = retValue.get_array(); + bool sproutCountMatch = false; + for (auto a : listarr.getValues()) { + auto source = find_value(a.get_obj(), "source"); + if (source.get_str() == "legacy_random") { + auto sprout_obj = find_value(a.get_obj(), "sprout").get_obj(); + auto sprout_addrs = find_value(sprout_obj, "addresses").get_array(); + sproutCountMatch = (sprout_addrs.size() == n1 + n2); + } + } + BOOST_CHECK(sproutCountMatch); + } // Verify the two sets of addresses are the same BOOST_CHECK(listaddrs.size() == numAddrs); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3d6fb1fdd0b..f31d332fffc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5211,6 +5211,50 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& return false; } +/// + +PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const +{ + return Random; +} + +PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const +{ + libzcash::SaplingIncomingViewingKey ivk; + + // If we have a SaplingExtendedSpendingKey in the wallet, then we will + // also have the corresponding SaplingExtendedFullViewingKey. + if (m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk)) { + if (m_wallet->HaveSaplingFullViewingKey(ivk)) { + // If we have the HD keypath, it's related to the legacy seed + if (m_wallet->mapSaplingZKeyMetadata.count(ivk) > 0 && + m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath != "") { + return LegacyHDSeed; + } else { + return Imported; + } + } else { + return Imported; + } + } else { + return AddressNotFound; + } +} + +PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const +{ + // TODO + return AddressNotFound; +} + +PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const +{ + return AddressNotFound; +} + + +/// + std::optional GetViewingKeyForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -5385,7 +5429,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS // 154051200 seconds from epoch is Friday, 26 October 2018 00:00:00 GMT - definitely before Sapling activates m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = std::max((int64_t) 154051200, nTime); } - if (hdKeypath) { + if (hdKeypath.has_value()) { m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.value(); } if (seedFpStr) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 423daf99f7b..01b15d00a6e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1411,6 +1411,27 @@ class GetSpendingKeyForPaymentAddress std::optional operator()(const libzcash::InvalidEncoding& no) const; }; +enum PaymentAddressSource { + Random, + LegacyHDSeed, + MnemonicHDSeed, + Imported, + AddressNotFound, +}; + +class GetSourceForPaymentAddress +{ +private: + CWallet *m_wallet; +public: + GetSourceForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + + PaymentAddressSource operator()(const libzcash::SproutPaymentAddress &zaddr) const; + PaymentAddressSource operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + PaymentAddressSource operator()(const libzcash::UnifiedAddress &uaddr) const; + PaymentAddressSource operator()(const libzcash::InvalidEncoding& no) const; +}; + enum KeyAddResult { SpendingKeyExists, KeyAlreadyExists, From 33e449fcd17e9116112278b3c7a26efe9a5d175e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 6 Oct 2021 20:43:48 -0600 Subject: [PATCH 057/514] Apply suggestions from code review Co-authored-by: str4d --- src/wallet/rpcwallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8e0fb403c20..7027f317593 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -339,8 +339,8 @@ UniValue listaddresses(const UniValue& params, bool fHelp) " \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\",\n" " ...\n" " ]\n" - " }\n" - " ,...\n" + " },\n" + " ...\n" " ]\n" " },\n" " ...\n" From f8350ce93d9abc38e093732d4e4ec2ba108aaf72 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 07:41:15 -0600 Subject: [PATCH 058/514] Apply suggestions from code review Co-authored-by: Daira Hopwood --- src/wallet/rpcwallet.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 7027f317593..d9b5ded6ac5 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -318,13 +318,13 @@ UniValue listaddresses(const UniValue& params, bool fHelp) "generated from the legacy HD seed, imported watchonly transparent \n" "addresses, shielded addresses tracked using imported viewing keys, \n" "and addresses derived from the wallet's mnemonic seed for releases \n" - "version 5.0.0 and above. \n" + "version 4.5.2 and above. \n" "\nREMINDER: It is recommended that you back up your wallet.dat file \n" "regularly!\n" "\nResult:\n" "[\n" " {\n" - " \"source\": \"imported|imported_watchonly|keypool|legacy_seed|mnemnoic_seed\"\n" + " \"source\": \"imported|imported_watchonly|keypool|legacy_seed|mnemonic_seed\"\n" " \"transparent\": {\n" " \"addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...],\n" " \"change_addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...]\n" @@ -332,7 +332,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) " \"sprout\": {\n" " \"addresses\": [\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\", ...]\n" " },\n" - " \"sapling\": [\n" + " \"sapling\": [ -- each element in this list represents a set of diversified addresses derived from a single IVK. \n" " {\n" " \"zip32_account_id\": 0, -- optional field, not present for imported/watchonly sources,\n" " \"addresses\": [\n" @@ -345,6 +345,8 @@ UniValue listaddresses(const UniValue& params, bool fHelp) " },\n" " ...\n" "]\n" + "In the case that a source does not have addresses for a pool, the key\n" + "associated with that pool will be absent.\n" "\nExamples:\n" + HelpExampleCli("listaddresses", "") + HelpExampleRpc("listaddresses", "") @@ -422,7 +424,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) hasData = true; } - if (hasData) { + if (!t_generated_dests.empty() || !t_change_dests.empty()) { entry.pushKV("transparent", random_t); } From 6b1d29078976121985558348a04fb2644cbe0874 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 09:41:09 -0600 Subject: [PATCH 059/514] Group legacy_hdseed Sapling addresses by account ID. --- src/wallet/rpcwallet.cpp | 40 +++++++++++++++++++++------- src/wallet/test/rpc_wallet_tests.cpp | 38 ++++++++++++++++++++++++-- src/zcash/address/zip32.cpp | 10 +++++++ src/zcash/address/zip32.h | 4 +++ 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index d9b5ded6ac5..85c1878afe2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -541,24 +541,46 @@ UniValue listaddresses(const UniValue& params, bool fHelp) { UniValue entry(UniValue::VOBJ); entry.pushKV("source", "legacy_hdseed"); + bool hasData = false; - // TODO: split up by zip32 account id - UniValue legacy_sapling_addrs(UniValue::VARR); + std::map> ivkAddrs; for (const SaplingPaymentAddress& addr : saplingAddresses) { if (GetSourceForPaymentAddress(pwalletMain)(addr) == LegacyHDSeed) { - legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + SaplingIncomingViewingKey ivkRet; + if (pwalletMain->GetSaplingIncomingViewingKey(addr, ivkRet)) { + ivkAddrs[ivkRet].push_back(addr); + } } } - if (!legacy_sapling_addrs.empty()) { - UniValue legacy_sapling_obj(UniValue::VOBJ); - legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs); - + { UniValue legacy_sapling(UniValue::VARR); - legacy_sapling.push_back(legacy_sapling_obj); + for (const auto& [ivk, addrs] : ivkAddrs) { + UniValue legacy_sapling_addrs(UniValue::VARR); + for (const SaplingPaymentAddress& addr : addrs) { + legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + + // this is known to be nonempty from the GetSourceForPaymentAddress check. + std::string hdKeypath = pwalletMain->mapSaplingZKeyMetadata[ivk].hdKeypath; + std::optional accountId = libzcash::ParseZip32KeypathAccount(hdKeypath); + + UniValue legacy_sapling_obj(UniValue::VOBJ); + if (accountId.has_value()) { + legacy_sapling_obj.pushKV("zip32_account_id", (uint64_t) accountId.value()); + } + legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs); - entry.pushKV("sapling", legacy_sapling); + legacy_sapling.push_back(legacy_sapling_obj); + } + if (!legacy_sapling.empty()) { + entry.pushKV("sapling", legacy_sapling); + hasData = true; + } + } + + if (hasData) { ret.push_back(entry); } } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 968cd9503bb..c93a9a4610f 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -616,7 +616,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) /* - * This test covers RPC commands z_listaddresses, z_importkey, z_exportkey + * This test covers RPC commands z_listaddresses, z_importkey, z_exportkey, listaddresses */ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) { @@ -749,7 +749,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) // Verify the two sets of addresses are the same BOOST_CHECK(listaddrs.size() == numAddrs); BOOST_CHECK(myaddrs == listaddrs); - } // Check if address is of given type and spendable from our wallet. @@ -790,6 +789,41 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { // Too many arguments will throw with the help BOOST_CHECK_THROW(CallRPC("z_getnewaddress many args"), runtime_error); + + { + UniValue list; + BOOST_CHECK_NO_THROW(list = CallRPC("listaddresses")); + auto listarr = list.get_array(); + bool sproutCountMatch = false; + bool saplingIvksMatch = false; + bool saplingAccount0 = false; + bool saplingAccount1 = false; + bool saplingCountMismatch = true; + for (auto a : listarr.getValues()) { + auto source = find_value(a.get_obj(), "source"); + if (source.get_str() == "legacy_random") { + auto sprout_obj = find_value(a.get_obj(), "sprout").get_obj(); + auto sprout_addrs = find_value(sprout_obj, "addresses").get_array(); + sproutCountMatch = (sprout_addrs.size() == 1); + } + if (source.get_str() == "legacy_hdseed") { + auto sapling_addr_sets = find_value(a.get_obj(), "sapling").get_array(); + saplingIvksMatch = (sapling_addr_sets.size() == 2); + + for (auto sapling_obj : sapling_addr_sets.getValues()) { + auto sapling_account = find_value(sapling_obj, "zip32_account_id").get_int(); + saplingAccount0 |= (sapling_account == 0); + saplingAccount1 |= (sapling_account == 1); + auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); + saplingCountMismatch &= (sapling_addrs.size() != 1); + } + } + } + BOOST_CHECK(sproutCountMatch); + BOOST_CHECK(!saplingCountMismatch); + BOOST_CHECK(saplingAccount0); + BOOST_CHECK(saplingAccount1); + } } /** diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index d366189602c..83cac8e727b 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -158,4 +158,14 @@ libzcash::SaplingPaymentAddress SaplingExtendedSpendingKey::DefaultAddress() con return ToXFVK().DefaultAddress(); } +std::optional ParseZip32KeypathAccount(const std::string& keyPath) { + std::regex pattern("m/32'/[0-9]+'/([0-9]+)'"); + std::smatch matches; + if (std::regex_match(keyPath, matches, pattern)) { + return stoul(matches[1]); + } else { + return std::nullopt; + } +} + } diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 50e5516ae5c..b68bd207843 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -11,6 +11,8 @@ #include "zcash/address/sapling.hpp" #include +#include +#include const uint32_t ZIP32_HARDENED_KEY_LIMIT = 0x80000000; const size_t ZIP32_XFVK_SIZE = 169; @@ -134,6 +136,8 @@ struct SaplingExtendedSpendingKey { } }; +std::optional ParseZip32KeypathAccount(const std::string& keyPath); + } #endif // ZCASH_ZCASH_ADDRESS_ZIP32_H From 7b40f874044611834ab730a822eb74b20e24b505 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 14:25:50 -0600 Subject: [PATCH 060/514] Update release notes for v4.5.1-1 to reflect the addition of `listaddresses` --- doc/release-notes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/release-notes.md b/doc/release-notes.md index a29094b5174..51e0d75eacd 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,3 +4,9 @@ release-notes at release time) Notable changes =============== +The `listaddresses` endpoint has been added to the RPC API. This method +allows the caller to obtain addresses managed by the wallet, grouped +by the source of the address, including both those addresses generated +by the wallet and those associated with imported viewing or spending +keys. This provides functionality that replaces and subsumes the +previously-removed `getaddressesbyaccount` method. From 9e83d28afb776e53c79b8b0feece648970940b6c Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 17:43:56 -0600 Subject: [PATCH 061/514] Include `ImportedWatchOnly` as a PaymentAddressSource result. --- src/wallet/rpcwallet.cpp | 2 +- src/wallet/test/rpc_wallet_tests.cpp | 55 ++++++++++++++++++---------- src/wallet/wallet.cpp | 6 ++- src/wallet/wallet.h | 1 + 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 85c1878afe2..16ca4e1cfb0 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -500,7 +500,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) { UniValue watchonly_sprout_addrs(UniValue::VARR); for (const SproutPaymentAddress& addr : sproutAddresses) { - if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + if (GetSourceForPaymentAddress(pwalletMain)(addr) == ImportedWatchOnly) { watchonly_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); } } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index c93a9a4610f..c649ad511c9 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -614,9 +614,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) BOOST_CHECK_EQUAL(testKey, keyIO.EncodeSpendingKey(k)); } - /* - * This test covers RPC commands z_listaddresses, z_importkey, z_exportkey, listaddresses + * This test covers RPC commands z_listaddresses, z_importkey, z_exportkey, + * listaddresses, z_importviewingkey, z_exportviewingkey */ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) { @@ -643,9 +643,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) BOOST_CHECK_THROW(CallRPC(prefix + "100badchars"), runtime_error); // wallet should currently be empty - std::set addrs; - pwalletMain->GetSproutPaymentAddresses(addrs); - BOOST_CHECK(addrs.size()==0); + std::set sproutAddrs; + pwalletMain->GetSproutPaymentAddresses(sproutAddrs); + BOOST_CHECK(sproutAddrs.size()==0); std::set saplingAddrs; pwalletMain->GetSaplingPaymentAddresses(saplingAddrs); BOOST_CHECK(saplingAddrs.empty()); @@ -663,27 +663,36 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testAddr)); BOOST_CHECK_EQUAL(retValue.get_str(), testKey); - // create a random Sapling key locally + // create a random Sapling key locally; split between IVKs and spending keys. auto testSaplingSpendingKey = m.Derive(i); auto testSaplingPaymentAddress = testSaplingSpendingKey.DefaultAddress(); - std::string testSaplingAddr = keyIO.EncodePaymentAddress(testSaplingPaymentAddress); - std::string testSaplingKey = keyIO.EncodeSpendingKey(testSaplingSpendingKey); - BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testSaplingKey)); - BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testSaplingAddr)); - BOOST_CHECK_EQUAL(retValue.get_str(), testSaplingKey); + if (i % 2 == 0) { + std::string testSaplingAddr = keyIO.EncodePaymentAddress(testSaplingPaymentAddress); + std::string testSaplingKey = keyIO.EncodeSpendingKey(testSaplingSpendingKey); + BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testSaplingKey)); + BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testSaplingAddr)); + BOOST_CHECK_EQUAL(retValue.get_str(), testSaplingKey); + } else { + std::string testSaplingAddr = keyIO.EncodePaymentAddress(testSaplingPaymentAddress); + std::string testSaplingKey = keyIO.EncodeViewingKey(testSaplingSpendingKey.ToXFVK()); + BOOST_CHECK_NO_THROW(CallRPC(string("z_importviewingkey ") + testSaplingKey)); + BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportviewingkey ") + testSaplingAddr)); + BOOST_CHECK_EQUAL(retValue.get_str(), testSaplingKey); + } } // Verify we can list the keys imported BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); UniValue arr = retValue.get_array(); - BOOST_CHECK(arr.size() == (2 * n1)); + BOOST_CHECK(arr.size() == n1 + (n1 / 2)); // Verify that the keys imported are also available from listaddresses { BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); auto listarr = retValue.get_array(); bool sproutCountMatch = false; - bool saplingCountMatch = false; + bool saplingSpendingKeyMatch = false; + bool saplingIVKMatch = false; for (auto a : listarr.getValues()) { auto source = find_value(a.get_obj(), "source"); if (source.get_str() == "legacy_random") { @@ -694,11 +703,17 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) if (source.get_str() == "imported") { auto sapling_obj = find_value(a.get_obj(), "sapling").get_array()[0]; auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); - saplingCountMatch = (sapling_addrs.size() == n1); + saplingSpendingKeyMatch = (sapling_addrs.size() == n1 / 2); + } + if (source.get_str() == "imported_watchonly") { + auto sapling_obj = find_value(a.get_obj(), "sapling").get_array()[0]; + auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); + saplingIVKMatch = (sapling_addrs.size() == n1 / 2); } } BOOST_CHECK(sproutCountMatch); - BOOST_CHECK(saplingCountMatch); + BOOST_CHECK(saplingSpendingKeyMatch); + BOOST_CHECK(saplingIVKMatch); } // Put addresses into a set @@ -712,12 +727,14 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) myaddrs.insert(keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey())); } - // Verify number of addresses stored in wallet is n1+n2 + // Verify number of addresses stored in wallet is correct int numAddrs = myaddrs.size(); - BOOST_CHECK(numAddrs == (2 * n1) + n2); - pwalletMain->GetSproutPaymentAddresses(addrs); + // sprout_addrs + sapling addrs with spend authority + new sprout addrs + BOOST_CHECK(numAddrs == (n1 + (n1 / 2) + n2)); + pwalletMain->GetSproutPaymentAddresses(sproutAddrs); pwalletMain->GetSaplingPaymentAddresses(saplingAddrs); - BOOST_CHECK(addrs.size() + saplingAddrs.size() == numAddrs); + // here we also include the watchonly sapling addresses + BOOST_CHECK(sproutAddrs.size() + saplingAddrs.size() == numAddrs + (n1 / 2)); // Ask wallet to list addresses BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f31d332fffc..4a99823f100 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5230,11 +5230,13 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl if (m_wallet->mapSaplingZKeyMetadata.count(ivk) > 0 && m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath != "") { return LegacyHDSeed; - } else { + } else if (HaveSpendingKeyForPaymentAddress(m_wallet)(zaddr)) { return Imported; + } else { + return ImportedWatchOnly; } } else { - return Imported; + return ImportedWatchOnly; } } else { return AddressNotFound; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 01b15d00a6e..c1f9c0cb199 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1416,6 +1416,7 @@ enum PaymentAddressSource { LegacyHDSeed, MnemonicHDSeed, Imported, + ImportedWatchOnly, AddressNotFound, }; From 2221bf5484abc3742009bb842920344d15815a67 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 18:12:13 -0600 Subject: [PATCH 062/514] Add listaddresses check to wallet_addresses.py --- qa/rpc-tests/wallet_addresses.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qa/rpc-tests/wallet_addresses.py b/qa/rpc-tests/wallet_addresses.py index d70b1bca06a..aa282ea7735 100755 --- a/qa/rpc-tests/wallet_addresses.py +++ b/qa/rpc-tests/wallet_addresses.py @@ -27,6 +27,15 @@ def addr_checks(default_type): assert_equal(res['type'], addr_type) assert(addr in all_addresses) + listed_addresses = self.nodes[0].listaddresses() + legacy_random_src = next(src for src in listed_addresses if src['source'] == 'legacy_random') + legacy_hdseed_src = next(src for src in listed_addresses if src['source'] == 'legacy_hdseed') + for addr_type, addr in types_and_addresses: + if addr_type == 'sprout': + assert(addr in legacy_random_src['sprout']['addresses']) + if addr_type == 'sapling': + assert(addr in [x for obj in legacy_hdseed_src['sapling'] for x in obj['addresses']]) + # Sanity-check the test harness assert_equal(self.nodes[0].getblockcount(), 200) From 23507899a3020a785f366ac9fa6375a6e3f4d223 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 19:58:31 -0600 Subject: [PATCH 063/514] Consistently group Sapling addresses by IVK for every source. --- src/wallet/rpcwallet.cpp | 131 +++++++++++++-------------- src/wallet/test/rpc_wallet_tests.cpp | 18 ++-- 2 files changed, 77 insertions(+), 72 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 16ca4e1cfb0..999ec54930d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -448,31 +448,83 @@ UniValue listaddresses(const UniValue& params, bool fHelp) } } + // inner function that groups Sapling addresses by IVK for use in all sources + // that can contain Sapling addresses + auto add_sapling = [&]( + const std::set& addrs, + const PaymentAddressSource source, + UniValue& entry + ) { + bool hasData = false; + + std::map> ivkAddrs; + for (const SaplingPaymentAddress& addr : addrs) { + if (GetSourceForPaymentAddress(pwalletMain)(addr) == source) { + SaplingIncomingViewingKey ivkRet; + if (pwalletMain->GetSaplingIncomingViewingKey(addr, ivkRet)) { + ivkAddrs[ivkRet].push_back(addr); + } + } + } + + { + UniValue ivk_groups(UniValue::VARR); + for (const auto& [ivk, addrs] : ivkAddrs) { + UniValue sapling_addrs(UniValue::VARR); + for (const SaplingPaymentAddress& addr : addrs) { + sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + } + + UniValue sapling_obj(UniValue::VOBJ); + + if (source == LegacyHDSeed) { + std::string hdKeypath = pwalletMain->mapSaplingZKeyMetadata[ivk].hdKeypath; + std::optional accountId = libzcash::ParseZip32KeypathAccount(hdKeypath); + + if (accountId.has_value()) { + sapling_obj.pushKV("zip32_account_id", (uint64_t) accountId.value()); + } + } + + sapling_obj.pushKV("addresses", sapling_addrs); + + ivk_groups.push_back(sapling_obj); + } + + if (!ivk_groups.empty()) { + entry.pushKV("sapling", ivk_groups); + hasData = true; + } + } + + return hasData; + }; + /// imported source { UniValue entry(UniValue::VOBJ); entry.pushKV("source", "imported"); bool hasData = false; + { - UniValue imported_sapling_addrs(UniValue::VARR); - for (const SaplingPaymentAddress& addr : saplingAddresses) { + UniValue imported_sprout_addrs(UniValue::VARR); + for (const SproutPaymentAddress& addr : sproutAddresses) { if (GetSourceForPaymentAddress(pwalletMain)(addr) == Imported) { - imported_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); + imported_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); } } - if (!imported_sapling_addrs.empty()) { - UniValue imported_sapling_obj(UniValue::VOBJ); - imported_sapling_obj.pushKV("addresses", imported_sapling_addrs); - UniValue imported_sapling(UniValue::VARR); - imported_sapling.push_back(imported_sapling_obj); - - entry.pushKV("sapling", imported_sapling); + if (!imported_sprout_addrs.empty()) { + UniValue imported_sprout(UniValue::VOBJ); + imported_sprout.pushKV("addresses", imported_sprout_addrs); + entry.pushKV("sprout", imported_sprout); hasData = true; } } + hasData |= add_sapling(saplingAddresses, Imported, entry); + if (hasData) { ret.push_back(entry); } @@ -513,24 +565,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) } } - { - UniValue watchonly_sapling_addrs(UniValue::VARR); - for (const SaplingPaymentAddress& addr : saplingAddresses) { - if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { - watchonly_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); - } - } - - if (!watchonly_sapling_addrs.empty()) { - UniValue watchonly_sapling_obj(UniValue::VOBJ); - watchonly_sapling_obj.pushKV("addresses", watchonly_sapling_addrs); - UniValue watchonly_sapling(UniValue::VARR); - watchonly_sapling.push_back(watchonly_sapling_obj); - - entry.pushKV("sapling", watchonly_sapling); - hasData = true; - } - } + hasData |= add_sapling(saplingAddresses, ImportedWatchOnly, entry); if (hasData) { ret.push_back(entry); @@ -541,48 +576,12 @@ UniValue listaddresses(const UniValue& params, bool fHelp) { UniValue entry(UniValue::VOBJ); entry.pushKV("source", "legacy_hdseed"); - bool hasData = false; - - std::map> ivkAddrs; - for (const SaplingPaymentAddress& addr : saplingAddresses) { - if (GetSourceForPaymentAddress(pwalletMain)(addr) == LegacyHDSeed) { - SaplingIncomingViewingKey ivkRet; - if (pwalletMain->GetSaplingIncomingViewingKey(addr, ivkRet)) { - ivkAddrs[ivkRet].push_back(addr); - } - } - } - - { - UniValue legacy_sapling(UniValue::VARR); - for (const auto& [ivk, addrs] : ivkAddrs) { - UniValue legacy_sapling_addrs(UniValue::VARR); - for (const SaplingPaymentAddress& addr : addrs) { - legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr)); - } - - // this is known to be nonempty from the GetSourceForPaymentAddress check. - std::string hdKeypath = pwalletMain->mapSaplingZKeyMetadata[ivk].hdKeypath; - std::optional accountId = libzcash::ParseZip32KeypathAccount(hdKeypath); - UniValue legacy_sapling_obj(UniValue::VOBJ); - if (accountId.has_value()) { - legacy_sapling_obj.pushKV("zip32_account_id", (uint64_t) accountId.value()); - } - legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs); - - legacy_sapling.push_back(legacy_sapling_obj); - } - - if (!legacy_sapling.empty()) { - entry.pushKV("sapling", legacy_sapling); - hasData = true; - } - } + bool hasData = add_sapling(saplingAddresses, LegacyHDSeed, entry); if (hasData) { ret.push_back(entry); - } + }; } return ret; diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index c649ad511c9..812261db819 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -701,14 +701,20 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) sproutCountMatch = (sprout_addrs.size() == n1); } if (source.get_str() == "imported") { - auto sapling_obj = find_value(a.get_obj(), "sapling").get_array()[0]; - auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); - saplingSpendingKeyMatch = (sapling_addrs.size() == n1 / 2); + int addr_count = 0; + for (auto sapling_obj : find_value(a.get_obj(), "sapling").get_array().getValues()) { + auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); + addr_count += sapling_addrs.size(); + } + saplingSpendingKeyMatch = (addr_count == n1 / 2); } if (source.get_str() == "imported_watchonly") { - auto sapling_obj = find_value(a.get_obj(), "sapling").get_array()[0]; - auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); - saplingIVKMatch = (sapling_addrs.size() == n1 / 2); + int addr_count = 0; + for (auto sapling_obj : find_value(a.get_obj(), "sapling").get_array().getValues()) { + auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); + addr_count += sapling_addrs.size(); + } + saplingIVKMatch = (addr_count == n1 / 2); } } BOOST_CHECK(sproutCountMatch); From d867a8dc67f873239b681becc138c826aabba509 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 8 Oct 2021 10:30:39 -0600 Subject: [PATCH 064/514] Use lowerCamelCase for listaddresses JSON --- src/wallet/rpcwallet.cpp | 8 ++++---- src/wallet/test/rpc_wallet_tests.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 999ec54930d..e4011d211e9 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -327,14 +327,14 @@ UniValue listaddresses(const UniValue& params, bool fHelp) " \"source\": \"imported|imported_watchonly|keypool|legacy_seed|mnemonic_seed\"\n" " \"transparent\": {\n" " \"addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...],\n" - " \"change_addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...]\n" + " \"changeAddresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...]\n" " },\n" " \"sprout\": {\n" " \"addresses\": [\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\", ...]\n" " },\n" " \"sapling\": [ -- each element in this list represents a set of diversified addresses derived from a single IVK. \n" " {\n" - " \"zip32_account_id\": 0, -- optional field, not present for imported/watchonly sources,\n" + " \"zip32AccountId\": 0, -- optional field, not present for imported/watchonly sources,\n" " \"addresses\": [\n" " \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\",\n" " ...\n" @@ -420,7 +420,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) for (const CTxDestination& dest : t_change_dests) { random_t_change_addrs.push_back(keyIO.EncodeDestination(dest)); } - random_t.pushKV("change_addresses", random_t_change_addrs); + random_t.pushKV("changeAddresses", random_t_change_addrs); hasData = true; } @@ -482,7 +482,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) std::optional accountId = libzcash::ParseZip32KeypathAccount(hdKeypath); if (accountId.has_value()) { - sapling_obj.pushKV("zip32_account_id", (uint64_t) accountId.value()); + sapling_obj.pushKV("zip32AccountId", (uint64_t) accountId.value()); } } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 812261db819..93cfda21468 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -818,7 +818,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { BOOST_CHECK_NO_THROW(list = CallRPC("listaddresses")); auto listarr = list.get_array(); bool sproutCountMatch = false; - bool saplingIvksMatch = false; + bool saplingExtfvksMatch = false; bool saplingAccount0 = false; bool saplingAccount1 = false; bool saplingCountMismatch = true; @@ -831,10 +831,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { } if (source.get_str() == "legacy_hdseed") { auto sapling_addr_sets = find_value(a.get_obj(), "sapling").get_array(); - saplingIvksMatch = (sapling_addr_sets.size() == 2); + saplingExtfvksMatch = (sapling_addr_sets.size() == 2); for (auto sapling_obj : sapling_addr_sets.getValues()) { - auto sapling_account = find_value(sapling_obj, "zip32_account_id").get_int(); + auto sapling_account = find_value(sapling_obj, "zip32AccountId").get_int(); saplingAccount0 |= (sapling_account == 0); saplingAccount1 |= (sapling_account == 1); auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); @@ -843,6 +843,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { } } BOOST_CHECK(sproutCountMatch); + BOOST_CHECK(saplingExtfvksMatch); BOOST_CHECK(!saplingCountMismatch); BOOST_CHECK(saplingAccount0); BOOST_CHECK(saplingAccount1); From 8f0f8b6687fd6d185cda112ea58c2dc28a931897 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Oct 2021 16:53:00 +0000 Subject: [PATCH 065/514] cargo update --- Cargo.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 030631c5b68..f2c60440176 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" dependencies = [ "cfg-if 1.0.0", ] @@ -838,9 +838,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.102" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" +checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" [[package]] name = "libm" @@ -1308,9 +1308,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -1532,9 +1532,9 @@ checksum = "76a77a8fd93886010f05e7ea0720e569d6d16c65329dbe3ec033bbbccccb017b" [[package]] name = "smallvec" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "socket2" @@ -1561,9 +1561,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.76" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" dependencies = [ "proc-macro2", "quote", @@ -1629,9 +1629,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986" +checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" dependencies = [ "tinyvec_macros", ] @@ -1658,9 +1658,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb" dependencies = [ "proc-macro2", "quote", @@ -1675,9 +1675,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1698,9 +1698,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ "proc-macro2", "quote", @@ -1709,18 +1709,18 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" dependencies = [ "lazy_static", ] [[package]] name = "tracing-subscriber" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd0568dbfe3baf7048b7908d2b32bca0d81cd56bec6d2a8f894b01d74f86be3" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ "ansi_term", "chrono", From 0263185a658124f3118f77376dc471c1cadbffe6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Oct 2021 16:54:36 +0000 Subject: [PATCH 066/514] depends: Postpone dependency updates We aren't going to move to Clang 13 in a hotfix release. The other dependencies passed their postponement re-evaluation date, but also won't be updated just yet. --- qa/zcash/postponed-updates.txt | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index 6ec3162f89d..e7186c32db8 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -5,21 +5,23 @@ # # Ccache 4.0 requires adding CMake to the depends system. -native_ccache 4.0 2021-10-01 -native_ccache 4.1 2021-10-01 -native_ccache 4.2 2021-10-01 -native_ccache 4.2.1 2021-10-01 -native_ccache 4.3 2021-10-01 -native_ccache 4.4 2021-10-01 -native_ccache 4.4.1 2021-10-01 -native_ccache 4.4.2 2021-10-01 +native_ccache 4.0 2021-11-01 +native_ccache 4.1 2021-11-01 +native_ccache 4.2 2021-11-01 +native_ccache 4.2.1 2021-11-01 +native_ccache 4.3 2021-11-01 +native_ccache 4.4 2021-11-01 +native_ccache 4.4.1 2021-11-01 +native_ccache 4.4.2 2021-11-01 # Clang is currently pinned to LLVM 12 +native_clang 13.0.0 2021-11-01 +libcxx 13.0.0 2021-11-01 # Rust is currently pinned to 1.55.0 bdb 18.1.40 2022-02-01 # Google Test 1.10.0 requires adding CMake to the depends system. -googletest 1.10.0 2021-10-01 -googletest 1.11.0 2021-10-01 +googletest 1.10.0 2021-11-01 +googletest 1.11.0 2021-11-01 From a4419b5f855ce427151e2a66b3de3aff9b1d1788 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Oct 2021 17:00:27 +0000 Subject: [PATCH 067/514] make-release.py: Versioning changes for 4.5.1-1. --- README.md | 2 +- configure.ac | 2 +- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4fd336db6c3..230a51221f5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.5.1 +Zcash 4.5.1-1 =========== diff --git a/configure.ac b/configure.ac index 3667cb55863..cbd7e8c6518 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) define(_CLIENT_VERSION_MINOR, 5) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 50) +define(_CLIENT_VERSION_BUILD, 51) 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))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 84960bba576..d29d0c5aab0 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.5.1" +name: "zcash-4.5.1-1" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index 9cdb0826812..326d068e792 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -18,7 +18,7 @@ #define CLIENT_VERSION_MAJOR 4 #define CLIENT_VERSION_MINOR 5 #define CLIENT_VERSION_REVISION 1 -#define CLIENT_VERSION_BUILD 50 +#define CLIENT_VERSION_BUILD 51 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true From 3a20ade75d1689ef05a9ad60721b0056482159c4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Oct 2021 17:03:31 +0000 Subject: [PATCH 068/514] make-release.py: Updated manpages for 4.5.1-1. --- doc/man/zcash-cli.1 | 6 +++--- doc/man/zcash-tx.1 | 6 +++--- doc/man/zcashd.1 | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 0fbd58d3af6..152d423b1c6 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "September 2021" "zcash-cli v4.5.1" "User Commands" +.TH ZCASH-CLI "1" "October 2021" "zcash-cli v4.5.1-1" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.5.1 +zcash-cli \- manual page for zcash-cli v4.5.1-1 .SH DESCRIPTION -Zcash RPC client version v4.5.1 +Zcash RPC client version v4.5.1\-1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 0f77ffb0bee..07d0a5b9e26 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "September 2021" "zcash-tx v4.5.1" "User Commands" +.TH ZCASH-TX "1" "October 2021" "zcash-tx v4.5.1-1" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.5.1 +zcash-tx \- manual page for zcash-tx v4.5.1-1 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.5.1 +Zcash zcash\-tx utility version v4.5.1\-1 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index 750334f9d27..d86cd12b850 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "September 2021" "zcashd v4.5.1" "User Commands" +.TH ZCASHD "1" "October 2021" "zcashd v4.5.1-1" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.5.1 +zcashd \- manual page for zcashd v4.5.1-1 .SH DESCRIPTION -Zcash Daemon version v4.5.1 +Zcash Daemon version v4.5.1\-1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -84,7 +84,7 @@ Keep at most unconnectable transactions in memory (default: 100) .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-6\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-16\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR From e2abf34cc984bc3bd44d68d021efeee3cd0959c1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Oct 2021 17:03:31 +0000 Subject: [PATCH 069/514] make-release.py: Updated release notes and changelog for 4.5.1-1. --- contrib/debian/changelog | 6 ++++ doc/authors.md | 4 +-- doc/release-notes.md | 6 ---- doc/release-notes/release-notes-4.5.1-1.md | 34 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 doc/release-notes/release-notes-4.5.1-1.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 6cde32c45d1..244daae2180 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.5.1+1) stable; urgency=medium + + * 4.5.1-1 release. + + -- Electric Coin Company Fri, 08 Oct 2021 17:03:31 +0000 + zcash (4.5.1) stable; urgency=medium * 4.5.1 release. diff --git a/doc/authors.md b/doc/authors.md index bac62175b70..60093355b8c 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,12 +1,12 @@ Zcash Contributors ================== -Jack Grigg (1117) +Jack Grigg (1123) Simon Liu (460) Sean Bowe (367) Daira Hopwood (270) Eirik Ogilvie-Wigley (216) -Kris Nuttycombe (163) +Kris Nuttycombe (174) Wladimir J. van der Laan (150) Alfredo Garcia (116) Taylor Hornby (114) diff --git a/doc/release-notes.md b/doc/release-notes.md index 51e0d75eacd..a29094b5174 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,9 +4,3 @@ release-notes at release time) Notable changes =============== -The `listaddresses` endpoint has been added to the RPC API. This method -allows the caller to obtain addresses managed by the wallet, grouped -by the source of the address, including both those addresses generated -by the wallet and those associated with imported viewing or spending -keys. This provides functionality that replaces and subsumes the -previously-removed `getaddressesbyaccount` method. diff --git a/doc/release-notes/release-notes-4.5.1-1.md b/doc/release-notes/release-notes-4.5.1-1.md new file mode 100644 index 00000000000..994ef49bf3b --- /dev/null +++ b/doc/release-notes/release-notes-4.5.1-1.md @@ -0,0 +1,34 @@ +Notable changes +=============== + +The `listaddresses` endpoint has been added to the RPC API. This method +allows the caller to obtain addresses managed by the wallet, grouped +by the source of the address, including both those addresses generated +by the wallet and those associated with imported viewing or spending +keys. This provides functionality that replaces and subsumes the +previously-removed `getaddressesbyaccount` method. + +Changelog +========= + +Jack Grigg (6): + Mark v5 transaction format as standard for NU5 + Fix comment + cargo update + depends: Postpone dependency updates + make-release.py: Versioning changes for 4.5.1-1. + make-release.py: Updated manpages for 4.5.1-1. + +Kris Nuttycombe (11): + Add `listaddresses` RPC method. + Categorize listaddresses result by source type. + Correctly handle imported Sapling addresses + Apply suggestions from code review + Apply suggestions from code review + Group legacy_hdseed Sapling addresses by account ID. + Update release notes for v4.5.1-1 to reflect the addition of `listaddresses` + Include `ImportedWatchOnly` as a PaymentAddressSource result. + Add listaddresses check to wallet_addresses.py + Consistently group Sapling addresses by IVK for every source. + Use lowerCamelCase for listaddresses JSON + From d397f4e36d0c9e55b99699639844f00626c475f7 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Oct 2021 17:07:24 +0000 Subject: [PATCH 070/514] Update v4.5.1-1 release notes with bugfix --- doc/release-notes/release-notes-4.5.1-1.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/release-notes/release-notes-4.5.1-1.md b/doc/release-notes/release-notes-4.5.1-1.md index 994ef49bf3b..f7847c3bcdd 100644 --- a/doc/release-notes/release-notes-4.5.1-1.md +++ b/doc/release-notes/release-notes-4.5.1-1.md @@ -1,6 +1,18 @@ Notable changes =============== +Added v5 transactions to standard rules +--------------------------------------- + +In v4.5.0 we added the v5 transaction format to the NU5 consensus rules for +testnet. However, it was omitted from the standard rules, which meant that +`zcashd` testnet nodes would not accept v5 transactions into their mempools, +causing them to not be propagated or mined. This release updates the `zcashd` +standard rules to accept v5 transactions alongside v4 transactions. + +New `listaddresses` RPC method +------------------------------ + The `listaddresses` endpoint has been added to the RPC API. This method allows the caller to obtain addresses managed by the wallet, grouped by the source of the address, including both those addresses generated From aac77dd284aaf82d4e6d1ca920c61cea8daee5f7 Mon Sep 17 00:00:00 2001 From: Alex Wied Date: Sun, 10 Oct 2021 22:31:40 -0400 Subject: [PATCH 071/514] Update support for FreeBSD --- depends/builders/freebsd.mk | 2 ++ depends/packages/native_clang.mk | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/depends/builders/freebsd.mk b/depends/builders/freebsd.mk index fb2fefe2a2e..7a61043e93e 100644 --- a/depends/builders/freebsd.mk +++ b/depends/builders/freebsd.mk @@ -1,2 +1,4 @@ +build_freebsd_CC = clang +build_freebsd_CXX = clang++ build_freebsd_SHA256SUM = shasum -a 256 build_freebsd_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o diff --git a/depends/packages/native_clang.mk b/depends/packages/native_clang.mk index 4db1a43390a..b3cc5b65a84 100644 --- a/depends/packages/native_clang.mk +++ b/depends/packages/native_clang.mk @@ -11,9 +11,9 @@ $(package)_download_file_darwin=clang+llvm-$($(package)_major_version).0.0-x86_6 $(package)_file_name_darwin=clang-llvm-$($(package)_major_version).0.0-x86_64-apple-darwin.tar.xz $(package)_sha256_hash_darwin=7bc2259bf75c003f644882460fc8e844ddb23b27236fe43a2787870a4cd8ab50 $(package)_download_path_freebsd=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) -$(package)_download_file_freebsd=clang+llvm-$($(package)_version)-amd64-unknown-freebsd11.tar.xz -$(package)_file_name_freebsd=clang-llvm-$($(package)_version)-amd64-unknown-freebsd11.tar.xz -$(package)_sha256_hash_freebsd=94dfe48d9e483283edbee968056d487a850b30de25258fa48f049cca3ede5db4 +$(package)_download_file_freebsd=clang+llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz +$(package)_file_name_freebsd=clang-llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz +$(package)_sha256_hash_freebsd=38857da36489880b0504ae7142b74abe41cf18711a6bb25ca96792d8190e8b0e $(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz $(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz From 58e4a76e60fdfe0920b94e08638fad8a15f24791 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 4 Oct 2021 23:27:10 -0600 Subject: [PATCH 072/514] better wallet network info error handling --- src/wallet/gtest/test_wallet.cpp | 10 ++++++++-- src/wallet/wallet.cpp | 14 ++++++-------- src/wallet/wallet.h | 2 +- src/wallet/walletdb.cpp | 19 ++++++++++++------- src/wallet/walletdb.h | 3 ++- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 4a47804b31c..edc06058558 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -138,9 +138,15 @@ TEST(WalletTests, WalletNetworkSerialization) { // create a new testnet wallet and generate a seed CWallet wallet(Params(), "wallet.dat"); wallet.InitLoadWallet(Params(), true); - wallet.GenerateNewSeed(); wallet.Flush(); + // Stay on TESTNET, make sure wallet can be successfully loaded. + { + CWallet restored(Params(), "wallet.dat"); + bool fFirstRunRet; + EXPECT_EQ(restored.LoadWallet(fFirstRunRet), DB_LOAD_OK); + } + // now, switch to mainnet and attempt to restore the wallet // using the same wallet.dat SelectParams(CBaseChainParams::MAIN); @@ -148,7 +154,7 @@ TEST(WalletTests, WalletNetworkSerialization) { // load should fail due to being associated with the wrong network bool fFirstRunRet; - EXPECT_NE(restored.LoadWallet(fFirstRunRet), DB_LOAD_OK); + EXPECT_EQ(restored.LoadWallet(fFirstRunRet), DB_WRONG_NETWORK); } TEST(WalletTests, SproutNoteDataSerialisation) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7d26262c43f..f8fc1d30f69 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2295,17 +2295,11 @@ void CWallet::SetHDChain(const CHDChain& chain, bool memonly) hdChain = chain; } -void CWallet::CheckNetworkInfo(std::pair readNetworkInfo) +bool CWallet::CheckNetworkInfo(std::pair readNetworkInfo) { LOCK(cs_wallet); std::pair networkInfo(PACKAGE_NAME, networkIdString); - if (readNetworkInfo != networkInfo) - throw std::runtime_error( - strprintf("%s: this wallet is for a different network (%s, %s) than the node is configured for (%s, %s)", - std::string(__func__), - readNetworkInfo.first, readNetworkInfo.second, - networkInfo.first, networkInfo.second) - ); + return readNetworkInfo == networkInfo; } uint32_t CWallet::BIP44CoinType() { @@ -4773,6 +4767,10 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches { return UIError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); } + else if (nLoadWalletRet == DB_WRONG_NETWORK) + { + return UIError(strprintf(_("Wallet %s is not for %s %s network"), walletFile, _(PACKAGE_NAME), params.NetworkIDString())); + } else return UIError(strprintf(_("Error loading %s"), walletFile)); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1bd9f16825a..b0d5c2df7fc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1298,7 +1298,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SetHDChain(const CHDChain& chain, bool memonly); const CHDChain& GetHDChain() const { return hdChain; } - void CheckNetworkInfo(std::pair networkInfo); + bool CheckNetworkInfo(std::pair networkInfo); uint32_t BIP44CoinType(); /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 7ac13f32af9..2793a9cf5af 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -728,7 +728,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { std::pair networkInfo; ssValue >> networkInfo; - pwallet->CheckNetworkInfo(networkInfo); + if (!pwallet->CheckNetworkInfo(networkInfo)) { + strErr = "Error in wallet database: unexpected network"; + return false; + } } } catch (...) { @@ -790,17 +793,19 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) string strType, strErr; if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { - // losing keys is considered a catastrophic error, anything else - // we assume the user can live with: - if (IsKeyType(strType)) + if (strType == "networkinfo") { + // example: running mainnet, but this wallet.dat is from testnet + result = DB_WRONG_NETWORK; + } else if (result != DB_WRONG_NETWORK && IsKeyType(strType)) { + // losing keys is considered a catastrophic error result = DB_CORRUPT; - else - { + } else { // Leave other errors alone, if we try to fix them we might make things worse. fNoncriticalErrors = true; // ... but do warn the user there is something wrong. - if (strType == "tx") + if (strType == "tx") { // Rescan if there is a bad transaction record: SoftSetBoolArg("-rescan", true); + } } } if (!strErr.empty()) diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 5d0a2d0debf..5518e38d988 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -37,7 +37,8 @@ enum DBErrors DB_NONCRITICAL_ERROR, DB_TOO_NEW, DB_LOAD_FAIL, - DB_NEED_REWRITE + DB_NEED_REWRITE, + DB_WRONG_NETWORK, }; /* simple hd chain data model */ From 0e685b67dabfe6cc47474ec9272ab4b65b27046f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 13 Oct 2021 13:20:40 -0600 Subject: [PATCH 073/514] Fix wallet-related wording in doc/reduce-traffic.md --- doc/reduce-traffic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md index 12b5c792b32..e5ccd8ca299 100644 --- a/doc/reduce-traffic.md +++ b/doc/reduce-traffic.md @@ -46,8 +46,8 @@ Be reminded of the effects of this setting. - Fee estimation will no longer work. - It sets the flag "-walletbroadcast" to be "0", only if it is currently unset. - Doing so disables the automatic broadcasting of transactions from a wallet. Not - relaying other's transactions could hurt your privacy if used while a wallet + Doing so disables the automatic broadcasting of transactions from the wallet. Not + relaying other's transactions could hurt your privacy if used while the wallet is loaded or if you use the node to broadcast transactions. - If a peer is whitelisted and "-whitelistforcerelay" is set to "1" (which will also set "whitelistrelay" to "1"), we will still receive and relay their transactions. From 5677649af1eee6eaeb1651fa5f12b88f3a7b9f7b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 19 Aug 2021 15:32:24 -0600 Subject: [PATCH 074/514] Derive random HD seeds from ZIP-339 seed phrases. Add support for dump of legacy HD seeds & persistence of mnemonic-based seeds. --- src/gtest/test_keystore.cpp | 98 ++++++----- src/gtest/test_zip32.cpp | 3 + src/keystore.cpp | 43 +++-- src/keystore.h | 50 +++--- src/utiltest.cpp | 4 +- src/wallet/crypter.cpp | 222 ++++++++++++++++++++----- src/wallet/crypter.h | 18 +- src/wallet/gtest/test_wallet_zkeys.cpp | 10 +- src/wallet/rpcdump.cpp | 42 +++-- src/wallet/test/rpc_wallet_tests.cpp | 14 +- src/wallet/wallet.cpp | 54 +++--- src/wallet/wallet.h | 21 ++- src/wallet/walletdb.cpp | 45 ++++- src/wallet/walletdb.h | 9 +- src/zcash/address/zip32.cpp | 17 +- src/zcash/address/zip32.h | 107 +++++++++++- 16 files changed, 557 insertions(+), 200 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index b61ba909ac9..f856877fa44 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -16,35 +16,37 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeed) { CBasicKeyStore keyStore; - HDSeed seedOut; // When we haven't set a seed, we shouldn't get one - EXPECT_FALSE(keyStore.HaveHDSeed()); - EXPECT_FALSE(keyStore.GetHDSeed(seedOut)); + EXPECT_FALSE(keyStore.HaveMnemonicSeed()); + auto seedOut = keyStore.GetMnemonicSeed(); + EXPECT_FALSE(seedOut.has_value()); // Generate a random seed - auto seed = HDSeed::Random(); + auto seed = MnemonicSeed::Random(); // We should be able to set and retrieve the seed - ASSERT_TRUE(keyStore.SetHDSeed(seed)); - EXPECT_TRUE(keyStore.HaveHDSeed()); - ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); - EXPECT_EQ(seed, seedOut); + ASSERT_TRUE(keyStore.SetMnemonicSeed(seed)); + EXPECT_TRUE(keyStore.HaveMnemonicSeed()); + seedOut = keyStore.GetMnemonicSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); // Generate another random seed - auto seed2 = HDSeed::Random(); + auto seed2 = MnemonicSeed::Random(); EXPECT_NE(seed, seed2); // We should not be able to set and retrieve a different seed - EXPECT_FALSE(keyStore.SetHDSeed(seed2)); - ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); - EXPECT_EQ(seed, seedOut); + EXPECT_FALSE(keyStore.SetMnemonicSeed(seed2)); + seedOut = keyStore.GetMnemonicSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); } TEST(KeystoreTests, SaplingKeys) { // ["sk, ask, nsk, ovk, ak, nk, ivk, default_d, default_pk_d, note_v, note_r, note_cm, note_pos, note_nf"], UniValue sapling_keys = read_json(MAKE_STRING(json_tests::sapling_key_components)); - + // Skipping over comments in sapling_key_components.json file for (size_t i = 2; i < 12; i++) { uint256 skSeed, ask, nsk, ovk, ak, nk, ivk; @@ -55,40 +57,40 @@ TEST(KeystoreTests, SaplingKeys) { ak.SetHex(sapling_keys[i][4].getValStr()); nk.SetHex(sapling_keys[i][5].getValStr()); ivk.SetHex(sapling_keys[i][6].getValStr()); - + libzcash::diversifier_t default_d; std::copy_n(ParseHex(sapling_keys[i][7].getValStr()).begin(), 11, default_d.begin()); - + uint256 default_pk_d; default_pk_d.SetHex(sapling_keys[i][8].getValStr()); - + auto sk = libzcash::SaplingSpendingKey(skSeed); - + // Check that expanded spending key from primitives and from sk are the same auto exp_sk_2 = libzcash::SaplingExpandedSpendingKey(ask, nsk, ovk); auto exp_sk = sk.expanded_spending_key(); EXPECT_EQ(exp_sk, exp_sk_2); - + // Check that full viewing key derived from sk and expanded sk are the same auto full_viewing_key = sk.full_viewing_key(); EXPECT_EQ(full_viewing_key, exp_sk.full_viewing_key()); - + // Check that full viewing key from primitives and from sk are the same auto full_viewing_key_2 = libzcash::SaplingFullViewingKey(ak, nk, ovk); EXPECT_EQ(full_viewing_key, full_viewing_key_2); - + // Check that incoming viewing key from primitives and from sk are the same auto in_viewing_key = full_viewing_key.in_viewing_key(); auto in_viewing_key_2 = libzcash::SaplingIncomingViewingKey(ivk); EXPECT_EQ(in_viewing_key, in_viewing_key_2); - + // Check that the default address from primitives and from sk method are the same auto default_addr = sk.default_address(); auto addrOpt2 = in_viewing_key.address(default_d); EXPECT_TRUE(addrOpt2); auto default_addr_2 = addrOpt2.value(); EXPECT_EQ(default_addr, default_addr_2); - + auto default_addr_3 = libzcash::SaplingPaymentAddress(default_d, default_pk_d); EXPECT_EQ(default_addr_2, default_addr_3); EXPECT_EQ(default_addr, default_addr_3); @@ -285,20 +287,22 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { TestCCryptoKeyStore keyStore; CKeyingMaterial vMasterKey(32, 0); GetRandBytes(vMasterKey.data(), 32); - HDSeed seedOut; // 1) Test adding a seed to an unencrypted key store, then encrypting it - auto seed = HDSeed::Random(); - EXPECT_FALSE(keyStore.HaveHDSeed()); - EXPECT_FALSE(keyStore.GetHDSeed(seedOut)); + auto seed = MnemonicSeed::Random(); + EXPECT_FALSE(keyStore.HaveMnemonicSeed()); + auto seedOut = keyStore.GetMnemonicSeed(); + EXPECT_FALSE(seedOut.has_value()); - ASSERT_TRUE(keyStore.SetHDSeed(seed)); - EXPECT_TRUE(keyStore.HaveHDSeed()); - ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); - EXPECT_EQ(seed, seedOut); + ASSERT_TRUE(keyStore.SetMnemonicSeed(seed)); + EXPECT_TRUE(keyStore.HaveMnemonicSeed()); + seedOut = keyStore.GetMnemonicSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey)); - EXPECT_FALSE(keyStore.GetHDSeed(seedOut)); + seedOut = keyStore.GetMnemonicSeed(); + EXPECT_FALSE(seedOut.has_value()); // Unlocking with a random key should fail CKeyingMaterial vRandomKey(32, 0); @@ -312,15 +316,17 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { // Unlocking with vMasterKey should succeed ASSERT_TRUE(keyStore.Unlock(vMasterKey)); - ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); - EXPECT_EQ(seed, seedOut); + seedOut = keyStore.GetMnemonicSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); // 2) Test replacing the seed in an already-encrypted key store fails - auto seed2 = HDSeed::Random(); - EXPECT_FALSE(keyStore.SetHDSeed(seed2)); - EXPECT_TRUE(keyStore.HaveHDSeed()); - ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); - EXPECT_EQ(seed, seedOut); + auto seed2 = MnemonicSeed::Random(); + EXPECT_FALSE(keyStore.SetMnemonicSeed(seed2)); + EXPECT_TRUE(keyStore.HaveMnemonicSeed()); + seedOut = keyStore.GetMnemonicSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); // 3) Test adding a new seed to an already-encrypted key store TestCCryptoKeyStore keyStore2; @@ -331,14 +337,16 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { ASSERT_TRUE(keyStore2.EncryptKeys(vMasterKey)); ASSERT_TRUE(keyStore2.Unlock(vMasterKey)); - EXPECT_FALSE(keyStore2.HaveHDSeed()); - EXPECT_FALSE(keyStore2.GetHDSeed(seedOut)); + EXPECT_FALSE(keyStore2.HaveMnemonicSeed()); + seedOut = keyStore2.GetMnemonicSeed(); + EXPECT_FALSE(seedOut.has_value()); - auto seed3 = HDSeed::Random(); - ASSERT_TRUE(keyStore2.SetHDSeed(seed3)); - EXPECT_TRUE(keyStore2.HaveHDSeed()); - ASSERT_TRUE(keyStore2.GetHDSeed(seedOut)); - EXPECT_EQ(seed3, seedOut); + auto seed3 = MnemonicSeed::Random(); + ASSERT_TRUE(keyStore2.SetMnemonicSeed(seed3)); + EXPECT_TRUE(keyStore2.HaveMnemonicSeed()); + seedOut = keyStore2.GetMnemonicSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed3, seedOut.value()); } TEST(KeystoreTests, StoreAndRetrieveSpendingKeyInEncryptedStore) { diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index c8ee5a3350d..17c80b6fff4 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -11,6 +11,9 @@ TEST(ZIP32, TestVectors) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; HDSeed seed(rawSeed); + // TODO: Regenerate test vectors using a BIP-44-derived seed. + // std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + // HDSeed seed(English, mnemonic); auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); EXPECT_EQ(m.depth, 0); diff --git a/src/keystore.cpp b/src/keystore.cpp index e39a0d6dbf5..d9542993d18 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -30,33 +30,50 @@ bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) con return true; } -bool CBasicKeyStore::SetHDSeed(const HDSeed& seed) +bool CBasicKeyStore::SetMnemonicSeed(const MnemonicSeed& seed) { LOCK(cs_KeyStore); - if (!hdSeed.IsNull()) { + if (mnemonicSeed.has_value()) { // Don't allow an existing seed to be changed. We can maybe relax this // restriction later once we have worked out the UX implications. return false; } - hdSeed = seed; + mnemonicSeed = seed; return true; } -bool CBasicKeyStore::HaveHDSeed() const +bool CBasicKeyStore::HaveMnemonicSeed() const { LOCK(cs_KeyStore); - return !hdSeed.IsNull(); + return mnemonicSeed.has_value(); } -bool CBasicKeyStore::GetHDSeed(HDSeed& seedOut) const +std::optional CBasicKeyStore::GetMnemonicSeed() const { LOCK(cs_KeyStore); - if (hdSeed.IsNull()) { + return mnemonicSeed; +} + +bool CBasicKeyStore::SetLegacyHDSeed(const HDSeed& seed) +{ + if (legacySeed.has_value()) { + // Don't allow an existing seed to be changed. return false; - } else { - seedOut = hdSeed; - return true; } + legacySeed = seed; + return true; +} + +bool CBasicKeyStore::HaveLegacyHDSeed() const +{ + LOCK(cs_KeyStore); + return legacySeed.has_value(); +} + +std::optional CBasicKeyStore::GetLegacyHDSeed() const +{ + LOCK(cs_KeyStore); + return legacySeed; } bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) @@ -151,7 +168,7 @@ bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) return true; } -//! Sapling +//! Sapling bool CBasicKeyStore::AddSaplingSpendingKey( const libzcash::SaplingExtendedSpendingKey &sk) { @@ -187,7 +204,7 @@ bool CBasicKeyStore::AddSaplingFullViewingKey( return CBasicKeyStore::AddSaplingIncomingViewingKey(ivk, extfvk.DefaultAddress()); } -// This function updates the wallet's internal address->ivk map. +// This function updates the wallet's internal address->ivk map. // If we add an address that is already in the map, the map will // remain unchanged as each address only has one ivk. bool CBasicKeyStore::AddSaplingIncomingViewingKey( @@ -265,7 +282,7 @@ bool CBasicKeyStore::GetSaplingIncomingViewingKey(const libzcash::SaplingPayment return false; } -bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymentAddress &addr, +bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymentAddress &addr, libzcash::SaplingExtendedSpendingKey &extskOut) const { libzcash::SaplingIncomingViewingKey ivk; libzcash::SaplingExtendedFullViewingKey extfvk; diff --git a/src/keystore.h b/src/keystore.h index 8246461aaed..1774d37bf34 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -25,11 +25,16 @@ class CKeyStore public: virtual ~CKeyStore() {} - //! Set the HD seed for this keystore - virtual bool SetHDSeed(const HDSeed& seed) =0; - virtual bool HaveHDSeed() const =0; - //! Get the HD seed for this keystore - virtual bool GetHDSeed(HDSeed& seedOut) const =0; + //! Set the mnemonic HD seed for this keystore + virtual bool SetMnemonicSeed(const MnemonicSeed& seed) =0; + virtual bool HaveMnemonicSeed() const =0; + //! Get the mnemonic HD seed for this keystore + virtual std::optional GetMnemonicSeed() const =0; + + //! Set the legacy HD seed for this keystore + virtual bool SetLegacyHDSeed(const HDSeed& seed) =0; + //! Get the legacy HD seed for this keystore + virtual std::optional GetLegacyHDSeed() const =0; //! Add a key to the store. virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0; @@ -59,10 +64,10 @@ class CKeyStore virtual bool HaveSproutSpendingKey(const libzcash::SproutPaymentAddress &address) const =0; virtual bool GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey& skOut) const =0; virtual void GetSproutPaymentAddresses(std::set &setAddress) const =0; - + //! Add a Sapling spending key to the store. virtual bool AddSaplingSpendingKey(const libzcash::SaplingExtendedSpendingKey &sk) =0; - + //! Check whether a Sapling spending key corresponding to a given Sapling viewing key is present in the store. virtual bool HaveSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk) const =0; @@ -74,16 +79,16 @@ class CKeyStore virtual bool AddSaplingFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk) =0; virtual bool HaveSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk) const =0; virtual bool GetSaplingFullViewingKey( - const libzcash::SaplingIncomingViewingKey &ivk, + const libzcash::SaplingIncomingViewingKey &ivk, libzcash::SaplingExtendedFullViewingKey& extfvkOut) const =0; - //! Sapling incoming viewing keys + //! Sapling incoming viewing keys virtual bool AddSaplingIncomingViewingKey( const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingPaymentAddress &addr) =0; virtual bool HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymentAddress &addr) const =0; virtual bool GetSaplingIncomingViewingKey( - const libzcash::SaplingPaymentAddress &addr, + const libzcash::SaplingPaymentAddress &addr, libzcash::SaplingIncomingViewingKey& ivkOut) const =0; virtual void GetSaplingPaymentAddresses(std::set &setAddress) const =0; @@ -112,14 +117,15 @@ typedef std::map< typedef std::map< libzcash::SaplingIncomingViewingKey, libzcash::SaplingExtendedFullViewingKey> SaplingFullViewingKeyMap; -// Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses. +// Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses. typedef std::map SaplingIncomingViewingKeyMap; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore { protected: - HDSeed hdSeed; + std::optional mnemonicSeed; + std::optional legacySeed; KeyMap mapKeys; WatchKeyMap mapWatchKeys; ScriptMap mapScripts; @@ -133,9 +139,13 @@ class CBasicKeyStore : public CKeyStore SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys; public: - bool SetHDSeed(const HDSeed& seed); - bool HaveHDSeed() const; - bool GetHDSeed(HDSeed& seedOut) const; + bool SetMnemonicSeed(const MnemonicSeed& seed); + bool HaveMnemonicSeed() const; + std::optional GetMnemonicSeed() const; + + bool SetLegacyHDSeed(const HDSeed& seed); + bool HaveLegacyHDSeed() const; + std::optional GetLegacyHDSeed() const; bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; @@ -235,7 +245,7 @@ class CBasicKeyStore : public CKeyStore } } - //! Sapling + //! Sapling bool AddSaplingSpendingKey(const libzcash::SaplingExtendedSpendingKey &sk); bool HaveSaplingSpendingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk) const { @@ -274,13 +284,13 @@ class CBasicKeyStore : public CKeyStore const libzcash::SaplingPaymentAddress &addr); virtual bool HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymentAddress &addr) const; virtual bool GetSaplingIncomingViewingKey( - const libzcash::SaplingPaymentAddress &addr, + const libzcash::SaplingPaymentAddress &addr, libzcash::SaplingIncomingViewingKey& ivkOut) const; bool GetSaplingExtendedSpendingKey( - const libzcash::SaplingPaymentAddress &addr, + const libzcash::SaplingPaymentAddress &addr, libzcash::SaplingExtendedSpendingKey &extskOut) const; - + void GetSaplingPaymentAddresses(std::set &setAddress) const { setAddress.clear(); @@ -307,7 +317,7 @@ typedef std::vector > CKeyingMate typedef std::map > > CryptedKeyMap; typedef std::map > CryptedSproutSpendingKeyMap; -//! Sapling +//! Sapling typedef std::map > CryptedSaplingSpendingKeyMap; #endif // BITCOIN_KEYSTORE_H diff --git a/src/utiltest.cpp b/src/utiltest.cpp index 26d0cfb3ed2..ac404ed794b 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -316,8 +316,8 @@ void RegtestDeactivateNU5() { } libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() { - std::vector> rawSeed(32); - HDSeed seed(rawSeed); + std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + MnemonicSeed seed(English, mnemonic); return libzcash::SaplingExtendedSpendingKey::Master(seed); } diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 10aacb37188..91180317e43 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -130,21 +130,56 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector DecryptMnemonicSeed( const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, - const uint256& seedFp, - HDSeed& seed) + const uint256& seedFp) { CKeyingMaterial vchSecret; // Use seed's fingerprint as IV // TODO: Handle IV properly when we make encryption a supported feature - if(!DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) - return false; + if(DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) { + CSecureDataStream ss(vchSecret, SER_NETWORK, PROTOCOL_VERSION); + auto seed = MnemonicSeed::Read(ss); + if (seed.Fingerprint() == seedFp) { + return seed; + } else { + return std::nullopt; + } + } else { + return std::nullopt; + } +} - seed = HDSeed(vchSecret); - return seed.Fingerprint() == seedFp; +static std::optional DecryptLegacyHDSeed( + const CKeyingMaterial& vMasterKey, + const std::vector& vchCryptedSecret, + const uint256& seedFp) +{ + CKeyingMaterial vchSecret; + + // Use seed's fingerprint as IV + // TODO: Handle IV properly when we make encryption a supported feature + if(DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) { + auto seed = HDSeed(vchSecret); + if (seed.Fingerprint() == seedFp) { + return seed; + } else { + return std::nullopt; + } + } else { + return std::nullopt; + } } static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) @@ -227,9 +262,22 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) bool keyPass = false; bool keyFail = false; - if (!cryptedHDSeed.first.IsNull()) { - HDSeed seed; - if (!DecryptHDSeed(vMasterKeyIn, cryptedHDSeed.second, cryptedHDSeed.first, seed)) + if (!cryptedMnemonicSeed.first.IsNull()) { + // Check that we can successfully decrypt the mnemonic seed, if present + auto seed = DecryptMnemonicSeed(vMasterKeyIn, cryptedMnemonicSeed.second, cryptedMnemonicSeed.first); + if (!seed.has_value()) + { + keyFail = true; + } else { + keyPass = true; + } + } + if (cryptedLegacySeed.has_value()) { + // Check that we can successfully decrypt the legacy seed, if present + auto seed = DecryptLegacyHDSeed(vMasterKeyIn, + cryptedLegacySeed.value().second, + cryptedLegacySeed.value().first); + if (!seed.has_value()) { keyFail = true; } else { @@ -295,71 +343,146 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) return true; } -bool CCryptoKeyStore::SetHDSeed(const HDSeed& seed) +// +// Mnemonic HD seeds +// + +bool CCryptoKeyStore::SetMnemonicSeed(const MnemonicSeed& seed) { { LOCK(cs_KeyStore); if (!fUseCrypto) { - return CBasicKeyStore::SetHDSeed(seed); + return CBasicKeyStore::SetMnemonicSeed(seed); } if (IsLocked()) return false; - std::vector vchCryptedSecret; // Use seed's fingerprint as IV // TODO: Handle this properly when we make encryption a supported feature auto seedFp = seed.Fingerprint(); - if (!EncryptSecret(vMasterKey, seed.RawSeed(), seedFp, vchCryptedSecret)) + CKeyingMaterial vchSecret = EncryptMnemonicSeed(seed); + + std::vector vchCryptedSecret; + if (!EncryptSecret(vMasterKey, vchSecret, seedFp, vchCryptedSecret)) return false; // This will call into CWallet to store the crypted seed to disk - if (!SetCryptedHDSeed(seedFp, vchCryptedSecret)) + if (!SetCryptedMnemonicSeed(seedFp, vchCryptedSecret)) return false; } return true; } -bool CCryptoKeyStore::SetCryptedHDSeed( +bool CCryptoKeyStore::SetCryptedMnemonicSeed( const uint256& seedFp, const std::vector& vchCryptedSecret) { LOCK(cs_KeyStore); - if (!fUseCrypto) { + if (!fUseCrypto || !cryptedMnemonicSeed.first.IsNull()) { + // Require encryption & don't allow an existing seed to be changed. return false; + } else { + cryptedMnemonicSeed = std::make_pair(seedFp, vchCryptedSecret); + return true; } +} - if (!cryptedHDSeed.first.IsNull()) { - // Don't allow an existing seed to be changed. We can maybe relax this - // restriction later once we have worked out the UX implications. - return false; +bool CCryptoKeyStore::HaveMnemonicSeed() const +{ + LOCK(cs_KeyStore); + if (fUseCrypto) { + return !cryptedMnemonicSeed.second.empty(); + } else { + return CBasicKeyStore::HaveMnemonicSeed(); } - - cryptedHDSeed = std::make_pair(seedFp, vchCryptedSecret); - return true; } -bool CCryptoKeyStore::HaveHDSeed() const +std::optional CCryptoKeyStore::GetMnemonicSeed() const { LOCK(cs_KeyStore); - if (!fUseCrypto) - return CBasicKeyStore::HaveHDSeed(); + if (fUseCrypto) { + if (cryptedMnemonicSeed.second.empty()) { + return std::nullopt; + } else { + return DecryptMnemonicSeed(vMasterKey, cryptedMnemonicSeed.second, cryptedMnemonicSeed.first); + } + } else { + return CBasicKeyStore::GetMnemonicSeed(); + } +} + +// +// Legacy HD seeds +// + +bool CCryptoKeyStore::SetLegacyHDSeed(const HDSeed& seed) +{ + { + LOCK(cs_KeyStore); + if (!fUseCrypto) { + return CBasicKeyStore::SetLegacyHDSeed(seed); + } + + if (IsLocked()) + return false; - return !cryptedHDSeed.second.empty(); + // Use seed's fingerprint as IV + // TODO: Handle this properly when we make encryption a supported feature + auto seedFp = seed.Fingerprint(); + std::vector vchCryptedSecret; + if (!EncryptSecret(vMasterKey, seed.RawSeed(), seedFp, vchCryptedSecret)) + return false; + + // This will call into CWallet to store the crypted seed to disk + if (!SetCryptedLegacyHDSeed(seedFp, vchCryptedSecret)) + return false; + } + return true; } -bool CCryptoKeyStore::GetHDSeed(HDSeed& seedOut) const +bool CCryptoKeyStore::SetCryptedLegacyHDSeed( + const uint256& seedFp, + const std::vector& vchCryptedSecret) { LOCK(cs_KeyStore); - if (!fUseCrypto) - return CBasicKeyStore::GetHDSeed(seedOut); - - if (cryptedHDSeed.second.empty()) + if (!fUseCrypto || cryptedLegacySeed.has_value()) { + // Require encryption & don't allow an existing seed to be changed. return false; + } else { + cryptedLegacySeed = std::make_pair(seedFp, vchCryptedSecret); + return true; + } +} + +bool CCryptoKeyStore::HaveLegacyHDSeed() const +{ + LOCK(cs_KeyStore); + if (fUseCrypto) { + return cryptedLegacySeed.has_value(); + } else { + return CBasicKeyStore::HaveLegacyHDSeed(); + } +} - return DecryptHDSeed(vMasterKey, cryptedHDSeed.second, cryptedHDSeed.first, seedOut); +std::optional CCryptoKeyStore::GetLegacyHDSeed() const { + LOCK(cs_KeyStore); + if (fUseCrypto) { + if (cryptedLegacySeed.has_value()) { + const auto cryptedPair = cryptedLegacySeed.value(); + return DecryptLegacyHDSeed(vMasterKey, cryptedPair.second, cryptedPair.first); + } else { + return std::nullopt; + } + } else { + return CBasicKeyStore::GetLegacyHDSeed(); + } } +// +// Transparent public keys +// + bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { LOCK(cs_KeyStore); @@ -420,6 +543,10 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); } +// +// Sprout & Sapling keys +// + bool CCryptoKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) { LOCK(cs_KeyStore); @@ -536,21 +663,38 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) return false; fUseCrypto = true; - if (!hdSeed.IsNull()) { + if (mnemonicSeed.has_value()) { + { + std::vector vchCryptedSecret; + // Use seed's fingerprint as IV + // TODO: Handle this properly when we make encryption a supported feature + auto seedFp = mnemonicSeed.value().Fingerprint(); + auto seedData = EncryptMnemonicSeed(mnemonicSeed.value()); + if (!EncryptSecret(vMasterKeyIn, seedData, seedFp, vchCryptedSecret)) { + return false; + } + // This will call into CWallet to store the crypted seed to disk + if (!SetCryptedMnemonicSeed(seedFp, vchCryptedSecret)) { + return false; + } + } + mnemonicSeed = std::nullopt; + } + if (legacySeed.has_value()) { { std::vector vchCryptedSecret; // Use seed's fingerprint as IV // TODO: Handle this properly when we make encryption a supported feature - auto seedFp = hdSeed.Fingerprint(); - if (!EncryptSecret(vMasterKeyIn, hdSeed.RawSeed(), seedFp, vchCryptedSecret)) { + auto seedFp = legacySeed.value().Fingerprint(); + if (!EncryptSecret(vMasterKeyIn, legacySeed.value().RawSeed(), seedFp, vchCryptedSecret)) { return false; } // This will call into CWallet to store the crypted seed to disk - if (!SetCryptedHDSeed(seedFp, vchCryptedSecret)) { + if (!SetCryptedLegacyHDSeed(seedFp, vchCryptedSecret)) { return false; } } - hdSeed = HDSeed(); + legacySeed = std::nullopt; } for (KeyMap::value_type& mKey : mapKeys) { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index f6cd738093b..6a11c5a527b 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -131,7 +131,8 @@ friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV class CCryptoKeyStore : public CBasicKeyStore { private: - std::pair> cryptedHDSeed; + std::pair> cryptedMnemonicSeed; + std::optional>> cryptedLegacySeed; CryptedKeyMap mapCryptedKeys; CryptedSproutSpendingKeyMap mapCryptedSproutSpendingKeys; CryptedSaplingSpendingKeyMap mapCryptedSaplingSpendingKeys; @@ -172,10 +173,15 @@ class CCryptoKeyStore : public CBasicKeyStore bool Lock(); - virtual bool SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); - bool SetHDSeed(const HDSeed& seed); - bool HaveHDSeed() const; - bool GetHDSeed(HDSeed& seedOut) const; + bool SetMnemonicSeed(const MnemonicSeed& seed); + virtual bool SetCryptedMnemonicSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + bool HaveMnemonicSeed() const; + std::optional GetMnemonicSeed() const; + + bool SetLegacyHDSeed(const HDSeed& seed); + virtual bool SetCryptedLegacyHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + bool HaveLegacyHDSeed() const; + std::optional GetLegacyHDSeed() const; virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); @@ -230,7 +236,7 @@ class CCryptoKeyStore : public CBasicKeyStore mi++; } } - //! Sapling + //! Sapling virtual bool AddCryptedSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector &vchCryptedSecret); diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 4f59ab35016..0ec05fa7dd6 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -32,9 +32,9 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { EXPECT_ANY_THROW(wallet.GenerateNewSaplingZKey()); // Load the all-zeroes seed - CKeyingMaterial rawSeed(32, 0); - HDSeed seed(rawSeed); - wallet.LoadHDSeed(seed); + std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + MnemonicSeed seed(English, mnemonic); + wallet.LoadMnemonicSeed(seed); // Now this call succeeds auto address = wallet.GenerateNewSaplingZKey(); @@ -428,7 +428,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // No default CPubKey set ASSERT_TRUE(fFirstRun); - ASSERT_FALSE(wallet.HaveHDSeed()); + ASSERT_FALSE(wallet.HaveMnemonicSeed()); wallet.GenerateNewSeed(); // wallet should be empty @@ -477,7 +477,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // Confirm it's not the same as the other wallet ASSERT_TRUE(&wallet != &wallet2); - ASSERT_TRUE(wallet2.HaveHDSeed()); + ASSERT_TRUE(wallet2.HaveMnemonicSeed()); // wallet should have three addresses wallet2.GetSaplingPaymentAddresses(addrs); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d6c3130b010..b54b33fa597 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -67,7 +67,7 @@ std::string DecodeDumpString(const std::string &str) { for (unsigned int pos = 0; pos < str.length(); pos++) { unsigned char c = str[pos]; if (c == '%' && pos+2 < str.length()) { - c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | + c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15)); pos += 2; } @@ -80,7 +80,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( "importprivkey \"zcashprivkey\" ( \"label\" rescan )\n" @@ -189,7 +189,7 @@ UniValue importaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 4) throw runtime_error( "importaddress \"address\" ( \"label\" rescan p2sh )\n" @@ -338,7 +338,7 @@ UniValue importwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "importwallet \"filename\"\n" @@ -476,7 +476,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "dumpprivkey \"t-addr\"\n" @@ -520,7 +520,7 @@ UniValue z_exportwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "z_exportwallet \"filename\"\n" @@ -609,13 +609,27 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys) file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); - { - HDSeed hdSeed; - pwalletMain->GetHDSeed(hdSeed); - auto rawSeed = hdSeed.RawSeed(); - file << strprintf("# HDSeed=%s fingerprint=%s", HexStr(rawSeed.begin(), rawSeed.end()), hdSeed.Fingerprint().GetHex()); - file << "\n"; + + std::optional hdSeed = pwalletMain->GetMnemonicSeed(); + if (hdSeed.has_value()) { + auto mSeed = hdSeed.value(); + file << strprintf( + "# Recovery Phrase=\"%s\" \n# language=%s \n# fingerprint=%s\n", + mSeed.GetMnemonic(), + MnemonicSeed::LanguageName(mSeed.GetLanguage()), + mSeed.Fingerprint().GetHex() + ); + } + + std::optional legacySeed = pwalletMain->GetLegacyHDSeed(); + if (legacySeed.has_value()) { + auto rawSeed = legacySeed.value().RawSeed(); + file << strprintf("# Legacy HDSeed=%s fingerprint=%s\n", + HexStr(rawSeed.begin(), rawSeed.end()), + legacySeed.value().Fingerprint().GetHex() + ); } + file << "\n"; for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; @@ -769,10 +783,10 @@ UniValue z_importkey(const UniValue& params, bool fHelp) if (addResult == KeyNotAdded) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet"); } - + // whenever a key is imported, we need to scan the whole chain pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' - + // We want to scan for transactions and notes if (fRescan) { pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true); diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 93cfda21468..6a142b50d76 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -790,7 +790,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { using namespace libzcash; UniValue addr; - if (!pwalletMain->HaveHDSeed()) { + if (!pwalletMain->HaveMnemonicSeed()) { pwalletMain->GenerateNewSeed(); } @@ -1449,7 +1449,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->HaveHDSeed()) { + if (!pwalletMain->HaveMnemonicSeed()) { pwalletMain->GenerateNewSeed(); } @@ -1523,11 +1523,11 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) // We should be able to decrypt the outCiphertext with the ovk // generated for transparent addresses - HDSeed seed; - BOOST_ASSERT(pwalletMain->GetHDSeed(seed)); + std::optional seed = pwalletMain->GetMnemonicSeed(); + BOOST_ASSERT(seed.has_value()); BOOST_CHECK(AttemptSaplingOutDecryption( tx.vShieldedOutput[0].outCiphertext, - ovkForShieldingFromTaddr(seed), + ovkForShieldingFromTaddr(seed.value()), tx.vShieldedOutput[0].cv, tx.vShieldedOutput[0].cmu, tx.vShieldedOutput[0].ephemeralKey)); @@ -1608,7 +1608,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) UniValue retValue; int n = 100; - if(!pwalletMain->HaveHDSeed()) + if(!pwalletMain->HaveMnemonicSeed()) { pwalletMain->GenerateNewSeed(); } @@ -1657,7 +1657,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) // Verify the key has been added BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); arr = retValue.get_array(); - BOOST_CHECK(arr.size() == n+1); + BOOST_CHECK_EQUAL(arr.size(), n+1); // We can't simulate over RPC the wallet closing and being reloaded // but there are tests for this in gtest. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index adc171603bf..05bb1e2a4d6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -120,11 +120,11 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() CKeyMetadata metadata(nCreationTime); // Try to get the seed - HDSeed seed; - if (!GetHDSeed(seed)) + auto seed = GetMnemonicSeed(); + if (!seed.has_value()) throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): HD seed not found"); - auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); + auto m = libzcash::SaplingExtendedSpendingKey::Master(seed.value()); // We use a fixed keypath scheme of m/32'/coin_type'/account' // Derive m/32' @@ -526,7 +526,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase) if (CCryptoKeyStore::Unlock(vMasterKey)) { // Now that the wallet is decrypted, ensure we have an HD seed. // https://github.com/zcash/zcash/issues/3607 - if (!this->HaveHDSeed()) { + if (!this->HaveMnemonicSeed()) { this->GenerateNewSeed(); } return true; @@ -2216,16 +2216,16 @@ bool CWallet::IsHDFullyEnabled() const return false; } -void CWallet::GenerateNewSeed() +void CWallet::GenerateNewSeed(Language language) { LOCK(cs_wallet); - auto seed = HDSeed::Random(HD_WALLET_SEED_LENGTH); + auto seed = MnemonicSeed::Random(language, HD_WALLET_SEED_LENGTH); int64_t nCreationTime = GetTime(); // If the wallet is encrypted and locked, this will fail. - if (!SetHDSeed(seed)) + if (!SetMnemonicSeed(seed)) throw std::runtime_error(std::string(__func__) + ": SetHDSeed failed"); // store the key creation time together with @@ -2238,9 +2238,9 @@ void CWallet::GenerateNewSeed() SetHDChain(newHdChain, false); } -bool CWallet::SetHDSeed(const HDSeed& seed) +bool CWallet::SetMnemonicSeed(const MnemonicSeed& seed) { - if (!CCryptoKeyStore::SetHDSeed(seed)) { + if (!CCryptoKeyStore::SetMnemonicSeed(seed)) { return false; } @@ -2252,15 +2252,15 @@ bool CWallet::SetHDSeed(const HDSeed& seed) LOCK(cs_wallet); CWalletDB(strWalletFile).WriteNetworkInfo(networkIdString); if (!IsCrypted()) { - return CWalletDB(strWalletFile).WriteHDSeed(seed); + return CWalletDB(strWalletFile).WriteMnemonicSeed(seed); } } return true; } -bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret) +bool CWallet::SetCryptedMnemonicSeed(const uint256& seedFp, const std::vector &vchCryptedSecret) { - if (!CCryptoKeyStore::SetCryptedHDSeed(seedFp, vchCryptedSecret)) { + if (!CCryptoKeyStore::SetCryptedMnemonicSeed(seedFp, vchCryptedSecret)) { return false; } @@ -2271,19 +2271,19 @@ bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vectorWriteCryptedHDSeed(seedFp, vchCryptedSecret); + return pwalletdbEncryption->WriteCryptedMnemonicSeed(seedFp, vchCryptedSecret); else - return CWalletDB(strWalletFile).WriteCryptedHDSeed(seedFp, vchCryptedSecret); + return CWalletDB(strWalletFile).WriteCryptedMnemonicSeed(seedFp, vchCryptedSecret); } return false; } HDSeed CWallet::GetHDSeedForRPC() const { - HDSeed seed; - if (!pwalletMain->GetHDSeed(seed)) { + auto seed = pwalletMain->GetMnemonicSeed(); + if (!seed.has_value()) { throw JSONRPCError(RPC_WALLET_ERROR, "HD seed not found"); } - return seed; + return seed.value(); } void CWallet::SetHDChain(const CHDChain& chain, bool memonly) @@ -2307,14 +2307,24 @@ uint32_t CWallet::BIP44CoinType() { } -bool CWallet::LoadHDSeed(const HDSeed& seed) +bool CWallet::LoadMnemonicSeed(const MnemonicSeed& seed) { - return CBasicKeyStore::SetHDSeed(seed); + return CBasicKeyStore::SetMnemonicSeed(seed); } -bool CWallet::LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed) +bool CWallet::LoadLegacyHDSeed(const HDSeed& seed) { - return CCryptoKeyStore::SetCryptedHDSeed(seedFp, seed); + return CBasicKeyStore::SetLegacyHDSeed(seed); +} + +bool CWallet::LoadCryptedMnemonicSeed(const uint256& seedFp, const std::vector& seed) +{ + return CCryptoKeyStore::SetCryptedMnemonicSeed(seedFp, seed); +} + +bool CWallet::LoadCryptedLegacyHDSeed(const uint256& seedFp, const std::vector& seed) +{ + return CCryptoKeyStore::SetCryptedLegacyHDSeed(seedFp, seed); } void CWalletTx::SetSproutNoteData(mapSproutNoteData_t ¬eData) @@ -4781,7 +4791,7 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches walletInstance->SetMaxVersion(nMaxVersion); } - if (!walletInstance->HaveHDSeed()) + if (!walletInstance->HaveMnemonicSeed()) { // We can't set the new HD seed until the wallet is decrypted. // https://github.com/zcash/zcash/issues/3607 diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7045a1ea217..e743ef74f3f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1284,10 +1284,20 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface Sets the seed's version based on the current wallet version (so the caller must ensure the current wallet version is correct before calling this function). */ - void GenerateNewSeed(); + void GenerateNewSeed(Language language = English); - bool SetHDSeed(const HDSeed& seed); - bool SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + bool SetMnemonicSeed(const MnemonicSeed& seed); + bool SetCryptedMnemonicSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + + /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadMnemonicSeed(const MnemonicSeed& seed); + /* Set the legacy HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadLegacyHDSeed(const HDSeed& seed); + + /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadCryptedMnemonicSeed(const uint256& seedFp, const std::vector& seed); + /* Set the legacy encrypted HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadCryptedLegacyHDSeed(const uint256& seedFp, const std::vector& seed); /* Returns the wallet's HD seed or throw JSONRPCError(...) */ HDSeed GetHDSeedForRPC() const; @@ -1299,11 +1309,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool CheckNetworkInfo(std::pair networkInfo); uint32_t BIP44CoinType(); - /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ - bool LoadHDSeed(const HDSeed& key); - /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ - bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); - /* Find notes filtered by payment address, min depth, ability to spend */ void GetFilteredNotes(std::vector& sproutEntries, std::vector& saplingEntries, diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 2793a9cf5af..f7a2bcba28c 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -685,6 +685,37 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> pwallet->nWitnessCacheSize; } + else if (strType == "mnemonicseed") + { + uint256 seedFp; + ssKey >> seedFp; + auto seed = MnemonicSeed::Read(ssValue); + + if (seed.Fingerprint() != seedFp) + { + strErr = "Error reading wallet database: HDSeed corrupt"; + return false; + } + + if (!pwallet->LoadMnemonicSeed(seed)) + { + strErr = "Error reading wallet database: LoadHDSeed failed"; + return false; + } + } + else if (strType == "chdmnemonicseed") + { + uint256 seedFp; + vector vchCryptedSecret; + ssKey >> seedFp; + ssValue >> vchCryptedSecret; + if (!pwallet->LoadCryptedMnemonicSeed(seedFp, vchCryptedSecret)) + { + strErr = "Error reading wallet database: LoadCryptedMnemonicSeed failed"; + return false; + } + wss.fIsEncrypted = true; + } else if (strType == "hdseed") { uint256 seedFp; @@ -699,7 +730,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return false; } - if (!pwallet->LoadHDSeed(seed)) + if (!pwallet->LoadLegacyHDSeed(seed)) { strErr = "Error reading wallet database: LoadHDSeed failed"; return false; @@ -711,7 +742,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, vector vchCryptedSecret; ssKey >> seedFp; ssValue >> vchCryptedSecret; - if (!pwallet->LoadCryptedHDSeed(seedFp, vchCryptedSecret)) + if (!pwallet->LoadCryptedLegacyHDSeed(seedFp, vchCryptedSecret)) { strErr = "Error reading wallet database: LoadCryptedHDSeed failed"; return false; @@ -744,6 +775,7 @@ static bool IsKeyType(string strType) { return (strType== "key" || strType == "wkey" || strType == "hdseed" || strType == "chdseed" || + strType == "mnemonicseed" || strType == "chdmnemonicseed" || strType == "zkey" || strType == "czkey" || strType == "sapzkey" || strType == "csapzkey" || strType == "vkey" || strType == "sapextfvk" || @@ -1160,18 +1192,19 @@ bool CWalletDB::WriteNetworkInfo(const std::string& networkId) return Write(std::string("networkinfo"), networkInfo); } -bool CWalletDB::WriteHDSeed(const HDSeed& seed) +bool CWalletDB::WriteMnemonicSeed(const MnemonicSeed& seed) { nWalletDBUpdateCounter++; - return Write(std::make_pair(std::string("hdseed"), seed.Fingerprint()), seed.RawSeed()); + return Write(std::make_pair(std::string("mnemonicseed"), seed.Fingerprint()), seed); } -bool CWalletDB::WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret) +bool CWalletDB::WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vector& vchCryptedSecret) { nWalletDBUpdateCounter++; - return Write(std::make_pair(std::string("chdseed"), seedFp), vchCryptedSecret); + return Write(std::make_pair(std::string("chdmnemonicseed"), seedFp), vchCryptedSecret); } + bool CWalletDB::WriteHDChain(const CHDChain& chain) { nWalletDBUpdateCounter++; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 5518e38d988..724b0d5079e 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -48,7 +48,7 @@ class CHDChain static const int VERSION_HD_BASE = 1; static const int CURRENT_VERSION = VERSION_HD_BASE; int nVersion; - uint256 seedFp; + uint256 seedFp; //NU5: Deprecate??? int64_t nCreateTime; // 0 means unknown uint32_t saplingAccountCounter; @@ -170,10 +170,13 @@ class CWalletDB : public CDB static bool Recover(CDBEnv& dbenv, const std::string& filename); bool WriteNetworkInfo(const std::string& networkId); - bool WriteHDSeed(const HDSeed& seed); - bool WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); + bool WriteMnemonicSeed(const MnemonicSeed& seed); + bool WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); + //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain); + std::optional ReadLegacyHDSeed(); + std::optional>> ReadLegacyCryptedHDSeed(); /// Write spending key to wallet database, where key is payment address and value is spending key. bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 83cac8e727b..0bf82517868 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -19,12 +19,21 @@ const unsigned char ZCASH_HD_SEED_FP_PERSONAL[BLAKE2bPersonalBytes] = const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = {'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'}; -HDSeed HDSeed::Random(size_t len) +MnemonicSeed MnemonicSeed::Random(Language language, size_t len) { assert(len >= 32); - RawHDSeed rawSeed(len, 0); - GetRandBytes(rawSeed.data(), len); - return HDSeed(rawSeed); + while (true) { + std::vector entropy(len, 0); + GetRandBytes(entropy.data(), len); + const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), len); + std::string mnemonic(phrase); + zip339_free_phrase(phrase); + MnemonicSeed seed(language, mnemonic); + + // TODO: check for the validity of the Sapling spending key at account 0 for this seed. + + return seed; + } } uint256 HDSeed::Fingerprint() const diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index b68bd207843..517bbfa126e 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -9,6 +9,7 @@ #include "support/allocators/secure.h" #include "uint256.h" #include "zcash/address/sapling.hpp" +#include "rust/zip339.h" #include #include @@ -18,27 +19,121 @@ const uint32_t ZIP32_HARDENED_KEY_LIMIT = 0x80000000; const size_t ZIP32_XFVK_SIZE = 169; const size_t ZIP32_XSK_SIZE = 169; +class CWalletDB; + typedef std::vector> RawHDSeed; class HDSeed { -private: +protected: RawHDSeed seed; -public: HDSeed() {} +public: + // HDSeed(RawHDSeed& seedIn) : seed(seedIn) {} - static HDSeed Random(size_t len = 32); - bool IsNull() const { return seed.empty(); }; + template + static HDSeed ReadLegacy(Stream& stream) { + RawHDSeed rawSeed; + stream >> rawSeed; + HDSeed seed(rawSeed); + return seed; + } + uint256 Fingerprint() const; RawHDSeed RawSeed() const { return seed; } +}; + + +class MnemonicSeed: public HDSeed { +private: + Language language; + std::string mnemonic; + + MnemonicSeed() {} + + void Init() { + unsigned char buf[64]; + zip339_phrase_to_seed(language, mnemonic.c_str(), &buf); + seed.assign(buf, std::end(buf)); + } + +public: + MnemonicSeed(Language languageIn, std::string& mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { + unsigned char buf[64]; + zip339_phrase_to_seed(languageIn, mnemonicIn.c_str(), &buf); + seed.assign(buf, std::end(buf)); + } + + static MnemonicSeed Random(Language language = English, size_t len = 32); + + static std::string LanguageName(Language language) { + switch (language) { + case English: + return "English"; + case SimplifiedChinese: + return "Simplified Chinese"; + case TraditionalChinese: + return "Traditional Chinese"; + case Czech: + return "Czech"; + case French: + return "French"; + case Italian: + return "Italian"; + case Japanese: + return "Japanese"; + case Korean: + return "Korean"; + case Portuguese: + return "Portuguese"; + case Spanish: + return "Spanish"; + default: + return "INVALID"; + } + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + if (ser_action.ForRead()) { + uint32_t language0; + + READWRITE(language0); + READWRITE(mnemonic); + language = (Language) language0; + Init(); + } else { + uint32_t language0 = (uint32_t) language; + + READWRITE(language0); + READWRITE(mnemonic); + } + } + + template + static MnemonicSeed Read(Stream& stream) { + MnemonicSeed seed; + stream >> seed; + return seed; + } + + const Language GetLanguage() const { + return language; + } + + const std::string GetMnemonic() const { + return mnemonic; + } - friend bool operator==(const HDSeed& a, const HDSeed& b) + friend bool operator==(const MnemonicSeed& a, const MnemonicSeed& b) { return a.seed == b.seed; } - friend bool operator!=(const HDSeed& a, const HDSeed& b) + friend bool operator!=(const MnemonicSeed& a, const MnemonicSeed& b) { return !(a == b); } From 8883ae8b9e935c5d61e219b8c799f4392ff7479d Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 15 Sep 2021 13:36:18 -0600 Subject: [PATCH 075/514] Add support for externally searching for valid Sapling diversifiers. In order to support generation of unified addresses, it needs to be possible for the code generating a unified address to search the space of Sapling diversifiers to obtain a valid diversifier. Historically, invalid diviersifiers were simply skipped, so we retain this behavior when obtaining a Sapling address from the legacy HD seed. --- Cargo.lock | 16 +-- Cargo.toml | 10 +- src/keystore.cpp | 1 + src/rust/include/librustzcash.h | 7 ++ src/rust/src/rustzcash.rs | 42 +++++-- src/uint256.h | 15 +++ .../asyncrpcoperation_saplingmigration.cpp | 26 ++-- src/wallet/gtest/test_wallet_zkeys.cpp | 18 +-- src/wallet/rpcwallet.cpp | 16 ++- src/wallet/test/rpc_wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 116 ++++++++++++------ src/wallet/wallet.h | 34 +++-- src/wallet/walletdb.cpp | 21 +++- src/wallet/walletdb.h | 57 ++++++--- src/zcash/address/zip32.cpp | 29 +++-- src/zcash/address/zip32.h | 9 +- 16 files changed, 282 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2c60440176..2e89ab76620 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,7 +534,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "blake2b_simd", "byteorder", @@ -543,7 +543,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "blake2b_simd", ] @@ -1896,7 +1896,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "bech32", "blake2b_simd", @@ -1908,7 +1908,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "byteorder", "nonempty", @@ -1917,7 +1917,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "bigint", "blake2b_simd", @@ -1927,7 +1927,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "blake2b_simd", "byteorder", @@ -1942,7 +1942,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "aes", "bip0039", @@ -1976,7 +1976,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 70a5122cbeb..9219db27d1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,8 +71,8 @@ codegen-units = 1 ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } +zcash_address = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } +zcash_history = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } +zcash_note_encryption = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } +zcash_primitives = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } +zcash_proofs = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } diff --git a/src/keystore.cpp b/src/keystore.cpp index d9542993d18..9287787be6f 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -201,6 +201,7 @@ bool CBasicKeyStore::AddSaplingFullViewingKey( auto ivk = extfvk.fvk.in_viewing_key(); mapSaplingFullViewingKeys[ivk] = extfvk; + // TODO: check whether DefaultAddress is the right thing to use here. return CBasicKeyStore::AddSaplingIncomingViewingKey(ivk, extfvk.DefaultAddress()); } diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index 66c5aaa896c..31de03ad04d 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -308,6 +308,13 @@ extern "C" { /// Derive a PaymentAddress from an ExtendedFullViewingKey. bool librustzcash_zip32_xfvk_address( + const unsigned char *xfvk, + const unsigned char *j, + unsigned char *addr_ret + ); + + /// Derive a PaymentAddress from an ExtendedFullViewingKey. + bool librustzcash_zip32_find_xfvk_address( const unsigned char *xfvk, const unsigned char *j, unsigned char *j_ret, diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index c4b1e6125bf..c01efd10153 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -1081,25 +1081,47 @@ pub extern "C" fn librustzcash_zip32_xfvk_derive( pub extern "C" fn librustzcash_zip32_xfvk_address( xfvk: *const [c_uchar; 169], j: *const [c_uchar; 11], - j_ret: *mut [c_uchar; 11], addr_ret: *mut [c_uchar; 43], ) -> bool { let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) .expect("valid ExtendedFullViewingKey"); let j = zip32::DiversifierIndex(unsafe { *j }); - let addr = match xfvk.address(j) { - Ok(addr) => addr, - Err(_) => return false, - }; + match xfvk.address(j) { + Some(addr) => { + let addr_ret = unsafe { &mut *addr_ret }; + addr_ret.copy_from_slice(&addr.to_bytes()); - let j_ret = unsafe { &mut *j_ret }; - let addr_ret = unsafe { &mut *addr_ret }; + true + } + None => false, + } +} - j_ret.copy_from_slice(&(addr.0).0); - addr_ret.copy_from_slice(&addr.1.to_bytes()); +/// Derive a PaymentAddress from an ExtendedFullViewingKey. +#[no_mangle] +pub extern "C" fn librustzcash_zip32_find_xfvk_address( + xfvk: *const [c_uchar; 169], + j: *const [c_uchar; 11], + j_ret: *mut [c_uchar; 11], + addr_ret: *mut [c_uchar; 43], +) -> bool { + let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) + .expect("valid ExtendedFullViewingKey"); + let j = zip32::DiversifierIndex(unsafe { *j }); - true + match xfvk.find_address(j) { + Ok((j, addr)) => { + let j_ret = unsafe { &mut *j_ret }; + let addr_ret = unsafe { &mut *addr_ret }; + + j_ret.copy_from_slice(&j.0); + addr_ret.copy_from_slice(&addr.to_bytes()); + + true + } + Err(_) => false, + } } #[no_mangle] diff --git a/src/uint256.h b/src/uint256.h index 8d98a0e5278..60746304d31 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -108,6 +109,20 @@ class blob88 : public base_blob<88> { blob88() {} blob88(const base_blob<88>& b) : base_blob<88>(b) {} explicit blob88(const std::vector& vch) : base_blob<88>(vch) {} + + std::optional increment() const { + blob88 result = *this; + + for (int i = 0; i < 11; i++) { + result.data[i] += 1; + if (result.data[i] != 0) { + // no overflow + return result; + } + } + + return std::nullopt; + } }; /** 160-bit opaque blob. diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 6671298c7d1..11fc89aa405 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -201,27 +201,15 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration assert(saplingAddress != nullptr); // This is checked in init.cpp return *saplingAddress; } - // Derive the address for Sapling account 0 - auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); - uint32_t bip44CoinType = Params().BIP44CoinType(); - // We use a fixed keypath scheme of m/32'/coin_type'/account' - // Derive m/32' - auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); - // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); - - // Derive m/32'/coin_type'/0' - libzcash::SaplingExtendedSpendingKey xsk = m_32h_cth.Derive(0 | ZIP32_HARDENED_KEY_LIMIT); - - libzcash::SaplingPaymentAddress toAddress = xsk.DefaultAddress(); - - if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(toAddress)) { - // Sapling account 0 must be the first address returned by GenerateNewSaplingZKey - assert(pwalletMain->GenerateNewSaplingZKey() == toAddress); + // TODO: use UVK-based derivation here instead. + auto xsk = pwalletMain->GenerateNewSaplingZKey(seed, 0); + if (xsk.has_value()) { + return xsk.value().DefaultAddress(); + } else { + // the wallet already has a key at account 0; what is the + // correct behavior here? } - - return toAddress; } void AsyncRPCOperation_saplingmigration::cancel() { diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 0ec05fa7dd6..0622932aff7 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -8,7 +8,7 @@ /** * This test covers Sapling methods on CWallet - * GenerateNewSaplingZKey() + * GenerateNewLegacySaplingZKey() * AddSaplingZKey() * AddSaplingIncomingViewingKey() * LoadSaplingZKey() @@ -29,7 +29,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { ASSERT_EQ(0, addrs.size()); // No HD seed in the wallet - EXPECT_ANY_THROW(wallet.GenerateNewSaplingZKey()); + EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); // Load the all-zeroes seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); @@ -37,7 +37,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { wallet.LoadMnemonicSeed(seed); // Now this call succeeds - auto address = wallet.GenerateNewSaplingZKey(); + auto address = wallet.GenerateNewLegacySaplingZKey().value(); // wallet should have one key wallet.GetSaplingPaymentAddresses(addrs); @@ -70,7 +70,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // If we can't get an early diversified address, we are very unlucky blob88 diversifier; diversifier.begin()[0] = 10; - auto dpa = sk.ToXFVK().Address(diversifier).value().second; + auto dpa = sk.ToXFVK().Address(diversifier).value(); // verify wallet only has the default address EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.DefaultAddress())); @@ -98,7 +98,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { ASSERT_EQ(wallet.mapSaplingZKeyMetadata[ivk2].nCreateTime, now); // Load a diversified address for the third key into the wallet - auto dpa2 = sk2.ToXFVK().Address(diversifier).value().second; + auto dpa2 = sk2.ToXFVK().Address(diversifier).value(); EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk2.DefaultAddress())); EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa2)); EXPECT_TRUE(wallet.LoadSaplingPaymentAddress(dpa2, ivk2)); @@ -437,7 +437,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_EQ(0, addrs.size()); // Add random key to the wallet - auto address = wallet.GenerateNewSaplingZKey(); + auto address = wallet.GenerateNewLegacySaplingZKey().value(); // wallet should have one key wallet.GetSaplingPaymentAddresses(addrs); @@ -449,7 +449,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { EXPECT_TRUE(wallet.GetSaplingExtendedSpendingKey(address, extsk)); blob88 diversifier; diversifier.begin()[0] = 10; - auto dpa = extsk.ToXFVK().Address(diversifier).value().second; + auto dpa = extsk.ToXFVK().Address(diversifier).value(); // Add diversified address to the wallet auto ivk = extsk.expsk.full_viewing_key().in_viewing_key(); @@ -462,11 +462,11 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_TRUE(wallet.EncryptWallet(strWalletPass)); // adding a new key will fail as the wallet is locked - EXPECT_ANY_THROW(wallet.GenerateNewSaplingZKey()); + EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); // unlock wallet and then add wallet.Unlock(strWalletPass); - auto address2 = wallet.GenerateNewSaplingZKey(); + auto address2 = wallet.GenerateNewLegacySaplingZKey().value(); // flush the wallet to prevent race conditions wallet.Flush(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e4011d211e9..5193133058f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2032,9 +2032,12 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) if (pwalletMain->IsCrypted()) obj.pushKV("unlocked_until", nWalletUnlockTime); obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); - uint256 seedFp = pwalletMain->GetHDChain().seedFp; - if (!seedFp.IsNull()) - obj.pushKV("seedfp", seedFp.GetHex()); + auto mnemonicChain = pwalletMain->GetMnemonicHDChain(); + if (mnemonicChain.has_value()) + obj.pushKV("seedfp", mnemonicChain.value().GetSeedFingerprint().GetHex()); + auto legacyChain = pwalletMain->GetLegacyHDChain(); + if (legacyChain.has_value()) + obj.pushKV("legacy_seedfp", legacyChain.value().GetSeedFingerprint().GetHex()); return obj; } @@ -2936,7 +2939,12 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) if (addrType == ADDR_TYPE_SPROUT) { return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()); } else if (addrType == ADDR_TYPE_SAPLING) { - return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey()); + auto saplingAddress = pwalletMain->GenerateNewLegacySaplingZKey(); + if (saplingAddress.has_value()) { + return keyIO.EncodePaymentAddress(saplingAddress.value()); + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "No legacy HD seed available; please use z_getunifiedaddress instead."); + } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type"); } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 6a142b50d76..134b36979a3 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -1459,7 +1459,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) // add keys manually auto taddr = pwalletMain->GenerateNewKey().GetID(); std::string taddr1 = keyIO.EncodeDestination(taddr); - auto pa = pwalletMain->GenerateNewSaplingZKey(); + auto pa = pwalletMain->GenerateNewLegacySaplingZKey().value(); std::string zaddr1 = keyIO.EncodePaymentAddress(pa); const Consensus::Params& consensusParams = Params().GetConsensus(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 05bb1e2a4d6..e41e2196748 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -110,51 +110,83 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() return addr; } -// Generate a new Sapling spending key and return its public payment address -SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() +// Generate a new Sapling spending key (generate a new account) based upon +// the legacy HD seed associated with this wallet and return its +// public payment address. +// +// The z_getnewaddress API must use the legacy HD seed, and fail if that seed +// is not present. When using legacy HD seeds, the account index is determined +// by trial of legacyHDChain.GetAccountCounter(); for unified addresses this must use +// valued derived from legacyHDChain.unifiedAccountCounter +std::optional CWallet::GenerateNewLegacySaplingZKey() { - AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata + // Try to get the seed + auto seedOpt = GetLegacyHDSeed(); + if (seedOpt.has_value()) { + auto seed = seedOpt.value(); + if (!legacyHDChain.has_value()) { + legacyHDChain = CHDChain(seed.Fingerprint(), GetTime()); + } + CHDChain& hdChain = legacyHDChain.value(); + while (true) { + auto xsk = GenerateNewSaplingZKey(seed, hdChain.GetAccountCounter()); + if (!xsk.has_value()) { + hdChain.IncrementAccountCounter(); + continue; + } else { + // Update the chain model in the database + if (fFileBacked && !CWalletDB(strWalletFile).WriteLegacyHDChain(hdChain)) { + throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Writing HD chain model failed"); + } - // Create new metadata - int64_t nCreationTime = GetTime(); - CKeyMetadata metadata(nCreationTime); + return xsk.value().ToXFVK().DefaultAddress(); + } + } + } else { + return std::nullopt; + } +} - // Try to get the seed - auto seed = GetMnemonicSeed(); - if (!seed.has_value()) - throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): HD seed not found"); +// Generate a new Sapling spending key and return its public payment address +// +// NU5: This must take both the account number and the HD seed to be used. +// Generation of unified accounts must allow the user to specify a set of protocol +// types that they wish to include in their unified addresses. +std::optional CWallet::GenerateNewSaplingZKey( + const HDSeed& seed, + uint32_t accountId) { + AssertLockHeld(cs_wallet); - auto m = libzcash::SaplingExtendedSpendingKey::Master(seed.value()); + auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); + uint32_t bip44CoinType = BIP44CoinType(); // We use a fixed keypath scheme of m/32'/coin_type'/account' // Derive m/32' auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.Derive(BIP44CoinType() | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); // Derive account key at next index, skip keys already known to the wallet - libzcash::SaplingExtendedSpendingKey xsk; - do - { - xsk = m_32h_cth.Derive(hdChain.saplingAccountCounter | ZIP32_HARDENED_KEY_LIMIT); - metadata.hdKeypath = "m/32'/" + std::to_string(BIP44CoinType()) + "'/" + std::to_string(hdChain.saplingAccountCounter) + "'"; - metadata.seedFp = hdChain.seedFp; - // Increment childkey index - hdChain.saplingAccountCounter++; - } while (HaveSaplingSpendingKey(xsk.ToXFVK())); + libzcash::SaplingExtendedSpendingKey xsk = m_32h_cth.Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); + if (HaveSaplingSpendingKey(xsk.ToXFVK())) { + return std::nullopt; + } else { + // Create new metadata + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); - // Update the chain model in the database - if (fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(hdChain)) - throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Writing HD chain model failed"); + metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + metadata.seedFp = seed.Fingerprint(); - auto ivk = xsk.expsk.full_viewing_key().in_viewing_key(); - mapSaplingZKeyMetadata[ivk] = metadata; + auto ivk = xsk.expsk.full_viewing_key().in_viewing_key(); + mapSaplingZKeyMetadata[ivk] = metadata; - if (!AddSaplingZKey(xsk)) { - throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); + if (!AddSaplingZKey(xsk)) { + throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); + } + + return xsk; } - // return default sapling payment address. - return xsk.DefaultAddress(); } // Add spending key to keystore @@ -2231,11 +2263,8 @@ void CWallet::GenerateNewSeed(Language language) // store the key creation time together with // the child index counter in the database // as a hdchain object - CHDChain newHdChain; - newHdChain.nVersion = CHDChain::VERSION_HD_BASE; - newHdChain.seedFp = seed.Fingerprint(); - newHdChain.nCreateTime = nCreationTime; - SetHDChain(newHdChain, false); + CHDChain newHdChain(seed.Fingerprint(), nCreationTime); + SetMnemonicHDChain(newHdChain, false); } bool CWallet::SetMnemonicSeed(const MnemonicSeed& seed) @@ -2286,13 +2315,24 @@ HDSeed CWallet::GetHDSeedForRPC() const { return seed.value(); } -void CWallet::SetHDChain(const CHDChain& chain, bool memonly) +// TODO: make private +void CWallet::SetMnemonicHDChain(const CHDChain& chain, bool memonly) +{ + LOCK(cs_wallet); + if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(chain)) + throw std::runtime_error(std::string(__func__) + ": writing chain failed"); + + mnemonicHDChain = chain; +} + +// TODO: make private +void CWallet::SetLegacyHDChain(const CHDChain& chain, bool memonly) { LOCK(cs_wallet); - if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(chain)) + if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteLegacyHDChain(chain)) throw std::runtime_error(std::string(__func__) + ": writing chain failed"); - hdChain = chain; + legacyHDChain = chain; } bool CWallet::CheckNetworkInfo(std::pair readNetworkInfo) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e743ef74f3f..677d23a356f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -804,8 +804,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); void MarkAffectedTransactionsDirty(const CTransaction& tx); - /* the hd chain data model (chain counters) */ - CHDChain hdChain; + /* the hd chain metadata for keys derived from the mnemonic seed */ + std::optional mnemonicHDChain; + /* the hd chain metadata for keys derived from the legacy seed */ + std::optional legacyHDChain; + + /* the network ID string for the network for which this wallet was created */ std::string networkIdString; public: @@ -1052,8 +1056,20 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** * Sapling ZKeys */ - //! Generates new Sapling key - libzcash::SaplingPaymentAddress GenerateNewSaplingZKey(); + + //! Generates new Sapling key given the specified HD seed and account id + //! and persists it to the wallet. + // + //! Returns the newly generated extended spending key, or `std::nullopt` + //! if a key corresponding to the specified account id already exists in + //! the wallet. + std::optional GenerateNewSaplingZKey( + const HDSeed& seed, + uint32_t accountId); + //! Generates new Sapling key using the legacy HD seed (if one is available) + //! and legacy account counter, stores the newly generated spending key to + //! the wallet, and returns the default address for the newly generated key. + std::optional GenerateNewLegacySaplingZKey(); //! Adds Sapling spending key to the store, and saves it to disk bool AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key); //! Add Sapling full viewing key to the wallet. @@ -1302,9 +1318,13 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Returns the wallet's HD seed or throw JSONRPCError(...) */ HDSeed GetHDSeedForRPC() const; - /* Set the HD chain model (chain child index counters) */ - void SetHDChain(const CHDChain& chain, bool memonly); - const CHDChain& GetHDChain() const { return hdChain; } + /* Set the metadata for the mnemonic HD seed (chain child index counters) */ + void SetMnemonicHDChain(const CHDChain& chain, bool memonly); + const std::optional GetMnemonicHDChain() const { return mnemonicHDChain; } + + /* Set the metadata for the legacy HD seed (chain child index counters) */ + void SetLegacyHDChain(const CHDChain& chain, bool memonly); + const std::optional GetLegacyHDChain() const { return legacyHDChain; } bool CheckNetworkInfo(std::pair networkInfo); uint32_t BIP44CoinType(); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index f7a2bcba28c..0dad1fdda69 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -751,9 +751,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "hdchain") { - CHDChain chain; - ssValue >> chain; - pwallet->SetHDChain(chain, true); + auto chain = CHDChain::Read(ssValue); + pwallet->SetLegacyHDChain(chain, true); + } + else if (strType == "mnemonichdchain") + { + auto chain = CHDChain::Read(ssValue); + pwallet->SetMnemonicHDChain(chain, true); } else if (strType == "networkinfo") { @@ -1204,13 +1208,20 @@ bool CWalletDB::WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vecto return Write(std::make_pair(std::string("chdmnemonicseed"), seedFp), vchCryptedSecret); } - -bool CWalletDB::WriteHDChain(const CHDChain& chain) +// This can be removed once generation of Sapling addresses using the legacy +// seed has been disabled. +bool CWalletDB::WriteLegacyHDChain(const CHDChain& chain) { nWalletDBUpdateCounter++; return Write(std::string("hdchain"), chain); } +bool CWalletDB::WriteMnemonicHDChain(const CHDChain& chain) +{ + nWalletDBUpdateCounter++; + return Write(std::string("mnemonichdchain"), chain); +} + void CWalletDB::IncrementUpdateCounter() { nWalletDBUpdateCounter++; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 724b0d5079e..b2f297e2ce9 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -41,19 +41,30 @@ enum DBErrors DB_WRONG_NETWORK, }; -/* simple hd chain data model */ +/* hd chain metadata */ class CHDChain { -public: - static const int VERSION_HD_BASE = 1; - static const int CURRENT_VERSION = VERSION_HD_BASE; +private: int nVersion; - uint256 seedFp; //NU5: Deprecate??? + uint256 seedFp; int64_t nCreateTime; // 0 means unknown - uint32_t saplingAccountCounter; + uint32_t accountCounter; CHDChain() { SetNull(); } + void SetNull() + { + nVersion = CHDChain::CURRENT_VERSION; + seedFp.SetNull(); + nCreateTime = 0; + accountCounter = 0; + } +public: + static const int VERSION_HD_BASE = 1; + static const int CURRENT_VERSION = VERSION_HD_BASE; + + CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0) {} + ADD_SERIALIZE_METHODS; template @@ -62,15 +73,26 @@ class CHDChain READWRITE(nVersion); READWRITE(seedFp); READWRITE(nCreateTime); - READWRITE(saplingAccountCounter); + READWRITE(accountCounter); } - void SetNull() - { - nVersion = CHDChain::CURRENT_VERSION; - seedFp.SetNull(); - nCreateTime = 0; - saplingAccountCounter = 0; + template + static CHDChain Read(Stream& stream) { + CHDChain chain; + stream >> chain; + return chain; + } + + const uint256 GetSeedFingerprint() const { + return seedFp; + } + + uint32_t GetAccountCounter() const { + return accountCounter; + } + + uint32_t IncrementAccountCounter() { + return ++accountCounter; } }; @@ -172,11 +194,12 @@ class CWalletDB : public CDB bool WriteNetworkInfo(const std::string& networkId); bool WriteMnemonicSeed(const MnemonicSeed& seed); bool WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); + bool WriteMnemonicHDChain(const CHDChain& chain); - //! write the hdchain model (external chain child index counter) - bool WriteHDChain(const CHDChain& chain); - std::optional ReadLegacyHDSeed(); - std::optional>> ReadLegacyCryptedHDSeed(); + //! Write the legacy hdchain metadata to the database + //! + //! TODO: remove when generation of new legacy-seed-based keys has been disabled. + bool WriteLegacyHDChain(const CHDChain& chain); /// Write spending key to wallet database, where key is payment address and value is spending key. bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 0bf82517868..8fad4d13620 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -84,23 +84,22 @@ std::optional SaplingExtendedFullViewingKey::Deri } } -std::optional> +std::optional SaplingExtendedFullViewingKey::Address(diversifier_index_t j) const { CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION); ss_xfvk << *this; CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end()); - diversifier_index_t j_ret; CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize); if (librustzcash_zip32_xfvk_address( reinterpret_cast(xfvk_bytes.data()), - j.begin(), j_ret.begin(), + j.begin(), reinterpret_cast(addr_bytes.data()))) { CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION); libzcash::SaplingPaymentAddress addr; ss_addr >> addr; - return std::make_pair(j_ret, addr); + return addr; } else { return std::nullopt; } @@ -108,13 +107,25 @@ std::optional> libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() const { - diversifier_index_t j0; - auto addr = Address(j0); - // If we can't obtain a default address, we are *very* unlucky... - if (!addr) { + CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION); + ss_xfvk << *this; + CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end()); + + diversifier_index_t j_default; + diversifier_index_t j_ret; + CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize); + if (librustzcash_zip32_find_xfvk_address( + reinterpret_cast(xfvk_bytes.data()), + j_default.begin(), j_ret.begin(), + reinterpret_cast(addr_bytes.data()))) { + CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION); + libzcash::SaplingPaymentAddress addr; + ss_addr >> addr; + return addr; + } else { + // If we can't obtain a default address, we are *very* unlucky... throw std::runtime_error("SaplingExtendedFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!"); } - return addr.value().second; } SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Master(const HDSeed& seed) diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 517bbfa126e..6c4a36068d7 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -168,11 +168,10 @@ struct SaplingExtendedFullViewingKey { std::optional Derive(uint32_t i) const; - // Returns the first index starting from j that generates a valid - // payment address, along with the corresponding address. Returns - // an error if the diversifier space is exhausted. - std::optional> - Address(diversifier_index_t j) const; + // Attempt to construct a valid payment address with diversifier index + // `j`; returns std::nullopt if `j` does not result in a valid diversifier + // given this xfvk. + std::optional Address(diversifier_index_t j) const; libzcash::SaplingPaymentAddress DefaultAddress() const; From 83dee7e8868a5fe773a35702d99bdd050c2ee7d6 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 21 Sep 2021 11:17:33 -0600 Subject: [PATCH 076/514] Adds basic unified spending key derivation. Also, begin pruning Sapling key derivation down to the minimal amount required to support the legacy Sapling key derivation process. --- src/gtest/test_keys.cpp | 2 +- src/gtest/test_keystore.cpp | 16 +-- src/key.cpp | 24 ++-- src/key.h | 13 ++- src/test/bip32_tests.cpp | 16 ++- src/test/key_tests.cpp | 2 +- src/utiltest.cpp | 2 +- .../asyncrpcoperation_saplingmigration.cpp | 11 +- src/wallet/gtest/test_wallet.cpp | 18 +-- src/wallet/gtest/test_wallet_zkeys.cpp | 12 +- src/wallet/test/rpc_wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 108 +++++++++--------- src/wallet/wallet.h | 7 ++ src/wallet/walletdb.h | 43 ------- src/zcash/Address.cpp | 6 +- src/zcash/address/zip32.cpp | 79 ++++++++++--- src/zcash/address/zip32.h | 74 +++++++++++- src/zcbenchmarks.cpp | 2 +- 18 files changed, 271 insertions(+), 166 deletions(-) diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index b7aef27bbf2..4ff275128d7 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -48,7 +48,7 @@ TEST(Keys, EncodeAndDecodeSapling) EXPECT_EQ(extfvk, extfvk2); } { - auto addr = sk.DefaultAddress(); + auto addr = sk.ToXFVK().DefaultAddress(); std::string addr_string = keyIO.EncodePaymentAddress(addr); EXPECT_EQ( diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index f856877fa44..03a9f082ffc 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -14,6 +14,8 @@ #define MAKE_STRING(x) std::string((x), (x)+sizeof(x)) +const uint32_t BIP44_TESTNET_TYPE = 1; + TEST(KeystoreTests, StoreAndRetrieveHDSeed) { CBasicKeyStore keyStore; @@ -23,7 +25,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeed) { EXPECT_FALSE(seedOut.has_value()); // Generate a random seed - auto seed = MnemonicSeed::Random(); + auto seed = MnemonicSeed::Random(BIP44_TESTNET_TYPE); // We should be able to set and retrieve the seed ASSERT_TRUE(keyStore.SetMnemonicSeed(seed)); @@ -33,7 +35,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeed) { EXPECT_EQ(seed, seedOut.value()); // Generate another random seed - auto seed2 = MnemonicSeed::Random(); + auto seed2 = MnemonicSeed::Random(BIP44_TESTNET_TYPE); EXPECT_NE(seed, seed2); // We should not be able to set and retrieve a different seed @@ -203,7 +205,7 @@ TEST(KeystoreTests, StoreAndRetrieveSaplingSpendingKey) { auto sk = GetTestMasterSaplingSpendingKey(); auto extfvk = sk.ToXFVK(); auto ivk = extfvk.fvk.in_viewing_key(); - auto addr = sk.DefaultAddress(); + auto addr = sk.ToXFVK().DefaultAddress(); // Sanity-check: we can't get a key we haven't added EXPECT_FALSE(keyStore.HaveSaplingSpendingKey(extfvk)); @@ -237,7 +239,7 @@ TEST(KeystoreTests, StoreAndRetrieveSaplingFullViewingKey) { auto sk = GetTestMasterSaplingSpendingKey(); auto extfvk = sk.ToXFVK(); auto ivk = extfvk.fvk.in_viewing_key(); - auto addr = sk.DefaultAddress(); + auto addr = sk.ToXFVK().DefaultAddress(); // Sanity-check: we can't get a full viewing key we haven't added EXPECT_FALSE(keyStore.HaveSaplingFullViewingKey(ivk)); @@ -289,7 +291,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { GetRandBytes(vMasterKey.data(), 32); // 1) Test adding a seed to an unencrypted key store, then encrypting it - auto seed = MnemonicSeed::Random(); + auto seed = MnemonicSeed::Random(BIP44_TESTNET_TYPE); EXPECT_FALSE(keyStore.HaveMnemonicSeed()); auto seedOut = keyStore.GetMnemonicSeed(); EXPECT_FALSE(seedOut.has_value()); @@ -321,7 +323,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { EXPECT_EQ(seed, seedOut.value()); // 2) Test replacing the seed in an already-encrypted key store fails - auto seed2 = MnemonicSeed::Random(); + auto seed2 = MnemonicSeed::Random(BIP44_TESTNET_TYPE); EXPECT_FALSE(keyStore.SetMnemonicSeed(seed2)); EXPECT_TRUE(keyStore.HaveMnemonicSeed()); seedOut = keyStore.GetMnemonicSeed(); @@ -341,7 +343,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { seedOut = keyStore2.GetMnemonicSeed(); EXPECT_FALSE(seedOut.has_value()); - auto seed3 = MnemonicSeed::Random(); + auto seed3 = MnemonicSeed::Random(BIP44_TESTNET_TYPE); ASSERT_TRUE(keyStore2.SetMnemonicSeed(seed3)); EXPECT_TRUE(keyStore2.HaveMnemonicSeed()); seedOut = keyStore2.GetMnemonicSeed(); diff --git a/src/key.cpp b/src/key.cpp index 9f03cf0c2cc..ff52e5eea0a 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -274,23 +274,31 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const return ret; } -bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { +std::optional CExtKey::Derive(unsigned int _nChild) const { + CExtKey out; out.nDepth = nDepth + 1; CKeyID id = key.GetPubKey().GetID(); memcpy(&out.vchFingerprint[0], &id, 4); out.nChild = _nChild; - return key.Derive(out.key, out.chaincode, _nChild, chaincode); + if (key.Derive(out.key, out.chaincode, _nChild, chaincode)) { + return out; + } else { + return std::nullopt; + } } -void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { +CExtKey CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) { + CExtKey xk; static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; std::vector> vout(64); CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data()); - key.Set(vout.data(), vout.data() + 32, true); - memcpy(chaincode.begin(), vout.data() + 32, 32); - nDepth = 0; - nChild = 0; - memset(vchFingerprint, 0, sizeof(vchFingerprint)); + xk.key.Set(vout.data(), vout.data() + 32, true); + memcpy(xk.chaincode.begin(), vout.data() + 32, 32); + xk.nDepth = 0; + xk.nChild = 0; + memset(xk.vchFingerprint, 0, sizeof(xk.vchFingerprint)); + + return xk; } CExtPubKey CExtKey::Neuter() const { diff --git a/src/key.h b/src/key.h index c61f1dd0228..2a9ee32c59b 100644 --- a/src/key.h +++ b/src/key.h @@ -15,6 +15,12 @@ #include #include +/** + * The account identifier used for HD derivation of the transparent + * p2pkh public key from which all child transparent addresses are + * derived in accordance with ZIP-316. + */ +const uint32_t ZCASH_LEGACY_TRANSPARENT_ACCOUNT = 0x7FFFFFFE; /** * secure_allocator is defined in allocators.h @@ -62,6 +68,8 @@ class CKey keydata.resize(32); } + static std::optional FromEntropy(std::vector> keydata); + friend bool operator==(const CKey& a, const CKey& b) { return a.fCompressed == b.fCompressed && @@ -160,11 +168,12 @@ struct CExtKey { a.key == b.key; } + static CExtKey Master(const unsigned char* seed, unsigned int nSeedLen); + void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const; void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]); - bool Derive(CExtKey& out, unsigned int nChild) const; + std::optional Derive(unsigned int nChild) const; CExtPubKey Neuter() const; - void SetMaster(const unsigned char* seed, unsigned int nSeedLen); template void Serialize(Stream& s) const { diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index f83ed78c872..64e9b87abe7 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -80,10 +80,8 @@ TestVector test2 = void RunTest(const TestVector &test) { std::vector seed = ParseHex(test.strHexMaster); - CExtKey key; - CExtPubKey pubkey; - key.SetMaster(&seed[0], seed.size()); - pubkey = key.Neuter(); + CExtKey key = CExtKey::Master(&seed[0], seed.size()); + CExtPubKey pubkey = key.Neuter(); KeyIO keyIO(Params()); for (const TestDerivation &derive : test.vDerive) { unsigned char data[74]; @@ -99,16 +97,16 @@ void RunTest(const TestVector &test) { BOOST_CHECK(keyIO.DecodeExtPubKey(derive.pub) == pubkey); //ensure a base58 decoded pubkey also matches // Derive new keys - CExtKey keyNew; - BOOST_CHECK(key.Derive(keyNew, derive.nChild)); - CExtPubKey pubkeyNew = keyNew.Neuter(); + auto keyNew = key.Derive(derive.nChild); + BOOST_CHECK(keyNew.has_value()); + CExtPubKey pubkeyNew = keyNew.value().Neuter(); if (!(derive.nChild & 0x80000000)) { // Compare with public derivation CExtPubKey pubkeyNew2; BOOST_CHECK(pubkey.Derive(pubkeyNew2, derive.nChild)); BOOST_CHECK(pubkeyNew == pubkeyNew2); } - key = keyNew; + key = keyNew.value(); pubkey = pubkeyNew; CDataStream ssPub(SER_DISK, CLIENT_VERSION); @@ -116,7 +114,7 @@ void RunTest(const TestVector &test) { BOOST_CHECK(ssPub.size() == 75); CDataStream ssPriv(SER_DISK, CLIENT_VERSION); - ssPriv << keyNew; + ssPriv << keyNew.value(); BOOST_CHECK(ssPriv.size() == 75); CExtPubKey pubCheck; diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 959bb25da4c..ae87834a64c 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -247,7 +247,7 @@ BOOST_AUTO_TEST_CASE(zs_address_test) BOOST_CHECK(sk == sk2); } { - auto addr = sk.DefaultAddress(); + auto addr = sk.ToXFVK().DefaultAddress(); std::string addr_string = keyIO.EncodePaymentAddress(addr); BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0); diff --git a/src/utiltest.cpp b/src/utiltest.cpp index ac404ed794b..be0e5ecf2a6 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -346,7 +346,7 @@ CWalletTx GetValidSaplingReceive(const Consensus::Params& consensusParams, auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID()); // To zaddr auto fvk = sk.expsk.full_viewing_key(); - auto pa = sk.DefaultAddress(); + auto pa = sk.ToXFVK().DefaultAddress(); auto builder = TransactionBuilder(consensusParams, 1, &keyStore); builder.SetFee(0); diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 11fc89aa405..5bfdb759af6 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -203,12 +203,15 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration } // TODO: use UVK-based derivation here instead. - auto xsk = pwalletMain->GenerateNewSaplingZKey(seed, 0); + auto usk = pwalletMain->GetUnifiedSpendingKeyForAccount(0); + assert(usk.has_value()); // mnemonic seeds are currently always generated to have valid USKs at account 0 + auto xsk = usk.value().GetSaplingExtendedSpendingKey(); if (xsk.has_value()) { - return xsk.value().DefaultAddress(); + return xsk.value().ToXFVK().DefaultAddress(); } else { - // the wallet already has a key at account 0; what is the - // correct behavior here? + // This error will only occur if Sapling address generation has been disbled for USKs from this + // wallet. + throw std::runtime_error(std::string(__func__) + ": No Sapling address generated for account 0."); } } diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index edc06058558..8308a202939 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -393,7 +393,7 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) { auto expsk = sk.expsk; auto fvk = expsk.full_viewing_key(); auto ivk = fvk.in_viewing_key(); - auto pk = sk.DefaultAddress(); + auto pk = sk.ToXFVK().DefaultAddress(); libzcash::SaplingNote note(pk, 50000, zip_212_enabled[ver]); auto cm = note.cmu().value(); @@ -535,7 +535,7 @@ TEST(WalletTests, FindMySaplingNotes) { auto sk = GetTestMasterSaplingSpendingKey(); auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); - auto pa = sk.DefaultAddress(); + auto pa = extfvk.DefaultAddress(); auto testNote = GetTestSaplingNote(pa, 50000); @@ -669,7 +669,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) { auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); auto ivk = extfvk.fvk.in_viewing_key(); - auto pk = sk.DefaultAddress(); + auto pk = extfvk.DefaultAddress(); ASSERT_TRUE(wallet.AddSaplingZKey(sk)); ASSERT_TRUE(wallet.HaveSaplingSpendingKey(extfvk)); @@ -829,7 +829,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) { auto sk = GetTestMasterSaplingSpendingKey(); auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); - auto pa = sk.DefaultAddress(); + auto pa = extfvk.DefaultAddress(); auto testNote = GetTestSaplingNote(pa, 50000); @@ -914,7 +914,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) { auto sk = GetTestMasterSaplingSpendingKey(); auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); - auto pa = sk.DefaultAddress(); + auto pa = extfvk.DefaultAddress(); auto testNote = GetTestSaplingNote(pa, 50000); @@ -1045,7 +1045,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); auto ivk = extfvk.fvk.in_viewing_key(); - auto pk = sk.DefaultAddress(); + auto pk = extfvk.DefaultAddress(); // Generate Sapling note A libzcash::SaplingNote note(pk, 50000, zip_212_enabled[ver]); @@ -1841,13 +1841,13 @@ TEST(WalletTests, UpdatedSaplingNoteData) { auto sk = m.Derive(0); auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); - auto pa = sk.DefaultAddress(); + auto pa = extfvk.DefaultAddress(); // Generate dummy recipient Sapling address auto sk2 = m.Derive(1); auto expsk2 = sk2.expsk; auto extfvk2 = sk2.ToXFVK(); - auto pa2 = sk2.DefaultAddress(); + auto pa2 = extfvk2.DefaultAddress(); auto testNote = GetTestSaplingNote(pa, 50000); @@ -1983,7 +1983,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); auto ivk = extfvk.fvk.in_viewing_key(); - auto pk = sk.DefaultAddress(); + auto pk = extfvk.DefaultAddress(); ASSERT_TRUE(wallet.AddSaplingZKey(sk)); ASSERT_TRUE(wallet.HaveSaplingSpendingKey(extfvk)); diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 0622932aff7..514fb9b35c4 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -64,7 +64,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { wallet.GetSaplingPaymentAddresses(addrs); EXPECT_EQ(2, addrs.size()); EXPECT_EQ(1, addrs.count(address)); - EXPECT_EQ(1, addrs.count(sk.DefaultAddress())); + EXPECT_EQ(1, addrs.count(sk.ToXFVK().DefaultAddress())); // Generate a diversified address different to the default // If we can't get an early diversified address, we are very unlucky @@ -73,7 +73,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { auto dpa = sk.ToXFVK().Address(diversifier).value(); // verify wallet only has the default address - EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.DefaultAddress())); + EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.ToXFVK().DefaultAddress())); EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa)); // manually add a diversified address @@ -81,7 +81,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { EXPECT_TRUE(wallet.AddSaplingIncomingViewingKey(ivk, dpa)); // verify wallet did add it - EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.DefaultAddress())); + EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.ToXFVK().DefaultAddress())); EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa)); // Load a third key into the wallet @@ -99,7 +99,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // Load a diversified address for the third key into the wallet auto dpa2 = sk2.ToXFVK().Address(diversifier).value(); - EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk2.DefaultAddress())); + EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk2.ToXFVK().DefaultAddress())); EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa2)); EXPECT_TRUE(wallet.LoadSaplingPaymentAddress(dpa2, ivk2)); EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa2)); @@ -501,8 +501,8 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { wallet2.Unlock(strWalletPass); EXPECT_TRUE(wallet2.GetSaplingExtendedSpendingKey(address, keyOut)); - ASSERT_EQ(address, keyOut.DefaultAddress()); + ASSERT_EQ(address, keyOut.ToXFVK().DefaultAddress()); EXPECT_TRUE(wallet2.GetSaplingExtendedSpendingKey(address2, keyOut)); - ASSERT_EQ(address2, keyOut.DefaultAddress()); + ASSERT_EQ(address2, keyOut.ToXFVK().DefaultAddress()); } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 134b36979a3..771e558f15a 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -665,7 +665,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) // create a random Sapling key locally; split between IVKs and spending keys. auto testSaplingSpendingKey = m.Derive(i); - auto testSaplingPaymentAddress = testSaplingSpendingKey.DefaultAddress(); + auto testSaplingPaymentAddress = testSaplingSpendingKey.ToXFVK().DefaultAddress(); if (i % 2 == 0) { std::string testSaplingAddr = keyIO.EncodePaymentAddress(testSaplingPaymentAddress); std::string testSaplingKey = keyIO.EncodeSpendingKey(testSaplingSpendingKey); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e41e2196748..bdf98e4da99 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -118,9 +118,9 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() // is not present. When using legacy HD seeds, the account index is determined // by trial of legacyHDChain.GetAccountCounter(); for unified addresses this must use // valued derived from legacyHDChain.unifiedAccountCounter -std::optional CWallet::GenerateNewLegacySaplingZKey() -{ - // Try to get the seed +std::optional CWallet::GenerateNewLegacySaplingZKey() { + AssertLockHeld(cs_wallet); + auto seedOpt = GetLegacyHDSeed(); if (seedOpt.has_value()) { auto seed = seedOpt.value(); @@ -128,18 +128,33 @@ std::optional CWallet::GenerateNewLegacySaplingZKey() legacyHDChain = CHDChain(seed.Fingerprint(), GetTime()); } CHDChain& hdChain = legacyHDChain.value(); + + // loop until we find an unused account id while (true) { - auto xsk = GenerateNewSaplingZKey(seed, hdChain.GetAccountCounter()); - if (!xsk.has_value()) { - hdChain.IncrementAccountCounter(); + auto xsk = libzcash::SaplingExtendedSpendingKey::ForAccount( + seed, + BIP44CoinType(), + hdChain.GetAccountCounter()); + // advance the account counter so that the next time we need to generate + // a key we're pointing at a free index. + hdChain.IncrementAccountCounter(); + if (HaveSaplingSpendingKey(xsk.first.ToXFVK())) { + // try the next account ID continue; } else { - // Update the chain model in the database + // Update the persisted chain information if (fFileBacked && !CWalletDB(strWalletFile).WriteLegacyHDChain(hdChain)) { throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Writing HD chain model failed"); } - return xsk.value().ToXFVK().DefaultAddress(); + auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key(); + mapSaplingZKeyMetadata[ivk] = xsk.second; + + if (!AddSaplingZKey(xsk.first)) { + throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); + } + + return xsk.first.ToXFVK().DefaultAddress(); } } } else { @@ -147,48 +162,6 @@ std::optional CWallet::GenerateNewLegacySaplingZKey() } } -// Generate a new Sapling spending key and return its public payment address -// -// NU5: This must take both the account number and the HD seed to be used. -// Generation of unified accounts must allow the user to specify a set of protocol -// types that they wish to include in their unified addresses. -std::optional CWallet::GenerateNewSaplingZKey( - const HDSeed& seed, - uint32_t accountId) { - AssertLockHeld(cs_wallet); - - auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); - uint32_t bip44CoinType = BIP44CoinType(); - - // We use a fixed keypath scheme of m/32'/coin_type'/account' - // Derive m/32' - auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); - // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); - - // Derive account key at next index, skip keys already known to the wallet - libzcash::SaplingExtendedSpendingKey xsk = m_32h_cth.Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); - if (HaveSaplingSpendingKey(xsk.ToXFVK())) { - return std::nullopt; - } else { - // Create new metadata - int64_t nCreationTime = GetTime(); - CKeyMetadata metadata(nCreationTime); - - metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - metadata.seedFp = seed.Fingerprint(); - - auto ivk = xsk.expsk.full_viewing_key().in_viewing_key(); - mapSaplingZKeyMetadata[ivk] = metadata; - - if (!AddSaplingZKey(xsk)) { - throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); - } - - return xsk; - } -} - // Add spending key to keystore bool CWallet::AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &sk) { @@ -394,6 +367,37 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi return false; } +UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { + AssertLockHeld(cs_wallet); + + auto seed = GetMnemonicSeed(); + if (!seed.has_value()) { + throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet."); + } + + auto hdChain = GetMnemonicHDChain().value(); + while (true) { + auto usk = UnifiedSpendingKey::Derive(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter()); + hdChain.IncrementAccountCounter(); + + if (usk.has_value()) { + // Update the persisted chain information + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { + throw std::runtime_error("CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed"); + } + + // TODO: Save the unified full viewing key to the wallet metadata + + return usk.value().first; + } + } +} + +std::optional CWallet::GetUnifiedSpendingKeyForAccount(uint32_t accountId) { + //TODO + return std::nullopt; +} + void CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) { AssertLockHeld(cs_wallet); // mapKeyMetadata @@ -2252,7 +2256,7 @@ void CWallet::GenerateNewSeed(Language language) { LOCK(cs_wallet); - auto seed = MnemonicSeed::Random(language, HD_WALLET_SEED_LENGTH); + auto seed = MnemonicSeed::Random(BIP44CoinType(), language, HD_WALLET_SEED_LENGTH); int64_t nCreationTime = GetTime(); @@ -5468,7 +5472,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS KeyIO keyIO(Params()); { if (log){ - LogPrint("zrpc", "Importing zaddr %s...\n", keyIO.EncodePaymentAddress(sk.DefaultAddress())); + LogPrint("zrpc", "Importing zaddr %s...\n", keyIO.EncodePaymentAddress(sk.ToXFVK().DefaultAddress())); } // Don't throw error in case a key is already there if (m_wallet->HaveSaplingSpendingKey(extfvk)) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 677d23a356f..f173dfd9222 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -829,6 +829,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapKeyMetadata; std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; + //std::map mapUnifiedKeyMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1100,6 +1101,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector &vchCryptedSecret); + /** + * Unified keys & addresses + */ + libzcash::UnifiedSpendingKey GenerateNewUnifiedSpendingKey(); + std::optional GetUnifiedSpendingKeyForAccount(uint32_t accountId); + /** * Increment the next transaction order id * @return next transaction order id diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index b2f297e2ce9..a3abf37bc3f 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -96,49 +96,6 @@ class CHDChain } }; -class CKeyMetadata -{ -public: - static const int VERSION_BASIC=1; - static const int VERSION_WITH_HDDATA=10; - static const int CURRENT_VERSION=VERSION_WITH_HDDATA; - int nVersion; - int64_t nCreateTime; // 0 means unknown - std::string hdKeypath; //optional HD/zip32 keypath - uint256 seedFp; - - CKeyMetadata() - { - SetNull(); - } - CKeyMetadata(int64_t nCreateTime_) - { - SetNull(); - nCreateTime = nCreateTime_; - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(nCreateTime); - if (this->nVersion >= VERSION_WITH_HDDATA) - { - READWRITE(hdKeypath); - READWRITE(seedFp); - } - } - - void SetNull() - { - nVersion = CKeyMetadata::CURRENT_VERSION; - nCreateTime = 0; - hdKeypath.clear(); - seedFp.SetNull(); - } -}; - /** Access to the wallet database */ class CWalletDB : public CDB { diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index d827a395a25..9b5340cb14b 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -39,7 +39,7 @@ std::pair AddressInfoFromSpendingKey::operator()(co return std::make_pair("sprout", sk.address()); } std::pair AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const { - return std::make_pair("sapling", sk.DefaultAddress()); + return std::make_pair("sapling", sk.ToXFVK().DefaultAddress()); } std::pair AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const { throw std::invalid_argument("Cannot derive default address from invalid spending key"); @@ -48,8 +48,8 @@ std::pair AddressInfoFromSpendingKey::operator()(co std::pair AddressInfoFromViewingKey::operator()(const SproutViewingKey &sk) const { return std::make_pair("sprout", sk.address()); } -std::pair AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &sk) const { - return std::make_pair("sapling", sk.DefaultAddress()); +std::pair AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &xfvk) const { + return std::make_pair("sapling", xfvk.DefaultAddress()); } std::pair AddressInfoFromViewingKey::operator()(const InvalidEncoding&) const { throw std::invalid_argument("Cannot derive default address from invalid viewing key"); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 8fad4d13620..12f5a8548f0 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -19,20 +19,23 @@ const unsigned char ZCASH_HD_SEED_FP_PERSONAL[BLAKE2bPersonalBytes] = const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = {'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'}; -MnemonicSeed MnemonicSeed::Random(Language language, size_t len) +MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) { - assert(len >= 32); + assert(entropyLen >= 32); while (true) { - std::vector entropy(len, 0); - GetRandBytes(entropy.data(), len); - const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), len); + std::vector entropy(entropyLen, 0); + GetRandBytes(entropy.data(), entropyLen); + const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); std::string mnemonic(phrase); zip339_free_phrase(phrase); MnemonicSeed seed(language, mnemonic); - // TODO: check for the validity of the Sapling spending key at account 0 for this seed. - - return seed; + // Verify that the seed data is valid entropy for unified spending keys at + // account 0 and account 0x7FFFFFFE + if (libzcash::UnifiedSpendingKey::Derive(seed, bip44CoinType, 0).has_value() && + libzcash::DeriveZip32TransparentSpendingKey(seed, bip44CoinType, ZCASH_LEGACY_TRANSPARENT_ACCOUNT).has_value()) { + return seed; + } } } @@ -113,12 +116,12 @@ libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() diversifier_index_t j_default; diversifier_index_t j_ret; - CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize); + CSerializeData addr_bytes_ret(libzcash::SerializedSaplingPaymentAddressSize); if (librustzcash_zip32_find_xfvk_address( reinterpret_cast(xfvk_bytes.data()), j_default.begin(), j_ret.begin(), - reinterpret_cast(addr_bytes.data()))) { - CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION); + reinterpret_cast(addr_bytes_ret.data()))) { + CDataStream ss_addr(addr_bytes_ret, SER_NETWORK, PROTOCOL_VERSION); libzcash::SaplingPaymentAddress addr; ss_addr >> addr; return addr; @@ -161,6 +164,27 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const return xsk_i; } +std::pair SaplingExtendedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { + auto m = Master(seed); + + // We use a fixed keypath scheme of m/32'/coin_type'/account' + // Derive m/32' + auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + // Derive m/32'/coin_type' + auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + + // Derive account key at next index, skip keys already known to the wallet + auto xsk = m_32h_cth.Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); + + // Create new metadata + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + metadata.seedFp = seed.Fingerprint(); + + return std::make_pair(xsk, metadata); +} + SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const { SaplingExtendedFullViewingKey ret; @@ -173,9 +197,34 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const return ret; } -libzcash::SaplingPaymentAddress SaplingExtendedSpendingKey::DefaultAddress() const -{ - return ToXFVK().DefaultAddress(); +std::optional DeriveZip32TransparentSpendingKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { + auto rawSeed = seed.RawSeed(); + auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); + + // We use a fixed keypath scheme of m/32'/coin_type'/account' + // Derive m/32' + auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + if (!m_32h.has_value()) return std::nullopt; + + // Derive m/32'/coin_type' + auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + if (!m_32h_cth.has_value()) return std::nullopt; + + // Derive m/32'/coin_type'/account_id' + return m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); +} + +std::optional> UnifiedSpendingKey::Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { + UnifiedSpendingKey usk; + + auto transparentKey = DeriveZip32TransparentSpendingKey(seed, bip44CoinType, accountId); + if (!transparentKey.has_value()) return std::nullopt; + usk.p2pkhKey = transparentKey.value(); + + auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); + usk.saplingKey = saplingKey.first; + + return std::make_pair(usk, saplingKey.second); } std::optional ParseZip32KeypathAccount(const std::string& keyPath) { @@ -188,4 +237,4 @@ std::optional ParseZip32KeypathAccount(const std::string& keyPath } } -} +}; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 6c4a36068d7..478f67c1353 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -6,8 +6,10 @@ #define ZCASH_ZCASH_ADDRESS_ZIP32_H #include "serialize.h" +#include "key.h" #include "support/allocators/secure.h" #include "uint256.h" +#include "utiltime.h" #include "zcash/address/sapling.hpp" #include "rust/zip339.h" @@ -65,7 +67,12 @@ class MnemonicSeed: public HDSeed { seed.assign(buf, std::end(buf)); } - static MnemonicSeed Random(Language language = English, size_t len = 32); + /** + * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible + * to check that the generated seed can produce valid transparent and unified addresses at account + * numbers 0x7FFFFFFE and 0x0 respectively. + */ + static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); static std::string LanguageName(Language language) { switch (language) { @@ -142,6 +149,51 @@ class MnemonicSeed: public HDSeed { // This is not part of ZIP 32, but is here because it's linked to the HD seed. uint256 ovkForShieldingFromTaddr(HDSeed& seed); +// Key derivation metadata +class CKeyMetadata +{ +public: + static const int VERSION_BASIC=1; + static const int VERSION_WITH_HDDATA=10; + static const int CURRENT_VERSION=VERSION_WITH_HDDATA; + int nVersion; + int64_t nCreateTime; // 0 means unknown + std::string hdKeypath; //optional HD/zip32 keypath + uint256 seedFp; + + CKeyMetadata() + { + SetNull(); + } + CKeyMetadata(int64_t nCreateTime_) + { + SetNull(); + nCreateTime = nCreateTime_; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(this->nVersion); + READWRITE(nCreateTime); + if (this->nVersion >= VERSION_WITH_HDDATA) + { + READWRITE(hdKeypath); + READWRITE(seedFp); + } + } + + void SetNull() + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = 0; + hdKeypath.clear(); + seedFp.SetNull(); + } +}; + + namespace libzcash { typedef blob88 diversifier_index_t; @@ -212,13 +264,12 @@ struct SaplingExtendedSpendingKey { } static SaplingExtendedSpendingKey Master(const HDSeed& seed); + static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); SaplingExtendedSpendingKey Derive(uint32_t i) const; SaplingExtendedFullViewingKey ToXFVK() const; - libzcash::SaplingPaymentAddress DefaultAddress() const; - friend bool operator==(const SaplingExtendedSpendingKey& a, const SaplingExtendedSpendingKey& b) { return a.depth == b.depth && @@ -230,8 +281,25 @@ struct SaplingExtendedSpendingKey { } }; +class UnifiedSpendingKey { +private: + uint32_t accountId; + std::optional p2pkhKey; + std::optional saplingKey; + + UnifiedSpendingKey() {} +public: + static std::optional> Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + + const std::optional& GetSaplingExtendedSpendingKey() { + return saplingKey; + } +}; + std::optional ParseZip32KeypathAccount(const std::string& keyPath); +std::optional DeriveZip32TransparentSpendingKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + } #endif // ZCASH_ZCASH_ADDRESS_ZIP32_H diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 7a864af7e59..b5299fc5e21 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -375,7 +375,7 @@ CWalletTx CreateSaplingTxWithNoteData(const Consensus::Params& consensusParams, CBasicKeyStore& keyStore, const libzcash::SaplingExtendedSpendingKey &sk) { auto wtx = GetValidSaplingReceive(consensusParams, keyStore, sk, 10); - auto testNote = GetTestSaplingNote(sk.DefaultAddress(), 10); + auto testNote = GetTestSaplingNote(sk.ToXFVK().DefaultAddress(), 10); auto fvk = sk.expsk.full_viewing_key(); auto nullifier = testNote.note.nullifier(fvk, testNote.tree.witness().position()).value(); From f6ad523ccbc77c88730f4c5ff113a64538e0ce9b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 24 Sep 2021 14:24:41 -0600 Subject: [PATCH 077/514] Add unified full viewing keys and Zcash-internal unified addresses. --- src/uint256.h | 21 +++++++++++ src/wallet/test/rpc_wallet_tests.cpp | 40 ++++++++++++++++++--- src/zcash/address/zip32.cpp | 50 +++++++++++++++++++++++++- src/zcash/address/zip32.h | 52 ++++++++++++++++++++++++++-- 4 files changed, 156 insertions(+), 7 deletions(-) diff --git a/src/uint256.h b/src/uint256.h index 60746304d31..76a7917ce86 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -108,6 +108,16 @@ class blob88 : public base_blob<88> { public: blob88() {} blob88(const base_blob<88>& b) : base_blob<88>(b) {} + blob88(uint64_t i): base_blob<88>() { + data[0] = (uint8_t) i; + data[1] = (uint8_t) (i >> 8); + data[2] = (uint8_t) (i >> 16); + data[3] = (uint8_t) (i >> 24); + data[4] = (uint8_t) (i >> 32); + data[5] = (uint8_t) (i >> 40); + data[6] = (uint8_t) (i >> 48); + data[7] = (uint8_t) (i >> 56); + } explicit blob88(const std::vector& vch) : base_blob<88>(vch) {} std::optional increment() const { @@ -123,6 +133,17 @@ class blob88 : public base_blob<88> { return std::nullopt; } + + // treat as little-endian for numeric comparison + bool less_than_le(const blob88& other) const { + for (int i = 10; i >= 0; i--) { + if (data[i] < other.data[i]) { + return true; + } + } + + return false; + } }; /** 160-bit opaque blob. diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 771e558f15a..49e84c47bb1 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -75,6 +75,15 @@ static UniValue ValueFromString(const std::string &str) return value; } +static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) { + auto usk = pwallet->GetUnifiedSpendingKeyForAccount(0); + + return usk.value() + .ToFullViewingKey() + .GetSaplingExtendedFullViewingKey().value() + .FindAddress(libzcash::diversifier_index_t(0)).first; +} + BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, WalletTestingSetup) BOOST_AUTO_TEST_CASE(rpc_addmultisig) @@ -788,12 +797,15 @@ void CheckHaveAddr(const libzcash::PaymentAddress& addr) { BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { using namespace libzcash; - UniValue addr; - if (!pwalletMain->HaveMnemonicSeed()) { - pwalletMain->GenerateNewSeed(); + if (!pwalletMain->HaveLegacyHDSeed()) { + // fake a legacy seed by creating a separate mnemonic seed + auto seed = MnemonicSeed::Random(1); + pwalletMain->LoadLegacyHDSeed(seed); } + UniValue addr; + KeyIO keyIO(Params()); // No parameter defaults to sapling address addr = CallRPC("z_getnewaddress"); @@ -1459,7 +1471,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) // add keys manually auto taddr = pwalletMain->GenerateNewKey().GetID(); std::string taddr1 = keyIO.EncodeDestination(taddr); - auto pa = pwalletMain->GenerateNewLegacySaplingZKey().value(); + auto pa = DefaultSaplingAddress(pwalletMain); std::string zaddr1 = keyIO.EncodePaymentAddress(pa); const Consensus::Params& consensusParams = Params().GetConsensus(); @@ -1549,6 +1561,13 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys) { LOCK2(cs_main, pwalletMain->cs_wallet); + + if (!pwalletMain->HaveLegacyHDSeed()) { + // fake a legacy seed by creating a separate mnemonic seed + auto seed = MnemonicSeed::Random(1); + pwalletMain->LoadLegacyHDSeed(seed); + } + UniValue retValue; int n = 100; @@ -1605,6 +1624,13 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys) BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) { LOCK2(cs_main, pwalletMain->cs_wallet); + + if (!pwalletMain->HaveLegacyHDSeed()) { + // fake a legacy seed by creating a separate mnemonic seed + auto seed = MnemonicSeed::Random(1); + pwalletMain->LoadLegacyHDSeed(seed); + } + UniValue retValue; int n = 100; @@ -1668,6 +1694,12 @@ BOOST_AUTO_TEST_CASE(rpc_z_listunspent_parameters) { SelectParams(CBaseChainParams::TESTNET); + if (!pwalletMain->HaveLegacyHDSeed()) { + // fake a legacy seed by creating a separate mnemonic seed + auto seed = MnemonicSeed::Random(1); + pwalletMain->LoadLegacyHDSeed(seed); + } + LOCK2(cs_main, pwalletMain->cs_wallet); UniValue retValue; diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 12f5a8548f0..01a7dd32581 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -19,6 +19,8 @@ const unsigned char ZCASH_HD_SEED_FP_PERSONAL[BLAKE2bPersonalBytes] = const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = {'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'}; +const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x40000000); + MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) { assert(entropyLen >= 32); @@ -216,10 +218,11 @@ std::optional DeriveZip32TransparentSpendingKey(const HDSeed& seed, uin std::optional> UnifiedSpendingKey::Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { UnifiedSpendingKey usk; + usk.accountId = accountId; auto transparentKey = DeriveZip32TransparentSpendingKey(seed, bip44CoinType, accountId); if (!transparentKey.has_value()) return std::nullopt; - usk.p2pkhKey = transparentKey.value(); + usk.transparentKey = transparentKey.value(); auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); usk.saplingKey = saplingKey.first; @@ -227,6 +230,51 @@ std::optional> UnifiedSpendingKey::D return std::make_pair(usk, saplingKey.second); } +UnifiedFullViewingKey UnifiedSpendingKey::ToFullViewingKey() const { + UnifiedFullViewingKey ufvk; + + if (transparentKey.has_value()) { + ufvk.transparentKey = transparentKey.value().Neuter(); + } + + if (saplingKey.has_value()) { + ufvk.saplingKey = saplingKey.value().ToXFVK(); + } + + return ufvk; +} + +std::optional UnifiedFullViewingKey::Address(diversifier_index_t j) const { + ZcashdUnifiedAddress ua; + + if (transparentKey.has_value()) { + if (MAX_TRANSPARENT_CHILD_IDX.less_than_le(j)) return std::nullopt; + CExtPubKey changeKey; + if (!transparentKey.value().Derive(changeKey, 0)) { + return std::nullopt; + } + + CExtPubKey childKey; + unsigned int childIndex = (unsigned int) j.GetUint64(0); + if (changeKey.Derive(childKey, childIndex)) { + ua.transparentKey = childKey.pubkey; + } else { + return std::nullopt; + } + } + + if (saplingKey.has_value()) { + auto saplingAddress = saplingKey.value().Address(j); + if (saplingAddress.has_value()) { + ua.saplingAddress = saplingAddress.value(); + } else { + return std::nullopt; + } + } + + return ua; +} + std::optional ParseZip32KeypathAccount(const std::string& keyPath) { std::regex pattern("m/32'/[0-9]+'/([0-9]+)'"); std::smatch matches; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 478f67c1353..46eff708d44 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -281,19 +281,67 @@ struct SaplingExtendedSpendingKey { } }; +class UnifiedSpendingKey; +class UnifiedFullViewingKey; + +class ZcashdUnifiedAddress { +private: + diversifier_index_t diversifier_index; + std::optional transparentKey; //TODO: should this just be the public key hash? + std::optional saplingAddress; + + friend class UnifiedFullViewingKey; + + ZcashdUnifiedAddress() {} +public: + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingPaymentAddress() const { + return saplingAddress; + } +}; + +class UnifiedFullViewingKey { +private: + std::optional transparentKey; + std::optional saplingKey; + + UnifiedFullViewingKey() {} + + friend class UnifiedSpendingKey; +public: + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingExtendedFullViewingKey() const { + return saplingKey; + } + + std::optional Address(diversifier_index_t j) const; +}; + class UnifiedSpendingKey { private: uint32_t accountId; - std::optional p2pkhKey; + std::optional transparentKey; std::optional saplingKey; UnifiedSpendingKey() {} public: static std::optional> Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); - const std::optional& GetSaplingExtendedSpendingKey() { + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingExtendedSpendingKey() const { return saplingKey; } + + UnifiedFullViewingKey ToFullViewingKey() const; }; std::optional ParseZip32KeypathAccount(const std::string& keyPath); From 290b2e4e3972787e70336a5059af475f1bc633ec Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 24 Sep 2021 15:20:13 -0600 Subject: [PATCH 078/514] Use the default UA-based Sapling address for the saplingmigration tool. --- src/test/arith_uint256_tests.cpp | 8 ++++++++ src/uint256.h | 13 +++++-------- src/wallet/asyncrpcoperation_saplingmigration.cpp | 9 +++++---- src/wallet/wallet.cpp | 11 +++++++++-- src/zcash/address/zip32.h | 10 ++++++++++ 5 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp index a1b7e26257c..47563fdd8c1 100644 --- a/src/test/arith_uint256_tests.cpp +++ b/src/test/arith_uint256_tests.cpp @@ -564,4 +564,12 @@ BOOST_AUTO_TEST_CASE( getmaxcoverage ) // some more tests just to get 100% cover CHECKBITWISEOPERATOR(R1,~R2,&) } +BOOST_AUTO_TEST_CASE( blob88_increment ) +{ + blob88 bzero(0); + blob88 bone(1); + BOOST_CHECK(bzero.increment()); + BOOST_CHECK(bzero == bone); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/uint256.h b/src/uint256.h index 76a7917ce86..68000108d0a 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -120,18 +120,15 @@ class blob88 : public base_blob<88> { } explicit blob88(const std::vector& vch) : base_blob<88>(vch) {} - std::optional increment() const { - blob88 result = *this; - + bool increment() { for (int i = 0; i < 11; i++) { - result.data[i] += 1; - if (result.data[i] != 0) { - // no overflow - return result; + this->data[i] += 1; + if (this->data[i] != 0) { + return true; // no overflow } } - return std::nullopt; + return false; //overflow } // treat as little-endian for numeric comparison diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 5bfdb759af6..24438e6a212 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -192,6 +192,7 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl } // Unless otherwise specified, the migration destination address is the address for Sapling account 0 +// at the smallest diversifier index which produces a valid diversified address. libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { KeyIO keyIO(Params()); if (mapArgs.count("-migrationdestaddress")) { @@ -202,12 +203,12 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration return *saplingAddress; } - // TODO: use UVK-based derivation here instead. auto usk = pwalletMain->GetUnifiedSpendingKeyForAccount(0); assert(usk.has_value()); // mnemonic seeds are currently always generated to have valid USKs at account 0 - auto xsk = usk.value().GetSaplingExtendedSpendingKey(); - if (xsk.has_value()) { - return xsk.value().ToXFVK().DefaultAddress(); + auto ua = usk.value().ToFullViewingKey().FindAddress(libzcash::diversifier_index_t(0)); + auto addr = ua.first.GetSaplingPaymentAddress(); + if (addr.has_value()) { + return addr.value(); } else { // This error will only occur if Sapling address generation has been disbled for USKs from this // wallet. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bdf98e4da99..7d475dcca9b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -394,8 +394,15 @@ UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { } std::optional CWallet::GetUnifiedSpendingKeyForAccount(uint32_t accountId) { - //TODO - return std::nullopt; + auto seed = GetMnemonicSeed(); + assert(seed.has_value()); + // TODO: is there any reason to cache and not re-derive this every time? + auto usk = UnifiedSpendingKey::Derive(seed.value(), BIP44CoinType(), accountId); + if (usk.has_value()) { + return usk.value().first; + } else { + return std::nullopt; + } } void CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 46eff708d44..3aef9d96065 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -321,6 +321,16 @@ class UnifiedFullViewingKey { } std::optional Address(diversifier_index_t j) const; + + std::pair FindAddress(diversifier_index_t j) const { + auto addr = Address(j); + while (!addr.has_value()) { + if (!j.increment()) + throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; + addr = Address(j); + } + return std::make_pair(addr.value(), j); + } }; class UnifiedSpendingKey { From f99f2e4b3f0212765709c49fcd1286820378ba12 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 29 Sep 2021 10:11:29 -0600 Subject: [PATCH 079/514] Fix tests for wallet operations on legacy Sapling keys. --- src/wallet/gtest/test_wallet_zkeys.cpp | 37 +++++++++++++++----------- src/zcash/address/zip32.h | 10 +++++++ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 514fb9b35c4..f97dbe742bc 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -29,15 +29,18 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { ASSERT_EQ(0, addrs.size()); // No HD seed in the wallet - EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); + auto legacyKey = wallet.GenerateNewLegacySaplingZKey(); + ASSERT_FALSE(legacyKey.has_value()); // Load the all-zeroes seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); - wallet.LoadMnemonicSeed(seed); + wallet.LoadLegacyHDSeed(seed); // Now this call succeeds - auto address = wallet.GenerateNewLegacySaplingZKey().value(); + legacyKey = wallet.GenerateNewLegacySaplingZKey(); + ASSERT_TRUE(legacyKey.has_value()); + auto address = legacyKey.value(); // wallet should have one key wallet.GetSaplingPaymentAddresses(addrs); @@ -68,9 +71,8 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // Generate a diversified address different to the default // If we can't get an early diversified address, we are very unlucky - blob88 diversifier; - diversifier.begin()[0] = 10; - auto dpa = sk.ToXFVK().Address(diversifier).value(); + blob88 diversifier(2); + auto dpa = sk.ToXFVK().FindAddress(diversifier).first; // verify wallet only has the default address EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.ToXFVK().DefaultAddress())); @@ -98,7 +100,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { ASSERT_EQ(wallet.mapSaplingZKeyMetadata[ivk2].nCreateTime, now); // Load a diversified address for the third key into the wallet - auto dpa2 = sk2.ToXFVK().Address(diversifier).value(); + auto dpa2 = sk2.ToXFVK().FindAddress(diversifier).first; EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk2.ToXFVK().DefaultAddress())); EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa2)); EXPECT_TRUE(wallet.LoadSaplingPaymentAddress(dpa2, ivk2)); @@ -411,7 +413,7 @@ TEST(WalletZkeysTest, WriteCryptedzkeyDirectToDb) { /** * This test covers methods on CWalletDB to load/save crypted sapling z keys. */ -TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { +TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { SelectParams(CBaseChainParams::TESTNET); // Get temporary and unique path for file. @@ -428,8 +430,12 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // No default CPubKey set ASSERT_TRUE(fFirstRun); - ASSERT_FALSE(wallet.HaveMnemonicSeed()); - wallet.GenerateNewSeed(); + ASSERT_FALSE(wallet.HaveLegacyHDSeed()); + + // Load the all-zeroes seed as the legacy seed + std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + MnemonicSeed seed(English, mnemonic); + wallet.LoadLegacyHDSeed(seed); // wallet should be empty std::set addrs; @@ -447,9 +453,8 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // If we can't get an early diversified address, we are very unlucky libzcash::SaplingExtendedSpendingKey extsk; EXPECT_TRUE(wallet.GetSaplingExtendedSpendingKey(address, extsk)); - blob88 diversifier; - diversifier.begin()[0] = 10; - auto dpa = extsk.ToXFVK().Address(diversifier).value(); + blob88 diversifier(2); + auto dpa = extsk.ToXFVK().FindAddress(diversifier).first; // Add diversified address to the wallet auto ivk = extsk.expsk.full_viewing_key().in_viewing_key(); @@ -462,11 +467,13 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_TRUE(wallet.EncryptWallet(strWalletPass)); // adding a new key will fail as the wallet is locked - EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); + EXPECT_FALSE(wallet.GenerateNewLegacySaplingZKey().has_value()); // unlock wallet and then add wallet.Unlock(strWalletPass); - auto address2 = wallet.GenerateNewLegacySaplingZKey().value(); + auto address2Opt = wallet.GenerateNewLegacySaplingZKey(); + EXPECT_TRUE(address2Opt.has_value()); + auto address2 = address2Opt.value(); // flush the wallet to prevent race conditions wallet.Flush(); diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 3aef9d96065..3e019e49e50 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -225,6 +225,16 @@ struct SaplingExtendedFullViewingKey { // given this xfvk. std::optional Address(diversifier_index_t j) const; + std::pair FindAddress(diversifier_index_t j) const { + auto addr = Address(j); + while (!addr.has_value()) { + if (!j.increment()) + throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; + addr = Address(j); + } + return std::make_pair(addr.value(), j); + } + libzcash::SaplingPaymentAddress DefaultAddress() const; friend inline bool operator==(const SaplingExtendedFullViewingKey& a, const SaplingExtendedFullViewingKey& b) { From 8f1d58f16fb894c493811ec4ea08b1e7398a78bf Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 7 Oct 2021 15:08:08 -0600 Subject: [PATCH 080/514] Remove unused forward declaration. --- src/wallet/wallet.cpp | 4 ++-- src/wallet/wallet.h | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7d475dcca9b..81f7fd33dd8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -144,14 +144,14 @@ std::optional CWallet::GenerateNewLegacySaplingZKey() { } else { // Update the persisted chain information if (fFileBacked && !CWalletDB(strWalletFile).WriteLegacyHDChain(hdChain)) { - throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Writing HD chain model failed"); + throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): Writing HD chain model failed"); } auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key(); mapSaplingZKeyMetadata[ivk] = xsk.second; if (!AddSaplingZKey(xsk.first)) { - throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); + throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): AddSaplingZKey failed"); } return xsk.first.ToXFVK().DefaultAddress(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f173dfd9222..6948325dcf0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1058,15 +1058,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * Sapling ZKeys */ - //! Generates new Sapling key given the specified HD seed and account id - //! and persists it to the wallet. - // - //! Returns the newly generated extended spending key, or `std::nullopt` - //! if a key corresponding to the specified account id already exists in - //! the wallet. - std::optional GenerateNewSaplingZKey( - const HDSeed& seed, - uint32_t accountId); //! Generates new Sapling key using the legacy HD seed (if one is available) //! and legacy account counter, stores the newly generated spending key to //! the wallet, and returns the default address for the newly generated key. From daf443c9e6ec755227b65166f068afef05e59f4e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 13 Oct 2021 09:15:34 -0600 Subject: [PATCH 081/514] Update librustzcash dependency version. --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 10 +++++----- src/rust/src/rustzcash.rs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e89ab76620..6c1c1ea3bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,7 +534,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "blake2b_simd", "byteorder", @@ -543,7 +543,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "blake2b_simd", ] @@ -1896,7 +1896,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "bech32", "blake2b_simd", @@ -1908,7 +1908,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "byteorder", "nonempty", @@ -1917,7 +1917,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "bigint", "blake2b_simd", @@ -1927,7 +1927,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "blake2b_simd", "byteorder", @@ -1942,7 +1942,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "aes", "bip0039", @@ -1976,7 +1976,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=3910168cf1cfbca9342411881967bfa1579a1826#3910168cf1cfbca9342411881967bfa1579a1826" +source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 9219db27d1b..efcd9529fe9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,8 +71,8 @@ codegen-units = 1 ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } -zcash_address = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } -zcash_history = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } -zcash_note_encryption = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } -zcash_primitives = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } -zcash_proofs = { git = "https://github.com/nuttycom/librustzcash.git", rev = "3910168cf1cfbca9342411881967bfa1579a1826" } +zcash_address = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } +zcash_history = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } +zcash_note_encryption = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } +zcash_primitives = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } +zcash_proofs = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index c01efd10153..95f1da1ff82 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -1111,7 +1111,7 @@ pub extern "C" fn librustzcash_zip32_find_xfvk_address( let j = zip32::DiversifierIndex(unsafe { *j }); match xfvk.find_address(j) { - Ok((j, addr)) => { + Some((j, addr)) => { let j_ret = unsafe { &mut *j_ret }; let addr_ret = unsafe { &mut *addr_ret }; @@ -1120,7 +1120,7 @@ pub extern "C" fn librustzcash_zip32_find_xfvk_address( true } - Err(_) => false, + None => false, } } From 666d135e2e70cc75ddc7030f340dbbb34072cb1a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 13 Oct 2021 13:59:06 -0600 Subject: [PATCH 082/514] Apply suggestions from code review Co-authored-by: Daira Hopwood --- src/gtest/test_keystore.cpp | 12 ++++---- src/rust/include/librustzcash.h | 28 +++++++++++++++++-- src/test/arith_uint256_tests.cpp | 8 +++--- .../asyncrpcoperation_saplingmigration.cpp | 2 +- src/wallet/crypter.cpp | 20 ++++--------- src/wallet/gtest/test_wallet_zkeys.cpp | 3 ++ 6 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 03a9f082ffc..5f4ea3ab0ea 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -14,7 +14,7 @@ #define MAKE_STRING(x) std::string((x), (x)+sizeof(x)) -const uint32_t BIP44_TESTNET_TYPE = 1; +const uint32_t SLIP44_TESTNET_TYPE = 1; TEST(KeystoreTests, StoreAndRetrieveHDSeed) { CBasicKeyStore keyStore; @@ -25,7 +25,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeed) { EXPECT_FALSE(seedOut.has_value()); // Generate a random seed - auto seed = MnemonicSeed::Random(BIP44_TESTNET_TYPE); + auto seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); // We should be able to set and retrieve the seed ASSERT_TRUE(keyStore.SetMnemonicSeed(seed)); @@ -35,7 +35,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeed) { EXPECT_EQ(seed, seedOut.value()); // Generate another random seed - auto seed2 = MnemonicSeed::Random(BIP44_TESTNET_TYPE); + auto seed2 = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); EXPECT_NE(seed, seed2); // We should not be able to set and retrieve a different seed @@ -291,7 +291,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { GetRandBytes(vMasterKey.data(), 32); // 1) Test adding a seed to an unencrypted key store, then encrypting it - auto seed = MnemonicSeed::Random(BIP44_TESTNET_TYPE); + auto seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); EXPECT_FALSE(keyStore.HaveMnemonicSeed()); auto seedOut = keyStore.GetMnemonicSeed(); EXPECT_FALSE(seedOut.has_value()); @@ -323,7 +323,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { EXPECT_EQ(seed, seedOut.value()); // 2) Test replacing the seed in an already-encrypted key store fails - auto seed2 = MnemonicSeed::Random(BIP44_TESTNET_TYPE); + auto seed2 = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); EXPECT_FALSE(keyStore.SetMnemonicSeed(seed2)); EXPECT_TRUE(keyStore.HaveMnemonicSeed()); seedOut = keyStore.GetMnemonicSeed(); @@ -343,7 +343,7 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { seedOut = keyStore2.GetMnemonicSeed(); EXPECT_FALSE(seedOut.has_value()); - auto seed3 = MnemonicSeed::Random(BIP44_TESTNET_TYPE); + auto seed3 = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); ASSERT_TRUE(keyStore2.SetMnemonicSeed(seed3)); EXPECT_TRUE(keyStore2.HaveMnemonicSeed()); seedOut = keyStore2.GetMnemonicSeed(); diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index 31de03ad04d..94d49535fc1 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -306,14 +306,38 @@ extern "C" { unsigned char *xfvk_i ); - /// Derive a PaymentAddress from an ExtendedFullViewingKey. + /** + * Derive a PaymentAddress from an ExtendedFullViewingKey. Returns 'false' + * if no valid address can be derived for the specified diversifier index. + * + * Arguments: + * - xfvk: [c_uchar; 169] the serialized form of a Sapling extended full viewing key + * - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching, + * encoded in little-endian order + * - addr_ret: [c_uchar; 43] array to which the returned address will be written, + * if the specified diversifier index `j` produces a valid address. + */ bool librustzcash_zip32_xfvk_address( const unsigned char *xfvk, const unsigned char *j, unsigned char *addr_ret ); - /// Derive a PaymentAddress from an ExtendedFullViewingKey. + /** + * Derive a PaymentAddress from an ExtendedFullViewingKey by searching the + * space of valid diversifiers starting at diversifier index `j`. This will + * always return a valid address along with the diversifier index that produced + * the address unless no addresses can be derived at any diversifier index >= `j`, + * in which case this function will return `false`. + * + * Arguments: + * - xfvk: [c_uchar; 169] the serialized form of a Sapling extended full viewing key + * - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching, + * encoded in little-endian order + * - j_ret: [c_uchar; 11] array that will store the diversifier index at which the + * returned address was found + * - addr_ret: [c_uchar; 43] array to which the returned address will be written + */ bool librustzcash_zip32_find_xfvk_address( const unsigned char *xfvk, const unsigned char *j, diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp index 47563fdd8c1..8928871bf87 100644 --- a/src/test/arith_uint256_tests.cpp +++ b/src/test/arith_uint256_tests.cpp @@ -566,10 +566,10 @@ BOOST_AUTO_TEST_CASE( getmaxcoverage ) // some more tests just to get 100% cover BOOST_AUTO_TEST_CASE( blob88_increment ) { - blob88 bzero(0); - blob88 bone(1); - BOOST_CHECK(bzero.increment()); - BOOST_CHECK(bzero == bone); + blob88 b_zero(0); + blob88 b_one(1); + BOOST_CHECK(b_zero.increment()); + BOOST_CHECK(b_zero == b_one); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 24438e6a212..8da4c281452 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -192,7 +192,7 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl } // Unless otherwise specified, the migration destination address is the address for Sapling account 0 -// at the smallest diversifier index which produces a valid diversified address. +// at the smallest diversifier index that produces a valid diversified address. libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { KeyIO keyIO(Params()); if (mapArgs.count("-migrationdestaddress")) { diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 91180317e43..3f5d1c7b793 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -148,17 +148,14 @@ static std::optional DecryptMnemonicSeed( // Use seed's fingerprint as IV // TODO: Handle IV properly when we make encryption a supported feature - if(DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) { + if (DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) { CSecureDataStream ss(vchSecret, SER_NETWORK, PROTOCOL_VERSION); auto seed = MnemonicSeed::Read(ss); if (seed.Fingerprint() == seedFp) { return seed; - } else { - return std::nullopt; } - } else { - return std::nullopt; } + return std::nullopt; } static std::optional DecryptLegacyHDSeed( @@ -170,16 +167,13 @@ static std::optional DecryptLegacyHDSeed( // Use seed's fingerprint as IV // TODO: Handle IV properly when we make encryption a supported feature - if(DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) { + if (DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) { auto seed = HDSeed(vchSecret); if (seed.Fingerprint() == seedFp) { return seed; - } else { - return std::nullopt; } - } else { - return std::nullopt; } + return std::nullopt; } static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) @@ -265,8 +259,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) if (!cryptedMnemonicSeed.first.IsNull()) { // Check that we can successfully decrypt the mnemonic seed, if present auto seed = DecryptMnemonicSeed(vMasterKeyIn, cryptedMnemonicSeed.second, cryptedMnemonicSeed.first); - if (!seed.has_value()) - { + if (!seed.has_value()) { keyFail = true; } else { keyPass = true; @@ -277,8 +270,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) auto seed = DecryptLegacyHDSeed(vMasterKeyIn, cryptedLegacySeed.value().second, cryptedLegacySeed.value().first); - if (!seed.has_value()) - { + if (!seed.has_value()) { keyFail = true; } else { keyPass = true; diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index f97dbe742bc..49540be6cf2 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -35,6 +35,9 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // Load the all-zeroes seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); + // The legacy seed used to be automatically derived from randomness; since + // non-mnemonic random generation has been removed we just use the + // all-zeros mnemonic for these tests. wallet.LoadLegacyHDSeed(seed); // Now this call succeeds From 2885ae76433d28b7d5cc1a0f28deb54e1fdaad36 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 18 Oct 2021 19:47:28 -0600 Subject: [PATCH 083/514] Derive transparent keys from mnemonic seed. --- src/bench/verification.cpp | 3 +- src/init.cpp | 2 +- src/key.cpp | 17 +-- src/key.h | 17 +-- src/test/DoS_tests.cpp | 3 +- src/test/multisig_tests.cpp | 6 +- src/test/sanity_tests.cpp | 2 +- src/test/script_P2SH_tests.cpp | 6 +- src/test/script_standard_tests.cpp | 17 ++- src/test/script_tests.cpp | 19 ++-- src/test/sigopcount_tests.cpp | 3 +- src/test/test_bitcoin.cpp | 2 +- src/test/transaction_tests.cpp | 11 +- src/wallet/asyncrpcoperation_sendmany.cpp | 21 +++- src/wallet/rpcwallet.cpp | 6 +- src/wallet/test/rpc_wallet_tests.cpp | 8 +- src/wallet/wallet.cpp | 84 +++++++++------ src/wallet/wallet.h | 6 +- src/wallet/walletdb.h | 17 ++- src/zcash/address/zip32.cpp | 124 +++++++++++++++++----- src/zcash/address/zip32.h | 30 +++++- src/zcbenchmarks.cpp | 3 +- 22 files changed, 270 insertions(+), 137 deletions(-) diff --git a/src/bench/verification.cpp b/src/bench/verification.cpp index ba7aea1c2e1..19c0b27d996 100644 --- a/src/bench/verification.cpp +++ b/src/bench/verification.cpp @@ -25,8 +25,7 @@ static void ECDSA(benchmark::State& state) mtx.nVersion = SAPLING_TX_VERSION; mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; - CKey key; - key.MakeNewKey(false); + CKey key = CKey::TestOnlyRandomKey(false); CBasicKeyStore keystore; keystore.AddKeyPubKey(key, key.GetPubKey()); CKeyID hash = key.GetPubKey().GetID(); diff --git a/src/init.cpp b/src/init.cpp index ab58e98aac1..65415a5a002 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -684,7 +684,7 @@ void ThreadImport(std::vector vImportFiles, const CChainParams& chainp */ bool InitSanityCheck(void) { - if(!ECC_InitSanityCheck()) { + if(!CKey::ECC_InitSanityCheck()) { InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } diff --git a/src/key.cpp b/src/key.cpp index ff52e5eea0a..75dbcd33cea 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -156,12 +156,14 @@ bool CKey::Check(const unsigned char *vch) { return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch); } -void CKey::MakeNewKey(bool fCompressedIn) { +CKey CKey::TestOnlyRandomKey(bool fCompressedIn) { + CKey key; do { - GetRandBytes(keydata.data(), keydata.size()); - } while (!Check(keydata.data())); - fValid = true; - fCompressed = fCompressedIn; + GetRandBytes(key.keydata.data(), key.keydata.size()); + } while (!Check(key.keydata.data())); + key.fValid = true; + key.fCompressed = fCompressedIn; + return key; } bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { @@ -330,9 +332,8 @@ void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) { key.Set(code+42, code+BIP32_EXTKEY_SIZE, true); } -bool ECC_InitSanityCheck() { - CKey key; - key.MakeNewKey(true); +bool CKey::ECC_InitSanityCheck() { + CKey key = CKey::TestOnlyRandomKey(true); CPubKey pubkey = key.GetPubKey(); return key.VerifyPubKey(pubkey); } diff --git a/src/key.h b/src/key.h index 2a9ee32c59b..4f48f3bbd5f 100644 --- a/src/key.h +++ b/src/key.h @@ -68,7 +68,17 @@ class CKey keydata.resize(32); } - static std::optional FromEntropy(std::vector> keydata); + /** + * Construct a random key. This is used only for internal sanity checks; + * all keys that actually control live funds should be derived from the + * wallet's mnemonic seed. + */ + static CKey TestOnlyRandomKey(bool fCompressedIn); + + static std::optional FromEntropy(std::vector> keydata, bool fComporessedIn); + + /** Check that required EC support is available at runtime. */ + static bool ECC_InitSanityCheck(); friend bool operator==(const CKey& a, const CKey& b) { @@ -106,9 +116,6 @@ class CKey //! Initialize from a CPrivKey (serialized OpenSSL-format private key data). bool SetPrivKey(const CPrivKey& vchPrivKey, bool fCompressed); - //! Generate a new private key using a cryptographic PRNG. - void MakeNewKey(bool fCompressed); - /** * Convert the private key to a CPrivKey (serialized OpenSSL-format private key data). * This is expensive. @@ -199,7 +206,5 @@ void ECC_Start(void); /** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */ void ECC_Stop(void); -/** Check that required EC support is available at runtime. */ -bool ECC_InitSanityCheck(void); #endif // BITCOIN_KEY_H diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index b006c2160ae..ff13b80a9a4 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -126,8 +126,7 @@ BOOST_DATA_TEST_CASE(DoS_mapOrphans, boost::unit_test::data::xrange(static_cast< { uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId; - CKey key; - key.MakeNewKey(true); + CKey key = CKey::TestOnlyRandomKey(true); CBasicKeyStore keystore; keystore.AddKey(key); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 2aabbb3d220..2986a83595a 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -49,7 +49,7 @@ BOOST_DATA_TEST_CASE(multisig_verify, boost::unit_test::data::xrange(static_cast CKey key[4]; CAmount amount = 0; for (int i = 0; i < 4; i++) - key[i].MakeNewKey(true); + key[i] = CKey::TestOnlyRandomKey(true); CScript a_and_b; a_and_b << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) { CKey key[4]; for (int i = 0; i < 4; i++) - key[i].MakeNewKey(true); + key[i] = CKey::TestOnlyRandomKey(true); txnouttype whichType; @@ -191,7 +191,7 @@ BOOST_DATA_TEST_CASE(multisig_Sign, boost::unit_test::data::xrange(static_cast(Conse CKey key[4]; for (int i = 0; i < 4; i++) { - key[i].MakeNewKey(true); + key[i] = CKey::TestOnlyRandomKey(true); keystore.AddKey(key[i]); } @@ -170,7 +170,7 @@ BOOST_DATA_TEST_CASE(set, boost::unit_test::data::xrange(static_cast(Consen std::vector keys; for (int i = 0; i < 4; i++) { - key[i].MakeNewKey(true); + key[i] = CKey::TestOnlyRandomKey(true); keystore.AddKey(key[i]); keys.push_back(key[i].GetPubKey()); } @@ -282,7 +282,7 @@ BOOST_DATA_TEST_CASE(AreInputsStandard, boost::unit_test::data::xrange(static_ca vector keys; for (int i = 0; i < 6; i++) { - key[i].MakeNewKey(true); + key[i] = CKey::TestOnlyRandomKey(true); keystore.AddKey(key[i]); } for (int i = 0; i < 3; i++) diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 0196b4b5b96..2a4ee144361 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -22,7 +22,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) CKey keys[3]; CPubKey pubkeys[3]; for (int i = 0; i < 3; i++) { - keys[i].MakeNewKey(true); + keys[i] = CKey::TestOnlyRandomKey(true); pubkeys[i] = keys[i].GetPubKey(); } @@ -103,9 +103,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) { - CKey key; + CKey key = CKey::TestOnlyRandomKey(true); CPubKey pubkey; - key.MakeNewKey(true); pubkey = key.GetPubKey(); CScript s; @@ -165,9 +164,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) { - CKey key; + CKey key = CKey::TestOnlyRandomKey(true); CPubKey pubkey; - key.MakeNewKey(true); pubkey = key.GetPubKey(); CScript s; @@ -221,7 +219,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) CKey keys[3]; CPubKey pubkeys[3]; for (int i = 0; i < 3; i++) { - keys[i].MakeNewKey(true); + keys[i] = CKey::TestOnlyRandomKey(true); pubkeys[i] = keys[i].GetPubKey(); } @@ -297,7 +295,7 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) CKey keys[3]; CPubKey pubkeys[3]; for (int i = 0; i < 3; i++) { - keys[i].MakeNewKey(true); + keys[i] = CKey::TestOnlyRandomKey(true); pubkeys[i] = keys[i].GetPubKey(); } @@ -343,12 +341,11 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CKey keys[2]; CPubKey pubkeys[2]; for (int i = 0; i < 2; i++) { - keys[i].MakeNewKey(true); + keys[i] = CKey::TestOnlyRandomKey(true); pubkeys[i] = keys[i].GetPubKey(); } - CKey uncompressedKey; - uncompressedKey.MakeNewKey(false); + CKey uncompressedKey = CKey::TestOnlyRandomKey(false); CPubKey uncompressedPubkey = uncompressedKey.GetPubKey(); CScript scriptPubKey; diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 6ed81ade652..5ce70486a24 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -701,10 +701,9 @@ BOOST_DATA_TEST_CASE(script_CHECKMULTISIG12, boost::unit_test::data::xrange(stat uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId; ScriptError err; - CKey key1, key2, key3; - key1.MakeNewKey(true); - key2.MakeNewKey(false); - key3.MakeNewKey(true); + CKey key1 = CKey::TestOnlyRandomKey(true); + CKey key2 = CKey::TestOnlyRandomKey(false); + CKey key3 = CKey::TestOnlyRandomKey(true); CScript scriptPubKey12; scriptPubKey12 << OP_1 << ToByteVector(key1.GetPubKey()) << ToByteVector(key2.GetPubKey()) << OP_2 << OP_CHECKMULTISIG; @@ -734,11 +733,10 @@ BOOST_DATA_TEST_CASE(script_CHECKMULTISIG23, boost::unit_test::data::xrange(stat uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId; ScriptError err; - CKey key1, key2, key3, key4; - key1.MakeNewKey(true); - key2.MakeNewKey(false); - key3.MakeNewKey(true); - key4.MakeNewKey(false); + CKey key1 = CKey::TestOnlyRandomKey(true); + CKey key2 = CKey::TestOnlyRandomKey(false); + CKey key3 = CKey::TestOnlyRandomKey(true); + CKey key4 = CKey::TestOnlyRandomKey(false); CScript scriptPubKey23; scriptPubKey23 << OP_2 << ToByteVector(key1.GetPubKey()) << ToByteVector(key2.GetPubKey()) << ToByteVector(key3.GetPubKey()) << OP_3 << OP_CHECKMULTISIG; @@ -812,8 +810,7 @@ BOOST_DATA_TEST_CASE(script_combineSigs, boost::unit_test::data::xrange(static_c vector pubkeys; for (int i = 0; i < 3; i++) { - CKey key; - key.MakeNewKey(i%2 == 1); + CKey key = CKey::TestOnlyRandomKey(i%2 == 1); keys.push_back(key); pubkeys.push_back(key.GetPubKey()); keystore.AddKey(key); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index b4c6aee8068..58b88e686ba 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -47,8 +47,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount) std::vector keys; for (int i = 0; i < 3; i++) { - CKey k; - k.MakeNewKey(true); + CKey k = CKey::TestOnlyRandomKey(true); keys.push_back(k.GetPubKey()); } CScript s2 = GetScriptForMultisig(1, keys); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index df5d0a06ee7..f3360fe19fb 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -130,7 +130,7 @@ TestingSetup::~TestingSetup() TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { // Generate a 100-block chain: - coinbaseKey.MakeNewKey(true); + coinbaseKey = CKey::TestOnlyRandomKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; for (int i = 0; i < COINBASE_MATURITY; i++) { diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index dfedf1be212..2c275849845 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -270,7 +270,7 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) CKey key[4]; for (int i = 0; i < 4; i++) { - key[i].MakeNewKey(i % 2); + key[i] = CKey::TestOnlyRandomKey(i % 2); keystoreRet.AddKey(key[i]); } @@ -639,8 +639,7 @@ BOOST_AUTO_TEST_CASE(test_big_overwinter_transaction) { mtx.nVersion = OVERWINTER_TX_VERSION; mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; - CKey key; - key.MakeNewKey(false); + CKey key = CKey::TestOnlyRandomKey(false); CBasicKeyStore keystore; keystore.AddKeyPubKey(key, key.GetPubKey()); CKeyID hash = key.GetPubKey().GetID(); @@ -731,8 +730,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vin[0].scriptSig << std::vector(65, 0); t.vout.resize(1); t.vout[0].nValue = 90*CENT; - CKey key; - key.MakeNewKey(true); + CKey key = CKey::TestOnlyRandomKey(true); t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); string reason; @@ -826,8 +824,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandardV2) t.vin[0].scriptSig << std::vector(65, 0); t.vout.resize(1); t.vout[0].nValue = 90*CENT; - CKey key; - key.MakeNewKey(true); + CKey key = CKey::TestOnlyRandomKey(true); t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); string reason; diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 155afd70087..4a2228e3518 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -865,15 +865,26 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase, TxValues& txVa return t_inputs_.size() > 0; } +/** + * Compute a dust threshold based upon a standard p2pkh txout. + */ +CAmount DefaultDustThreshold(const CFeeRate& minRelayTxFee) { + // Use the all-zeros seed, we're only constructing a key in order + // to get a txout from which we can obtain the dust threshold. + // Master key generation results in a compressed key. + RawHDSeed seed(64, 0x00); + CKey secret = CExtKey::Master(seed.data(), 64).key; + CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID()); + CTxOut txout(CAmount(1), scriptPubKey); + return txout.GetDustThreshold(minRelayTxFee); +} + bool AsyncRPCOperation_sendmany::load_inputs(TxValues& txValues) { // If from address is a taddr, select UTXOs to spend CAmount selectedUTXOAmount = 0; + // Get dust threshold - CKey secret; - secret.MakeNewKey(true); - CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID()); - CTxOut out(CAmount(1), scriptPubKey); - CAmount dustThreshold = out.GetDustThreshold(minRelayTxFee); + CAmount dustThreshold = DefaultDustThreshold(minRelayTxFee); CAmount dustChange = -1; std::vector selectedTInputs; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5193133058f..c9a7a21692f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -167,10 +167,10 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) pwalletMain->TopUpKeyPool(); // Generate a new key that is added to wallet - CPubKey newKey; - if (!pwalletMain->GetKeyFromPool(newKey)) + std::optional newKey = pwalletMain->GetKeyFromPool(); + if (!newKey.has_value()) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - CKeyID keyID = newKey.GetID(); + CKeyID keyID = newKey.value().GetID(); std::string dummy_account; pwalletMain->SetAddressBook(keyID, dummy_account, "receive"); diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 49e84c47bb1..7d7a1714437 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) LOCK2(cs_main, pwalletMain->cs_wallet); - CPubKey demoPubkey = pwalletMain->GenerateNewKey(); + CPubKey demoPubkey = pwalletMain->GenerateNewKey().value(); CTxDestination demoAddress(CTxDestination(demoPubkey.GetID())); UniValue retValue; string strPurpose = "receive"; @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) pwalletMain->SetAddressBook(demoPubkey.GetID(), "", strPurpose); }); - CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey(); + CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey().value(); CTxDestination setaccountDemoAddress(CTxDestination(setaccountDemoPubkey.GetID())); /********************************* @@ -1469,7 +1469,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) KeyIO keyIO(Params()); // add keys manually - auto taddr = pwalletMain->GenerateNewKey().GetID(); + auto taddr = pwalletMain->GenerateNewKey().value().GetID(); std::string taddr1 = keyIO.EncodeDestination(taddr); auto pa = DefaultSaplingAddress(pwalletMain); std::string zaddr1 = keyIO.EncodePaymentAddress(pa); @@ -2173,7 +2173,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals) void TestWTxStatus(const Consensus::Params consensusParams, const int delta) { auto AddTrx = [&consensusParams]() { - auto taddr = pwalletMain->GenerateNewKey().GetID(); + auto taddr = pwalletMain->GenerateNewKey().value().GetID(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, 1); CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(taddr) << OP_EQUALVERIFY << OP_CHECKSIG; mtx.vout.push_back(CTxOut(5 * COIN, scriptPubKey)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 81f7fd33dd8..a02a6aa253a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -245,30 +245,50 @@ bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key) return true; } -CPubKey CWallet::GenerateNewKey() +std::optional CWallet::GenerateNewKey() { AssertLockHeld(cs_wallet); // mapKeyMetadata - bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + auto seedOpt = GetMnemonicSeed(); + CHDChain& hdChain = mnemonicHDChain.value(); + if (seedOpt.has_value()) { + // All mnemonic seeds are checked at construction to ensure that we can obtain + // a valid spending key for the account ZCASH_LEGACY_TRANSPARENT_ACCOUNT; + // therefore, the `value()` call here is safe. + BIP32AccountChains accountChains = BIP32AccountChains::ForAccount( + seedOpt.value(), + BIP44CoinType(), + ZCASH_LEGACY_TRANSPARENT_ACCOUNT).value(); - CKey secret; - secret.MakeNewKey(fCompressed); + while (true) { + auto extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); + hdChain.IncrementLegacyTKeyCounter(); - // Compressed public keys were introduced in version 0.6.0 - if (fCompressed) - SetMinVersion(FEATURE_COMPRPUBKEY); + // if we did not successfully generate a key, try again. + if (extKey.has_value()) { + CKey secret = extKey.value().first.key; + CPubKey pubkey = secret.GetPubKey(); + assert(secret.VerifyPubKey(pubkey)); - CPubKey pubkey = secret.GetPubKey(); - assert(secret.VerifyPubKey(pubkey)); + // Create new metadata + const CKeyMetadata& keyMeta = extKey.value().second; + mapKeyMetadata[pubkey.GetID()] = keyMeta; + if (!nTimeFirstKey || keyMeta.nCreateTime < nTimeFirstKey) + nTimeFirstKey = keyMeta.nCreateTime; - // Create new metadata - int64_t nCreationTime = GetTime(); - mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime); - if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) - nTimeFirstKey = nCreationTime; + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); + + // Update the persisted chain information + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { + throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); + } - if (!AddKeyPubKey(secret, pubkey)) - throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); - return pubkey; + return pubkey; + } + } + } else { + return std::nullopt; + } } bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) @@ -375,9 +395,9 @@ UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet."); } - auto hdChain = GetMnemonicHDChain().value(); + CHDChain& hdChain = mnemonicHDChain.value(); while (true) { - auto usk = UnifiedSpendingKey::Derive(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter()); + auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter()); hdChain.IncrementAccountCounter(); if (usk.has_value()) { @@ -397,7 +417,7 @@ std::optional CWallet::GetUnifiedSpendingKeyForAcc auto seed = GetMnemonicSeed(); assert(seed.has_value()); // TODO: is there any reason to cache and not re-derive this every time? - auto usk = UnifiedSpendingKey::Derive(seed.value(), BIP44CoinType(), accountId); + auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); if (usk.has_value()) { return usk.value().first; } else { @@ -4185,7 +4205,10 @@ bool CWallet::NewKeyPool() for (int i = 0; i < nKeys; i++) { int64_t nIndex = i+1; - walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); + auto key = GenerateNewKey(); + if (!key.has_value()) + return false; // should have been caught by the `IsLocked` call. + walletdb.WritePool(nIndex, CKeyPool(key.value())); setKeyPool.insert(nIndex); } LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); @@ -4215,7 +4238,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) int64_t nEnd = 1; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; - if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + auto newKey = GenerateNewKey(); + if (!newKey.has_value() || !walletdb.WritePool(nEnd, CKeyPool(newKey.value()))) throw runtime_error("TopUpKeyPool(): writing generated key failed"); setKeyPool.insert(nEnd); LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); @@ -4272,7 +4296,7 @@ void CWallet::ReturnKey(int64_t nIndex) LogPrintf("keypool return %d\n", nIndex); } -bool CWallet::GetKeyFromPool(CPubKey& result) +std::optional CWallet::GetKeyFromPool() { int64_t nIndex = 0; CKeyPool keypool; @@ -4281,14 +4305,12 @@ bool CWallet::GetKeyFromPool(CPubKey& result) ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { - if (IsLocked()) return false; - result = GenerateNewKey(); - return true; + if (IsLocked()) return std::nullopt; + return GenerateNewKey(); } KeepKey(nIndex); - result = keypool.vchPubKey; + return keypool.vchPubKey; } - return true; } int64_t CWallet::GetOldestKeyPoolTime() @@ -4858,9 +4880,9 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches if (fFirstRun) { // Create new keyUser and set as default key - CPubKey newDefaultKey; - if (walletInstance->GetKeyFromPool(newDefaultKey)) { - walletInstance->SetDefaultKey(newDefaultKey); + std::optional newDefaultKey = walletInstance->GetKeyFromPool(); + if (newDefaultKey.has_value()) { + walletInstance->SetDefaultKey(newDefaultKey.value()); if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) return UIError(_("Cannot write default address") += "\n"); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6948325dcf0..35e13bcaf6b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -993,7 +993,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * keystore implementation * Generate a new key */ - CPubKey GenerateNewKey(); + std::optional GenerateNewKey(); //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); //! Adds a key to the store, without saving it to disk (used by LoadWallet) @@ -1161,7 +1161,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool); void KeepKey(int64_t nIndex); void ReturnKey(int64_t nIndex); - bool GetKeyFromPool(CPubKey &key); + std::optional GetKeyFromPool(); int64_t GetOldestKeyPoolTime(); void GetAllReserveKeys(std::set& setAddress) const; @@ -1318,7 +1318,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Set the metadata for the mnemonic HD seed (chain child index counters) */ void SetMnemonicHDChain(const CHDChain& chain, bool memonly); - const std::optional GetMnemonicHDChain() const { return mnemonicHDChain; } + const std::optional& GetMnemonicHDChain() const { return mnemonicHDChain; } /* Set the metadata for the legacy HD seed (chain child index counters) */ void SetLegacyHDChain(const CHDChain& chain, bool memonly); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index a3abf37bc3f..aef6789cd43 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -49,6 +49,7 @@ class CHDChain uint256 seedFp; int64_t nCreateTime; // 0 means unknown uint32_t accountCounter; + uint32_t legacyTKeyCounter; CHDChain() { SetNull(); } @@ -58,12 +59,13 @@ class CHDChain seedFp.SetNull(); nCreateTime = 0; accountCounter = 0; + legacyTKeyCounter = 0; } public: static const int VERSION_HD_BASE = 1; static const int CURRENT_VERSION = VERSION_HD_BASE; - CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0) {} + CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0) {} ADD_SERIALIZE_METHODS; @@ -74,6 +76,7 @@ class CHDChain READWRITE(seedFp); READWRITE(nCreateTime); READWRITE(accountCounter); + READWRITE(legacyTKeyCounter); } template @@ -91,8 +94,16 @@ class CHDChain return accountCounter; } - uint32_t IncrementAccountCounter() { - return ++accountCounter; + void IncrementAccountCounter() { + accountCounter += 1; + } + + uint32_t GetLegacyTKeyCounter() { + return legacyTKeyCounter; + } + + void IncrementLegacyTKeyCounter() { + legacyTKeyCounter += 1; } }; diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 01a7dd32581..fe09bc18358 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -24,7 +24,7 @@ const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x40000000); MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) { assert(entropyLen >= 32); - while (true) { + while (true) { // loop until we find usable entropy std::vector entropy(entropyLen, 0); GetRandBytes(entropy.data(), entropyLen); const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); @@ -33,9 +33,9 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz MnemonicSeed seed(language, mnemonic); // Verify that the seed data is valid entropy for unified spending keys at - // account 0 and account 0x7FFFFFFE - if (libzcash::UnifiedSpendingKey::Derive(seed, bip44CoinType, 0).has_value() && - libzcash::DeriveZip32TransparentSpendingKey(seed, bip44CoinType, ZCASH_LEGACY_TRANSPARENT_ACCOUNT).has_value()) { + // account 0 and at both the public & private chain levels for account 0x7FFFFFFE + if (libzcash::UnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && + libzcash::BIP32AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_TRANSPARENT_ACCOUNT).has_value()) { return seed; } } @@ -68,6 +68,87 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed) { namespace libzcash { +// +// Transparent +// + +std::optional> DeriveZip32TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { + auto rawSeed = seed.RawSeed(); + auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); + + // We use a fixed keypath scheme of m/32'/coin_type'/account' + // Derive m/32' + auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + if (!m_32h.has_value()) return std::nullopt; + + // Derive m/32'/coin_type' + auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + if (!m_32h_cth.has_value()) return std::nullopt; + + // Derive m/32'/coin_type'/account_id' + auto result = m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); + if (!result.has_value()) return std::nullopt; + + int64_t nCreationTime = GetTime(); + auto keyMeta = CKeyMetadata(nCreationTime); + keyMeta.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + keyMeta.seedFp = seed.Fingerprint(); + + return std::make_pair(result.value(), keyMeta); +} + +std::optional BIP32AccountChains::ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + uint32_t accountId) { + auto accountKeyOpt = DeriveZip32TransparentAccountKey(seed, bip44CoinType, accountId); + if (!accountKeyOpt.has_value()) return std::nullopt; + + auto accountKey = accountKeyOpt.value(); + auto external = accountKey.first.Derive(0); + auto internal = accountKey.first.Derive(1); + + if (!(external.has_value() && internal.has_value())) return std::nullopt; + + return BIP32AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); +} + +std::optional> BIP32AccountChains::DeriveExternal(uint32_t addrIndex) { + auto childKey = external.Derive(addrIndex); + if (!childKey.has_value()) return std::nullopt; + + int64_t nCreationTime = GetTime(); + auto keyMeta = CKeyMetadata(nCreationTime); + keyMeta.hdKeypath = "m/32'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(accountId) + "'/" + + "0/" + + std::to_string(addrIndex); + keyMeta.seedFp = seedFp; + + return std::make_pair(childKey.value(), keyMeta); +} + +std::optional> BIP32AccountChains::DeriveInternal(uint32_t addrIndex) { + auto childKey = internal.Derive(addrIndex); + if (!childKey.has_value()) return std::nullopt; + + int64_t nCreationTime = GetTime(); + auto keyMeta = CKeyMetadata(nCreationTime); + keyMeta.hdKeypath = "m/32'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(accountId) + "'/" + + "1/" + + std::to_string(addrIndex); + keyMeta.seedFp = seedFp; + + return std::make_pair(childKey.value(), keyMeta); +} + +// +// Sapling +// + std::optional SaplingExtendedFullViewingKey::Derive(uint32_t i) const { CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION); @@ -180,11 +261,11 @@ std::pair SaplingExtendedSpendingKey:: // Create new metadata int64_t nCreationTime = GetTime(); - CKeyMetadata metadata(nCreationTime); - metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - metadata.seedFp = seed.Fingerprint(); + CKeyMetadata keyMeta(nCreationTime); + keyMeta.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + keyMeta.seedFp = seed.Fingerprint(); - return std::make_pair(xsk, metadata); + return std::make_pair(xsk, keyMeta); } SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const @@ -199,30 +280,17 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const return ret; } -std::optional DeriveZip32TransparentSpendingKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { - auto rawSeed = seed.RawSeed(); - auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); - - // We use a fixed keypath scheme of m/32'/coin_type'/account' - // Derive m/32' - auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); - if (!m_32h.has_value()) return std::nullopt; - - // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); - if (!m_32h_cth.has_value()) return std::nullopt; - - // Derive m/32'/coin_type'/account_id' - return m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); -} +// +// Unified +// -std::optional> UnifiedSpendingKey::Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> UnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { UnifiedSpendingKey usk; usk.accountId = accountId; - auto transparentKey = DeriveZip32TransparentSpendingKey(seed, bip44CoinType, accountId); + auto transparentKey = DeriveZip32TransparentAccountKey(seed, bip44CoinType, accountId); if (!transparentKey.has_value()) return std::nullopt; - usk.transparentKey = transparentKey.value(); + usk.transparentKey = transparentKey.value().first; auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); usk.saplingKey = saplingKey.first; @@ -248,7 +316,9 @@ std::optional UnifiedFullViewingKey::Address(diversifier_i ZcashdUnifiedAddress ua; if (transparentKey.has_value()) { + // ensure that the diversifier index is small enough for a t-addr if (MAX_TRANSPARENT_CHILD_IDX.less_than_le(j)) return std::nullopt; + CExtPubKey changeKey; if (!transparentKey.value().Derive(changeKey, 0)) { return std::nullopt; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 3e019e49e50..cc7eb9f6e93 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -351,7 +351,10 @@ class UnifiedSpendingKey { UnifiedSpendingKey() {} public: - static std::optional> Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + static std::optional> ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + uint32_t accountId); const std::optional& GetTransparentKey() const { return transparentKey; @@ -366,7 +369,30 @@ class UnifiedSpendingKey { std::optional ParseZip32KeypathAccount(const std::string& keyPath); -std::optional DeriveZip32TransparentSpendingKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); +std::optional> DeriveZip32TransparentMasterKey( + const HDSeed& seed, + uint32_t bip44CoinType, + uint32_t accountId); + +class BIP32AccountChains { +private: + uint256 seedFp; + uint32_t accountId; + uint32_t bip44CoinType; + CExtKey external; + CExtKey internal; + + BIP32AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, uint32_t accountIdIn, CExtKey externalIn, CExtKey internalIn): + seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} +public: + static std::optional ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + uint32_t accountId); + + std::optional> DeriveExternal(uint32_t addrIndex); + std::optional> DeriveInternal(uint32_t addrIndex); +}; } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index b5299fc5e21..27ab37765ce 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -212,8 +212,7 @@ double benchmark_verify_equihash() double benchmark_large_tx(size_t nInputs) { // Create priv/pub key - CKey priv; - priv.MakeNewKey(false); + CKey priv = CKey::TestOnlyRandomKey(true); auto pub = priv.GetPubKey(); CBasicKeyStore tempKeystore; tempKeystore.AddKey(priv); From dc2c07bbdeead281a80ee19d1a2936c5fadcf10b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 14 Oct 2021 11:50:11 -0600 Subject: [PATCH 084/514] Generate legacy Sapling addresses from the mnemonic seed. This further deprecates the legacy seed, by no longer using it as a source of randomness. Instead of continuing to rely on successive account identifiers for the legacy seed, it now takes advantage of the `m/32'/coin_type'/account'/addressIndex'` keypath scheme defined by ZIP 32. For the purpose of the zcashd wallet, the account identifier used in generation of Sapling addresses associated with the legacy address generation code path is fixed to 0x7FFFFFFE, the same as the account used for all future transparent address generation. --- src/key.h | 7 ----- src/wallet/gtest/test_wallet_zkeys.cpp | 8 +++--- src/wallet/rpcdump.cpp | 2 +- src/wallet/rpcwallet.cpp | 11 +++++--- src/wallet/test/wallet_test_fixture.cpp | 4 ++- src/wallet/wallet.cpp | 33 +++++++++-------------- src/wallet/wallet.h | 4 --- src/wallet/walletdb.cpp | 13 --------- src/wallet/walletdb.h | 18 ++++++++----- src/zcash/address/zip32.cpp | 36 +++++++++++++++++++++++-- src/zcash/address/zip32.h | 10 +++++++ 11 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/key.h b/src/key.h index 4f48f3bbd5f..654e8bcd70d 100644 --- a/src/key.h +++ b/src/key.h @@ -15,13 +15,6 @@ #include #include -/** - * The account identifier used for HD derivation of the transparent - * p2pkh public key from which all child transparent addresses are - * derived in accordance with ZIP-316. - */ -const uint32_t ZCASH_LEGACY_TRANSPARENT_ACCOUNT = 0x7FFFFFFE; - /** * secure_allocator is defined in allocators.h * CPrivKey is a serialized private key, with all parameters included diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 49540be6cf2..b0c0ddf01b0 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -35,10 +35,10 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // Load the all-zeroes seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); - // The legacy seed used to be automatically derived from randomness; since - // non-mnemonic random generation has been removed we just use the + // The legacy seed used to be automatically derived from randomness; since + // non-mnemonic random generation has been removed we just use the // all-zeros mnemonic for these tests. - wallet.LoadLegacyHDSeed(seed); + wallet.LoadMnemonicSeed(seed); // Now this call succeeds legacyKey = wallet.GenerateNewLegacySaplingZKey(); @@ -438,7 +438,7 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { // Load the all-zeroes seed as the legacy seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); - wallet.LoadLegacyHDSeed(seed); + wallet.LoadMnemonicSeed(seed); // wallet should be empty std::set addrs; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index b54b33fa597..177ae373f4f 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -614,7 +614,7 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys) if (hdSeed.has_value()) { auto mSeed = hdSeed.value(); file << strprintf( - "# Recovery Phrase=\"%s\" \n# language=%s \n# fingerprint=%s\n", + "# Emergency Recovery Phrase=\"%s\" \n# language=%s \n# fingerprint=%s\n", mSeed.GetMnemonic(), MnemonicSeed::LanguageName(mSeed.GetLanguage()), mSeed.Fingerprint().GetHex() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c9a7a21692f..93600bf24b0 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2034,10 +2034,13 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); auto mnemonicChain = pwalletMain->GetMnemonicHDChain(); if (mnemonicChain.has_value()) - obj.pushKV("seedfp", mnemonicChain.value().GetSeedFingerprint().GetHex()); - auto legacyChain = pwalletMain->GetLegacyHDChain(); - if (legacyChain.has_value()) - obj.pushKV("legacy_seedfp", legacyChain.value().GetSeedFingerprint().GetHex()); + obj.pushKV("mnemonic_seedfp", mnemonicChain.value().GetSeedFingerprint().GetHex()); + // TODO: do we really need to return the legacy seed fingerprint if we're + // no longer using it to generate any new keys? What do people actually use + // the fingerprint for? + auto legacySeed = pwalletMain->GetLegacyHDSeed(); + if (legacySeed.has_value()) + obj.pushKV("legacy_seedfp", legacySeed.value().Fingerprint().GetHex()); return obj; } diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 806bcec9354..dd591af56a3 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -11,8 +11,10 @@ WalletTestingSetup::WalletTestingSetup(): TestingSetup() bool fFirstRun; pwalletMain = new CWallet(Params(), "wallet_test.dat"); pwalletMain->LoadWallet(fFirstRun); - RegisterValidationInterface(pwalletMain); + if (!pwalletMain->HaveMnemonicSeed()) + pwalletMain->GenerateNewSeed(); + RegisterValidationInterface(pwalletMain); RegisterWalletRPCCommands(tableRPC); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a02a6aa253a..6635f81e40b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -121,7 +121,7 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() std::optional CWallet::GenerateNewLegacySaplingZKey() { AssertLockHeld(cs_wallet); - auto seedOpt = GetLegacyHDSeed(); + auto seedOpt = GetMnemonicSeed(); if (seedOpt.has_value()) { auto seed = seedOpt.value(); if (!legacyHDChain.has_value()) { @@ -129,21 +129,21 @@ std::optional CWallet::GenerateNewLegacySaplingZKey() { } CHDChain& hdChain = legacyHDChain.value(); - // loop until we find an unused account id + // loop until we find an unused address index while (true) { - auto xsk = libzcash::SaplingExtendedSpendingKey::ForAccount( + auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy( seed, BIP44CoinType(), - hdChain.GetAccountCounter()); - // advance the account counter so that the next time we need to generate + hdChain.GetLegacySaplingKeyCounter()); + // advance the address index counter so that the next time we need to generate // a key we're pointing at a free index. - hdChain.IncrementAccountCounter(); + hdChain.IncrementLegacySaplingKeyCounter(); if (HaveSaplingSpendingKey(xsk.first.ToXFVK())) { - // try the next account ID + // try the next index continue; } else { // Update the persisted chain information - if (fFileBacked && !CWalletDB(strWalletFile).WriteLegacyHDChain(hdChain)) { + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): Writing HD chain model failed"); } @@ -252,12 +252,12 @@ std::optional CWallet::GenerateNewKey() CHDChain& hdChain = mnemonicHDChain.value(); if (seedOpt.has_value()) { // All mnemonic seeds are checked at construction to ensure that we can obtain - // a valid spending key for the account ZCASH_LEGACY_TRANSPARENT_ACCOUNT; + // a valid spending key for the account ZCASH_LEGACY_ACCOUNT; // therefore, the `value()` call here is safe. BIP32AccountChains accountChains = BIP32AccountChains::ForAccount( seedOpt.value(), BIP44CoinType(), - ZCASH_LEGACY_TRANSPARENT_ACCOUNT).value(); + ZCASH_LEGACY_ACCOUNT).value(); while (true) { auto extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); @@ -272,7 +272,7 @@ std::optional CWallet::GenerateNewKey() // Create new metadata const CKeyMetadata& keyMeta = extKey.value().second; mapKeyMetadata[pubkey.GetID()] = keyMeta; - if (!nTimeFirstKey || keyMeta.nCreateTime < nTimeFirstKey) + if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) nTimeFirstKey = keyMeta.nCreateTime; if (!AddKeyPubKey(secret, pubkey)) @@ -2356,16 +2356,6 @@ void CWallet::SetMnemonicHDChain(const CHDChain& chain, bool memonly) mnemonicHDChain = chain; } -// TODO: make private -void CWallet::SetLegacyHDChain(const CHDChain& chain, bool memonly) -{ - LOCK(cs_wallet); - if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteLegacyHDChain(chain)) - throw std::runtime_error(std::string(__func__) + ": writing chain failed"); - - legacyHDChain = chain; -} - bool CWallet::CheckNetworkInfo(std::pair readNetworkInfo) { LOCK(cs_wallet); @@ -4864,6 +4854,7 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches walletInstance->SetMaxVersion(nMaxVersion); } + // TODO: should this go in `LoadWallet` instead? if (!walletInstance->HaveMnemonicSeed()) { // We can't set the new HD seed until the wallet is decrypted. diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 35e13bcaf6b..6a7da396524 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1320,10 +1320,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SetMnemonicHDChain(const CHDChain& chain, bool memonly); const std::optional& GetMnemonicHDChain() const { return mnemonicHDChain; } - /* Set the metadata for the legacy HD seed (chain child index counters) */ - void SetLegacyHDChain(const CHDChain& chain, bool memonly); - const std::optional GetLegacyHDChain() const { return legacyHDChain; } - bool CheckNetworkInfo(std::pair networkInfo); uint32_t BIP44CoinType(); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 0dad1fdda69..417a5f8d8ca 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -749,11 +749,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } wss.fIsEncrypted = true; } - else if (strType == "hdchain") - { - auto chain = CHDChain::Read(ssValue); - pwallet->SetLegacyHDChain(chain, true); - } else if (strType == "mnemonichdchain") { auto chain = CHDChain::Read(ssValue); @@ -1208,14 +1203,6 @@ bool CWalletDB::WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vecto return Write(std::make_pair(std::string("chdmnemonicseed"), seedFp), vchCryptedSecret); } -// This can be removed once generation of Sapling addresses using the legacy -// seed has been disabled. -bool CWalletDB::WriteLegacyHDChain(const CHDChain& chain) -{ - nWalletDBUpdateCounter++; - return Write(std::string("hdchain"), chain); -} - bool CWalletDB::WriteMnemonicHDChain(const CHDChain& chain) { nWalletDBUpdateCounter++; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index aef6789cd43..2f808822d14 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -50,6 +50,7 @@ class CHDChain int64_t nCreateTime; // 0 means unknown uint32_t accountCounter; uint32_t legacyTKeyCounter; + uint32_t legacySaplingKeyCounter; CHDChain() { SetNull(); } @@ -60,12 +61,13 @@ class CHDChain nCreateTime = 0; accountCounter = 0; legacyTKeyCounter = 0; + legacySaplingKeyCounter = 0; } public: static const int VERSION_HD_BASE = 1; static const int CURRENT_VERSION = VERSION_HD_BASE; - CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0) {} + CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0), legacySaplingKeyCounter(0) {} ADD_SERIALIZE_METHODS; @@ -77,6 +79,7 @@ class CHDChain READWRITE(nCreateTime); READWRITE(accountCounter); READWRITE(legacyTKeyCounter); + READWRITE(legacySaplingKeyCounter); } template @@ -105,6 +108,14 @@ class CHDChain void IncrementLegacyTKeyCounter() { legacyTKeyCounter += 1; } + + uint32_t GetLegacySaplingKeyCounter() const { + return legacySaplingKeyCounter; + } + + void IncrementLegacySaplingKeyCounter() { + legacySaplingKeyCounter += 1; + } }; /** Access to the wallet database */ @@ -164,11 +175,6 @@ class CWalletDB : public CDB bool WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); bool WriteMnemonicHDChain(const CHDChain& chain); - //! Write the legacy hdchain metadata to the database - //! - //! TODO: remove when generation of new legacy-seed-based keys has been disabled. - bool WriteLegacyHDChain(const CHDChain& chain); - /// Write spending key to wallet database, where key is payment address and value is spending key. bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta); bool WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index fe09bc18358..19e28db4bda 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -33,9 +33,13 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz MnemonicSeed seed(language, mnemonic); // Verify that the seed data is valid entropy for unified spending keys at - // account 0 and at both the public & private chain levels for account 0x7FFFFFFE + // account 0 and at both the public & private chain levels for account 0x7FFFFFFE. + // It is not necessary to check for a valid diversified Sapling address at + // account 0x7FFFFFFE because derivation via the legacy path can simply search + // for a valid diversifier; unlike in the unified spending key case, diversifier + // indices don't need to line up with anything. if (libzcash::UnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && - libzcash::BIP32AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_TRANSPARENT_ACCOUNT).has_value()) { + libzcash::BIP32AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { return seed; } } @@ -268,6 +272,34 @@ std::pair SaplingExtendedSpendingKey:: return std::make_pair(xsk, keyMeta); } +std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { + auto m = Master(seed); + + // We use a fixed keypath scheme of m/32'/coin_type'/account'/addressIndex' + + // Derive m/32' + auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + // Derive m/32'/coin_type' + auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + + // Derive account key at the legacy account index + auto m_32h_cth_l = m_32h_cth.Derive(ZCASH_LEGACY_ACCOUNT | ZIP32_HARDENED_KEY_LIMIT); + + // Derive key at the specified address index (non-hardened) + auto xsk = m_32h_cth_l.Derive(addressIndex); + + // Create new metadata + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + metadata.hdKeypath = "m/32'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(ZCASH_LEGACY_ACCOUNT) + "'/" + + std::to_string(addressIndex); + metadata.seedFp = seed.Fingerprint(); + + return std::make_pair(xsk, metadata); +} + SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const { SaplingExtendedFullViewingKey ret; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index cc7eb9f6e93..1b4da4777c7 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -21,6 +21,14 @@ const uint32_t ZIP32_HARDENED_KEY_LIMIT = 0x80000000; const size_t ZIP32_XFVK_SIZE = 169; const size_t ZIP32_XSK_SIZE = 169; +/** + * The account identifier used for HD derivation of + * transparent and Sapling addresses via the legacy + * `getnewaddress` and `z_getnewaddress` code paths, + */ +const uint32_t ZCASH_LEGACY_ACCOUNT = 0x7FFFFFFE; + + class CWalletDB; typedef std::vector> RawHDSeed; @@ -275,6 +283,8 @@ struct SaplingExtendedSpendingKey { static SaplingExtendedSpendingKey Master(const HDSeed& seed); static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + static std::pair Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); + SaplingExtendedSpendingKey Derive(uint32_t i) const; From ebab190fdf379ac1a471d41d7a5359bb266c627f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 19 Oct 2021 20:04:21 -0600 Subject: [PATCH 085/514] Replace account ID with ZIP-0032 key path in listaddresses output. Account ID information is insufficiently granular to identify separate pools of spend authority. --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 10 +++++----- src/gtest/test_zip32.cpp | 18 ++++++++++++++++++ src/wallet/rpcwallet.cpp | 12 +++++------- src/wallet/test/rpc_wallet_tests.cpp | 26 +++++++++++++------------- src/wallet/wallet.cpp | 12 ++++++++---- src/zcash/address/zip32.cpp | 2 +- 7 files changed, 58 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c1c1ea3bcb..cfcf268f5b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,7 +534,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "blake2b_simd", "byteorder", @@ -543,7 +543,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "blake2b_simd", ] @@ -1896,7 +1896,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "bech32", "blake2b_simd", @@ -1908,7 +1908,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "byteorder", "nonempty", @@ -1917,7 +1917,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "bigint", "blake2b_simd", @@ -1927,7 +1927,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "blake2b_simd", "byteorder", @@ -1942,7 +1942,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "aes", "bip0039", @@ -1976,7 +1976,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/nuttycom/librustzcash.git?rev=86d4affe739170ab5ed9e69f5123938d12973fde#86d4affe739170ab5ed9e69f5123938d12973fde" +source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index efcd9529fe9..c21d2701fed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,8 +71,8 @@ codegen-units = 1 ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } -zcash_address = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } -zcash_history = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } -zcash_note_encryption = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } -zcash_primitives = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } -zcash_proofs = { git = "https://github.com/nuttycom/librustzcash.git", rev = "86d4affe739170ab5ed9e69f5123938d12973fde" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index 17c80b6fff4..fc7a7275869 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -135,3 +135,21 @@ TEST(ZIP32, TestVectors) { m_1_2hv_3.DefaultAddress().d, testing::ElementsAreArray({ 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd })); } + +TEST(ZIP32, ParseZip32KeypathAccount) { + std::string sAccount = "m/32'/1234'/5'"; + EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); + EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 5); + + sAccount = "m/32'/1234'/50'"; + EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); + EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 50); + + sAccount = "m/32'/1234'/5'/0"; + EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); + EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 5); + + sAccount = "m/32'/133'/2147483646'/1"; + EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); + EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 2147483646); +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 93600bf24b0..b0c7f8ef6af 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -334,7 +334,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) " },\n" " \"sapling\": [ -- each element in this list represents a set of diversified addresses derived from a single IVK. \n" " {\n" - " \"zip32AccountId\": 0, -- optional field, not present for imported/watchonly sources,\n" + " \"zip32KeyPath\": \"m/32'/133'/0'\", -- optional field, not present for imported/watchonly sources,\n" " \"addresses\": [\n" " \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\",\n" " ...\n" @@ -477,12 +477,10 @@ UniValue listaddresses(const UniValue& params, bool fHelp) UniValue sapling_obj(UniValue::VOBJ); - if (source == LegacyHDSeed) { - std::string hdKeypath = pwalletMain->mapSaplingZKeyMetadata[ivk].hdKeypath; - std::optional accountId = libzcash::ParseZip32KeypathAccount(hdKeypath); - - if (accountId.has_value()) { - sapling_obj.pushKV("zip32AccountId", (uint64_t) accountId.value()); + if (source == LegacyHDSeed || source == MnemonicHDSeed) { + std::string hdKeyPath = pwalletMain->mapSaplingZKeyMetadata[ivk].hdKeypath; + if (hdKeyPath != "") { + sapling_obj.pushKV("zip32KeyPath", hdKeyPath); } } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 7d7a1714437..f6d1823cb90 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -831,34 +831,34 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { auto listarr = list.get_array(); bool sproutCountMatch = false; bool saplingExtfvksMatch = false; - bool saplingAccount0 = false; - bool saplingAccount1 = false; + bool saplingSpendAuth0 = false; + bool saplingSpendAuth1 = false; bool saplingCountMismatch = true; for (auto a : listarr.getValues()) { auto source = find_value(a.get_obj(), "source"); if (source.get_str() == "legacy_random") { - auto sprout_obj = find_value(a.get_obj(), "sprout").get_obj(); - auto sprout_addrs = find_value(sprout_obj, "addresses").get_array(); - sproutCountMatch = (sprout_addrs.size() == 1); + auto sproutObj = find_value(a.get_obj(), "sprout").get_obj(); + auto sproutAddrs = find_value(sproutObj, "addresses").get_array(); + sproutCountMatch = (sproutAddrs.size() == 1); } if (source.get_str() == "legacy_hdseed") { auto sapling_addr_sets = find_value(a.get_obj(), "sapling").get_array(); saplingExtfvksMatch = (sapling_addr_sets.size() == 2); - for (auto sapling_obj : sapling_addr_sets.getValues()) { - auto sapling_account = find_value(sapling_obj, "zip32AccountId").get_int(); - saplingAccount0 |= (sapling_account == 0); - saplingAccount1 |= (sapling_account == 1); - auto sapling_addrs = find_value(sapling_obj, "addresses").get_array(); - saplingCountMismatch &= (sapling_addrs.size() != 1); + for (auto saplingObj : sapling_addr_sets.getValues()) { + auto keypath = find_value(saplingObj, "zip32KeyPath").get_str(); + saplingSpendAuth0 |= (keypath == "m/32'/133'/2147483646'/0"); + saplingSpendAuth1 |= (keypath == "m/32'/133'/2147483646'/1"); + auto saplingAddrs = find_value(saplingObj, "addresses").get_array(); + saplingCountMismatch &= (saplingAddrs.size() != 1); } } } BOOST_CHECK(sproutCountMatch); BOOST_CHECK(saplingExtfvksMatch); BOOST_CHECK(!saplingCountMismatch); - BOOST_CHECK(saplingAccount0); - BOOST_CHECK(saplingAccount1); + BOOST_CHECK(saplingSpendAuth0); + BOOST_CHECK(saplingSpendAuth1); } } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6635f81e40b..4ec8961e44f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -124,10 +124,10 @@ std::optional CWallet::GenerateNewLegacySaplingZKey() { auto seedOpt = GetMnemonicSeed(); if (seedOpt.has_value()) { auto seed = seedOpt.value(); - if (!legacyHDChain.has_value()) { - legacyHDChain = CHDChain(seed.Fingerprint(), GetTime()); + if (!mnemonicHDChain.has_value()) { + mnemonicHDChain = CHDChain(seed.Fingerprint(), GetTime()); } - CHDChain& hdChain = legacyHDChain.value(); + CHDChain& hdChain = mnemonicHDChain.value(); // loop until we find an unused address index while (true) { @@ -249,8 +249,12 @@ std::optional CWallet::GenerateNewKey() { AssertLockHeld(cs_wallet); // mapKeyMetadata auto seedOpt = GetMnemonicSeed(); - CHDChain& hdChain = mnemonicHDChain.value(); if (seedOpt.has_value()) { + if (!mnemonicHDChain.has_value()) { + mnemonicHDChain = CHDChain(seedOpt.value().Fingerprint(), GetTime()); + } + CHDChain& hdChain = mnemonicHDChain.value(); + // All mnemonic seeds are checked at construction to ensure that we can obtain // a valid spending key for the account ZCASH_LEGACY_ACCOUNT; // therefore, the `value()` call here is safe. diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 19e28db4bda..38260631de6 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -378,7 +378,7 @@ std::optional UnifiedFullViewingKey::Address(diversifier_i } std::optional ParseZip32KeypathAccount(const std::string& keyPath) { - std::regex pattern("m/32'/[0-9]+'/([0-9]+)'"); + std::regex pattern("m/32'/[0-9]+'/([0-9]+)'.*"); std::smatch matches; if (std::regex_match(keyPath, matches, pattern)) { return stoul(matches[1]); From d8d9cd129e53362f6d432aef7f883979662d37ce Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 20 Oct 2021 17:13:52 -0600 Subject: [PATCH 086/514] Use legacy address for Sapling migration until UA functionality is available to the RPC tests. --- .../asyncrpcoperation_saplingmigration.cpp | 19 +--- src/wallet/gtest/test_wallet_zkeys.cpp | 18 ++- src/wallet/rpcwallet.cpp | 6 +- src/wallet/test/rpc_wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 103 ++++++++++-------- src/wallet/wallet.h | 14 ++- 6 files changed, 79 insertions(+), 83 deletions(-) diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 8da4c281452..43f0f60b652 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -191,8 +191,9 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl return amount; } -// Unless otherwise specified, the migration destination address is the address for Sapling account 0 -// at the smallest diversifier index that produces a valid diversified address. +// Unless otherwise specified, the migration destination address is the address +// the legacy Sapling account (0x7FFFFFFE) at the smallest diversifier index +// that produces a valid diversified address. libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { KeyIO keyIO(Params()); if (mapArgs.count("-migrationdestaddress")) { @@ -203,17 +204,9 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration return *saplingAddress; } - auto usk = pwalletMain->GetUnifiedSpendingKeyForAccount(0); - assert(usk.has_value()); // mnemonic seeds are currently always generated to have valid USKs at account 0 - auto ua = usk.value().ToFullViewingKey().FindAddress(libzcash::diversifier_index_t(0)); - auto addr = ua.first.GetSaplingPaymentAddress(); - if (addr.has_value()) { - return addr.value(); - } else { - // This error will only occur if Sapling address generation has been disbled for USKs from this - // wallet. - throw std::runtime_error(std::string(__func__) + ": No Sapling address generated for account 0."); - } + // TODO: move off of legacy addresses. + auto generatedKey = pwalletMain->GenerateLegacySaplingZKey(0); + return generatedKey.first.ToXFVK().DefaultAddress(); } void AsyncRPCOperation_saplingmigration::cancel() { diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index b0c0ddf01b0..26fa8b45c1b 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -29,8 +29,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { ASSERT_EQ(0, addrs.size()); // No HD seed in the wallet - auto legacyKey = wallet.GenerateNewLegacySaplingZKey(); - ASSERT_FALSE(legacyKey.has_value()); + EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); // Load the all-zeroes seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); @@ -41,9 +40,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { wallet.LoadMnemonicSeed(seed); // Now this call succeeds - legacyKey = wallet.GenerateNewLegacySaplingZKey(); - ASSERT_TRUE(legacyKey.has_value()); - auto address = legacyKey.value(); + auto address = wallet.GenerateNewLegacySaplingZKey(); // wallet should have one key wallet.GetSaplingPaymentAddresses(addrs); @@ -433,12 +430,13 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { // No default CPubKey set ASSERT_TRUE(fFirstRun); - ASSERT_FALSE(wallet.HaveLegacyHDSeed()); + ASSERT_FALSE(wallet.HaveMnemonicSeed()); // Load the all-zeroes seed as the legacy seed std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); wallet.LoadMnemonicSeed(seed); + ASSERT_TRUE(wallet.HaveMnemonicSeed()); // wallet should be empty std::set addrs; @@ -446,7 +444,7 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_EQ(0, addrs.size()); // Add random key to the wallet - auto address = wallet.GenerateNewLegacySaplingZKey().value(); + auto address = wallet.GenerateNewLegacySaplingZKey(); // wallet should have one key wallet.GetSaplingPaymentAddresses(addrs); @@ -470,13 +468,11 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_TRUE(wallet.EncryptWallet(strWalletPass)); // adding a new key will fail as the wallet is locked - EXPECT_FALSE(wallet.GenerateNewLegacySaplingZKey().has_value()); + EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); // unlock wallet and then add wallet.Unlock(strWalletPass); - auto address2Opt = wallet.GenerateNewLegacySaplingZKey(); - EXPECT_TRUE(address2Opt.has_value()); - auto address2 = address2Opt.value(); + auto address2 = wallet.GenerateNewLegacySaplingZKey(); // flush the wallet to prevent race conditions wallet.Flush(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b0c7f8ef6af..82477411e1b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2941,11 +2941,7 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()); } else if (addrType == ADDR_TYPE_SAPLING) { auto saplingAddress = pwalletMain->GenerateNewLegacySaplingZKey(); - if (saplingAddress.has_value()) { - return keyIO.EncodePaymentAddress(saplingAddress.value()); - } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, "No legacy HD seed available; please use z_getunifiedaddress instead."); - } + return keyIO.EncodePaymentAddress(saplingAddress); } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type"); } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index f6d1823cb90..d3ccf872b26 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -76,7 +76,7 @@ static UniValue ValueFromString(const std::string &str) } static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) { - auto usk = pwallet->GetUnifiedSpendingKeyForAccount(0); + auto usk = pwallet->GenerateUnifiedSpendingKeyForAccount(0); return usk.value() .ToFullViewingKey() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4ec8961e44f..335c6bd1c56 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -118,48 +118,59 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() // is not present. When using legacy HD seeds, the account index is determined // by trial of legacyHDChain.GetAccountCounter(); for unified addresses this must use // valued derived from legacyHDChain.unifiedAccountCounter -std::optional CWallet::GenerateNewLegacySaplingZKey() { +SaplingPaymentAddress CWallet::GenerateNewLegacySaplingZKey() { AssertLockHeld(cs_wallet); - auto seedOpt = GetMnemonicSeed(); - if (seedOpt.has_value()) { - auto seed = seedOpt.value(); - if (!mnemonicHDChain.has_value()) { - mnemonicHDChain = CHDChain(seed.Fingerprint(), GetTime()); + if (!mnemonicHDChain.has_value()) { + mnemonicHDChain = CHDChain(GetMnemonicSeed().value().Fingerprint(), GetTime()); + } + CHDChain& hdChain = mnemonicHDChain.value(); + + // loop until we find an unused address index + while (true) { + auto generatedKey = GenerateLegacySaplingZKey(hdChain.GetLegacySaplingKeyCounter()); + auto xfvk = generatedKey.first.ToXFVK(); + + // advance the address index counter so that the next time we need to generate + // a key we're pointing at a free index. + hdChain.IncrementLegacySaplingKeyCounter(); + if (!generatedKey.second) { + // the key already existed, so try the next one + continue; + } else { + // Update the persisted chain information + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { + throw std::runtime_error( + "CWallet::GenerateLegacySaplingZKey(): Writing HD chain model failed"); + } + + return xfvk.DefaultAddress(); } - CHDChain& hdChain = mnemonicHDChain.value(); + } +} - // loop until we find an unused address index - while (true) { - auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy( - seed, - BIP44CoinType(), - hdChain.GetLegacySaplingKeyCounter()); - // advance the address index counter so that the next time we need to generate - // a key we're pointing at a free index. - hdChain.IncrementLegacySaplingKeyCounter(); - if (HaveSaplingSpendingKey(xsk.first.ToXFVK())) { - // try the next index - continue; - } else { - // Update the persisted chain information - if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { - throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): Writing HD chain model failed"); - } +std::pair CWallet::GenerateLegacySaplingZKey(uint32_t addrIndex) { + auto seedOpt = GetMnemonicSeed(); + if (!seedOpt.has_value()) { + throw std::runtime_error( + "CWallet::GenerateLegacySaplingZKey(): Wallet does not have a mnemonic seed."); + } - auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key(); - mapSaplingZKeyMetadata[ivk] = xsk.second; + auto seed = seedOpt.value(); - if (!AddSaplingZKey(xsk.first)) { - throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): AddSaplingZKey failed"); - } + auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy(seed, BIP44CoinType(), addrIndex); + if (!HaveSaplingSpendingKey(xsk.first.ToXFVK())) { + auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key(); + mapSaplingZKeyMetadata[ivk] = xsk.second; - return xsk.first.ToXFVK().DefaultAddress(); - } + if (!AddSaplingZKey(xsk.first)) { + throw std::runtime_error("CWallet::GenerateLegacySaplingZKey(): AddSaplingZKey failed."); } + return std::make_pair(xsk.first, true) ; } else { - return std::nullopt; + return std::make_pair(xsk.first, false); } + } // Add spending key to keystore @@ -238,9 +249,7 @@ bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key) return true; if (!IsCrypted()) { - return CWalletDB(strWalletFile).WriteZKey(addr, - key, - mapSproutZKeyMetadata[addr]); + return CWalletDB(strWalletFile).WriteZKey(addr, key, mapSproutZKeyMetadata[addr]); } return true; } @@ -394,35 +403,33 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); - auto seed = GetMnemonicSeed(); - if (!seed.has_value()) { - throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet."); - } - CHDChain& hdChain = mnemonicHDChain.value(); while (true) { - auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter()); + auto usk = GenerateUnifiedSpendingKeyForAccount(hdChain.GetAccountCounter()); hdChain.IncrementAccountCounter(); if (usk.has_value()) { // Update the persisted chain information if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { - throw std::runtime_error("CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed"); + throw std::runtime_error( + "CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed"); } - // TODO: Save the unified full viewing key to the wallet metadata - - return usk.value().first; + return usk.value(); } } } -std::optional CWallet::GetUnifiedSpendingKeyForAccount(uint32_t accountId) { +std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) { auto seed = GetMnemonicSeed(); - assert(seed.has_value()); - // TODO: is there any reason to cache and not re-derive this every time? + if (!seed.has_value()) { + throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet."); + } + auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); if (usk.has_value()) { + // TODO: Save the unified full viewing key & metadata to the wallet + return usk.value().first; } else { return std::nullopt; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6a7da396524..b98801685de 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1058,10 +1058,14 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * Sapling ZKeys */ - //! Generates new Sapling key using the legacy HD seed (if one is available) - //! and legacy account counter, stores the newly generated spending key to - //! the wallet, and returns the default address for the newly generated key. - std::optional GenerateNewLegacySaplingZKey(); + //! Generates new Sapling key, stores the newly generated spending + //! key to the wallet, and returns the default address for the newly generated key. + libzcash::SaplingPaymentAddress GenerateNewLegacySaplingZKey(); + //! Generates Sapling key at the specified address index, and stores the newly generated + //! spending key to the wallet if it has not alreay been persisted. + //! Returns the newly created key, its metadata, and a flag distinguishing + //! whether or not the key was already known by the wallet. + std::pair GenerateLegacySaplingZKey(uint32_t addrIndex); //! Adds Sapling spending key to the store, and saves it to disk bool AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key); //! Add Sapling full viewing key to the wallet. @@ -1096,7 +1100,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * Unified keys & addresses */ libzcash::UnifiedSpendingKey GenerateNewUnifiedSpendingKey(); - std::optional GetUnifiedSpendingKeyForAccount(uint32_t accountId); + std::optional GenerateUnifiedSpendingKeyForAccount(uint32_t accountId); /** * Increment the next transaction order id From e0ae5362c9b9b9a17b9ae43b9a4e74251cbd2a1e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 21 Oct 2021 08:21:13 -0600 Subject: [PATCH 087/514] Revert change to the type of GenerateNewKey --- src/wallet/test/rpc_wallet_tests.cpp | 8 +-- src/wallet/wallet.cpp | 92 ++++++++++++++-------------- src/wallet/wallet.h | 2 +- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index d3ccf872b26..f415ec1c813 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) LOCK2(cs_main, pwalletMain->cs_wallet); - CPubKey demoPubkey = pwalletMain->GenerateNewKey().value(); + CPubKey demoPubkey = pwalletMain->GenerateNewKey(); CTxDestination demoAddress(CTxDestination(demoPubkey.GetID())); UniValue retValue; string strPurpose = "receive"; @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) pwalletMain->SetAddressBook(demoPubkey.GetID(), "", strPurpose); }); - CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey().value(); + CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey(); CTxDestination setaccountDemoAddress(CTxDestination(setaccountDemoPubkey.GetID())); /********************************* @@ -1469,7 +1469,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) KeyIO keyIO(Params()); // add keys manually - auto taddr = pwalletMain->GenerateNewKey().value().GetID(); + auto taddr = pwalletMain->GenerateNewKey().GetID(); std::string taddr1 = keyIO.EncodeDestination(taddr); auto pa = DefaultSaplingAddress(pwalletMain); std::string zaddr1 = keyIO.EncodePaymentAddress(pa); @@ -2173,7 +2173,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals) void TestWTxStatus(const Consensus::Params consensusParams, const int delta) { auto AddTrx = [&consensusParams]() { - auto taddr = pwalletMain->GenerateNewKey().value().GetID(); + auto taddr = pwalletMain->GenerateNewKey().GetID(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, 1); CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(taddr) << OP_EQUALVERIFY << OP_CHECKSIG; mtx.vout.push_back(CTxOut(5 * COIN, scriptPubKey)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 335c6bd1c56..67ef4f91117 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -155,7 +155,6 @@ std::pair CWallet::GenerateLegacySaplingZKey(u throw std::runtime_error( "CWallet::GenerateLegacySaplingZKey(): Wallet does not have a mnemonic seed."); } - auto seed = seedOpt.value(); auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy(seed, BIP44CoinType(), addrIndex); @@ -254,53 +253,56 @@ bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key) return true; } -std::optional CWallet::GenerateNewKey() +CPubKey CWallet::GenerateNewKey() { AssertLockHeld(cs_wallet); // mapKeyMetadata + auto seedOpt = GetMnemonicSeed(); - if (seedOpt.has_value()) { - if (!mnemonicHDChain.has_value()) { - mnemonicHDChain = CHDChain(seedOpt.value().Fingerprint(), GetTime()); - } - CHDChain& hdChain = mnemonicHDChain.value(); - - // All mnemonic seeds are checked at construction to ensure that we can obtain - // a valid spending key for the account ZCASH_LEGACY_ACCOUNT; - // therefore, the `value()` call here is safe. - BIP32AccountChains accountChains = BIP32AccountChains::ForAccount( - seedOpt.value(), - BIP44CoinType(), - ZCASH_LEGACY_ACCOUNT).value(); - - while (true) { - auto extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); - hdChain.IncrementLegacyTKeyCounter(); - - // if we did not successfully generate a key, try again. - if (extKey.has_value()) { - CKey secret = extKey.value().first.key; - CPubKey pubkey = secret.GetPubKey(); - assert(secret.VerifyPubKey(pubkey)); - - // Create new metadata - const CKeyMetadata& keyMeta = extKey.value().second; - mapKeyMetadata[pubkey.GetID()] = keyMeta; - if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) - nTimeFirstKey = keyMeta.nCreateTime; - - if (!AddKeyPubKey(secret, pubkey)) - throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); - - // Update the persisted chain information - if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { - throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); - } + if (!seedOpt.has_value()) { + throw std::runtime_error( + "CWallet::GenerateNewKey(): Wallet does not have a mnemonic seed."); + } + auto seed = seedOpt.value(); + + if (!mnemonicHDChain.has_value()) { + mnemonicHDChain = CHDChain(seedOpt.value().Fingerprint(), GetTime()); + } + CHDChain& hdChain = mnemonicHDChain.value(); - return pubkey; + // All mnemonic seeds are checked at construction to ensure that we can obtain + // a valid spending key for the account ZCASH_LEGACY_ACCOUNT; + // therefore, the `value()` call here is safe. + BIP32AccountChains accountChains = BIP32AccountChains::ForAccount( + seedOpt.value(), + BIP44CoinType(), + ZCASH_LEGACY_ACCOUNT).value(); + + while (true) { + auto extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); + hdChain.IncrementLegacyTKeyCounter(); + + // if we did not successfully generate a key, try again. + if (extKey.has_value()) { + CKey secret = extKey.value().first.key; + CPubKey pubkey = secret.GetPubKey(); + assert(secret.VerifyPubKey(pubkey)); + + // Create new metadata + const CKeyMetadata& keyMeta = extKey.value().second; + mapKeyMetadata[pubkey.GetID()] = keyMeta; + if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) + nTimeFirstKey = keyMeta.nCreateTime; + + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); + + // Update the persisted chain information + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { + throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); } + + return pubkey; } - } else { - return std::nullopt; } } @@ -4207,9 +4209,7 @@ bool CWallet::NewKeyPool() { int64_t nIndex = i+1; auto key = GenerateNewKey(); - if (!key.has_value()) - return false; // should have been caught by the `IsLocked` call. - walletdb.WritePool(nIndex, CKeyPool(key.value())); + walletdb.WritePool(nIndex, CKeyPool(key)); setKeyPool.insert(nIndex); } LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); @@ -4240,7 +4240,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; auto newKey = GenerateNewKey(); - if (!newKey.has_value() || !walletdb.WritePool(nEnd, CKeyPool(newKey.value()))) + if (!walletdb.WritePool(nEnd, CKeyPool(newKey))) throw runtime_error("TopUpKeyPool(): writing generated key failed"); setKeyPool.insert(nEnd); LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b98801685de..cbfe6fce1f5 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -993,7 +993,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * keystore implementation * Generate a new key */ - std::optional GenerateNewKey(); + CPubKey GenerateNewKey(); //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); //! Adds a key to the store, without saving it to disk (used by LoadWallet) From d6984dbf78c32232218a45ae3cc4ff7c85c5b5ce Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 21 Oct 2021 11:44:08 -0600 Subject: [PATCH 088/514] Fix wallet import/export test --- qa/rpc-tests/wallet_import_export.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qa/rpc-tests/wallet_import_export.py b/qa/rpc-tests/wallet_import_export.py index 67e43842c6d..ddda99ee737 100755 --- a/qa/rpc-tests/wallet_import_export.py +++ b/qa/rpc-tests/wallet_import_export.py @@ -59,10 +59,9 @@ def run_test(self): def parse_wallet_file(dump_path): file_lines = open(dump_path, "r", encoding="utf8").readlines() # We expect information about the HDSeed and fingerpring in the header - assert_true("HDSeed" in file_lines[4], "Expected HDSeed") - assert_true("fingerprint" in file_lines[4], "Expected fingerprint") - seed_comment_line = file_lines[4][2:].split() # ["HDSeed=...", "fingerprint=..."] - assert_true(seed_comment_line[0].split("=")[1] != seed_comment_line[1].split("=")[1], "The seed should not equal the fingerprint") + assert_true("Emergency Recovery Phrase" in file_lines[4], "Expected Emergency Recovery Phrase") + assert_true("language" in file_lines[5], "Expected mnemonic seed language") + assert_true("fingerprint" in file_lines[6], "Expected mnemonic seed fingerprint") (t_keys, i) = parse_wallet_file_lines(file_lines, 0) (sprout_keys, i) = parse_wallet_file_lines(file_lines, i) (sapling_keys, i) = parse_wallet_file_lines(file_lines, i) @@ -81,4 +80,4 @@ def parse_wallet_file_lines(file_lines, i): return ("".join(keys), i) if __name__ == '__main__': - WalletImportExportTest().main() \ No newline at end of file + WalletImportExportTest().main() From 5760a639d2257620fc87931c7673bad7100e0173 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 25 Oct 2021 10:55:23 -0600 Subject: [PATCH 089/514] Use 0x7FFFFFFF for the legacy account ID. --- src/wallet/asyncrpcoperation_saplingmigration.cpp | 2 +- src/wallet/test/rpc_wallet_tests.cpp | 4 ++-- src/zcash/address/zip32.cpp | 4 ++-- src/zcash/address/zip32.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 43f0f60b652..db6ae3a3d95 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -192,7 +192,7 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl } // Unless otherwise specified, the migration destination address is the address -// the legacy Sapling account (0x7FFFFFFE) at the smallest diversifier index +// the legacy Sapling account (0x7FFFFFFF) at the smallest diversifier index // that produces a valid diversified address. libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { KeyIO keyIO(Params()); diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index f415ec1c813..c12546a826d 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -847,8 +847,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { for (auto saplingObj : sapling_addr_sets.getValues()) { auto keypath = find_value(saplingObj, "zip32KeyPath").get_str(); - saplingSpendAuth0 |= (keypath == "m/32'/133'/2147483646'/0"); - saplingSpendAuth1 |= (keypath == "m/32'/133'/2147483646'/1"); + saplingSpendAuth0 |= (keypath == "m/32'/133'/2147483647'/0"); + saplingSpendAuth1 |= (keypath == "m/32'/133'/2147483647'/1"); auto saplingAddrs = find_value(saplingObj, "addresses").get_array(); saplingCountMismatch &= (saplingAddrs.size() != 1); } diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 38260631de6..e72899d3af3 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -33,9 +33,9 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz MnemonicSeed seed(language, mnemonic); // Verify that the seed data is valid entropy for unified spending keys at - // account 0 and at both the public & private chain levels for account 0x7FFFFFFE. + // account 0 and at both the public & private chain levels for account 0x7FFFFFFF. // It is not necessary to check for a valid diversified Sapling address at - // account 0x7FFFFFFE because derivation via the legacy path can simply search + // account 0x7FFFFFFF because derivation via the legacy path can simply search // for a valid diversifier; unlike in the unified spending key case, diversifier // indices don't need to line up with anything. if (libzcash::UnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 1b4da4777c7..b9867814bd0 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -26,7 +26,7 @@ const size_t ZIP32_XSK_SIZE = 169; * transparent and Sapling addresses via the legacy * `getnewaddress` and `z_getnewaddress` code paths, */ -const uint32_t ZCASH_LEGACY_ACCOUNT = 0x7FFFFFFE; +const uint32_t ZCASH_LEGACY_ACCOUNT = 0x7FFFFFFF; class CWalletDB; @@ -78,7 +78,7 @@ class MnemonicSeed: public HDSeed { /** * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible * to check that the generated seed can produce valid transparent and unified addresses at account - * numbers 0x7FFFFFFE and 0x0 respectively. + * numbers 0x7FFFFFFF and 0x0 respectively. */ static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); From 8bf4ec3b4aa2d0e0c4fd820bcb3d7ee49df1908d Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 27 Oct 2021 18:44:09 -0600 Subject: [PATCH 090/514] Require backup of the emergency recovery phrase. After 4.5.2, all wallets will be populated with an emergency recovery phrase, and all future addresses will be derived from the associated seed. To prevent potential loss of funds, we require that the user explicitly invoke the `walletconfirmbackup` RPC method to verify that they have backed up this seed. --- qa/rpc-tests/wallet_broadcast.py | 2 +- qa/rpc-tests/wallet_import_export.py | 31 +++++++++-- src/chainparams.cpp | 3 ++ src/chainparams.h | 12 +++-- src/key_constants.h | 2 +- src/rpc/protocol.h | 1 + src/wallet/rpcwallet.cpp | 72 +++++++++++++++++++++++-- src/wallet/test/rpc_wallet_tests.cpp | 29 ++-------- src/wallet/test/wallet_test_fixture.cpp | 4 +- src/wallet/wallet.cpp | 39 +++++++++++--- src/wallet/wallet.h | 6 ++- src/wallet/walletdb.h | 13 ++++- test/lint/lint-includes.sh | 2 + 13 files changed, 166 insertions(+), 50 deletions(-) diff --git a/qa/rpc-tests/wallet_broadcast.py b/qa/rpc-tests/wallet_broadcast.py index f0cebe355fa..f625c8aacdb 100755 --- a/qa/rpc-tests/wallet_broadcast.py +++ b/qa/rpc-tests/wallet_broadcast.py @@ -13,7 +13,7 @@ def run_test(self): #do some -walletbroadcast tests stop_nodes(self.nodes) wait_bitcoinds() - self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]]) + self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"]] * 3) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) diff --git a/qa/rpc-tests/wallet_import_export.py b/qa/rpc-tests/wallet_import_export.py index ddda99ee737..f0c01bf6e76 100755 --- a/qa/rpc-tests/wallet_import_export.py +++ b/qa/rpc-tests/wallet_import_export.py @@ -3,13 +3,16 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . +from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_true, start_nodes class WalletImportExportTest (BitcoinTestFramework): def setup_network(self, split=False): num_nodes = 3 - extra_args = [["-exportdir={}/export{}".format(self.options.tmpdir, i)] for i in range(num_nodes)] + extra_args = [([ + "-exportdir={}/export{}".format(self.options.tmpdir, i), + ] + (["-walletrequirebackup"] if i == 0 else [])) for i in range(num_nodes)] self.nodes = start_nodes(num_nodes, self.options.tmpdir, extra_args) def run_test(self): @@ -17,12 +20,28 @@ def run_test(self): privkey2 = self.nodes[2].z_exportkey(sapling_address2) self.nodes[0].z_importkey(privkey2) + # test walletconfirmbackup + try: + self.nodes[0].getnewaddress() + except JSONRPCException as e: + errorString = e.error['message'] + assert_equal("Error: Please acknowledge that you have backed up" in errorString, True) + try: + self.nodes[0].z_getnewaddress('sapling') + except JSONRPCException as e: + errorString = e.error['message'] + assert_equal("Error: Please acknowledge that you have backed up" in errorString, True) + dump_path0 = self.nodes[0].z_exportwallet('walletdumpmnem') + (mnemonic, t_keys0, sprout_keys0, sapling_keys0) = parse_wallet_file(dump_path0) + self.nodes[0].walletconfirmbackup(mnemonic) + + # Now that we've confirmed backup, we can generate addresses sprout_address0 = self.nodes[0].z_getnewaddress('sprout') sapling_address0 = self.nodes[0].z_getnewaddress('sapling') # node 0 should have the keys dump_path0 = self.nodes[0].z_exportwallet('walletdump') - (t_keys0, sprout_keys0, sapling_keys0) = parse_wallet_file(dump_path0) + (_, t_keys0, sprout_keys0, sapling_keys0) = parse_wallet_file(dump_path0) sapling_line_lengths = [len(sapling_key0.split(' #')[0].split()) for sapling_key0 in sapling_keys0.splitlines()] assert_equal(2, len(sapling_line_lengths), "Should have 2 sapling keys") @@ -35,7 +54,7 @@ def run_test(self): # node 1 should not have the keys dump_path1 = self.nodes[1].z_exportwallet('walletdumpbefore') - (t_keys1, sprout_keys1, sapling_keys1) = parse_wallet_file(dump_path1) + (_, t_keys1, sprout_keys1, sapling_keys1) = parse_wallet_file(dump_path1) assert_true(sprout_address0 not in sprout_keys1) assert_true(sapling_address0 not in sapling_keys1) @@ -45,7 +64,7 @@ def run_test(self): # node 1 should now have the keys dump_path1 = self.nodes[1].z_exportwallet('walletdumpafter') - (t_keys1, sprout_keys1, sapling_keys1) = parse_wallet_file(dump_path1) + (_, t_keys1, sprout_keys1, sapling_keys1) = parse_wallet_file(dump_path1) assert_true(sprout_address0 in sprout_keys1) assert_true(sapling_address0 in sapling_keys1) @@ -62,11 +81,13 @@ def parse_wallet_file(dump_path): assert_true("Emergency Recovery Phrase" in file_lines[4], "Expected Emergency Recovery Phrase") assert_true("language" in file_lines[5], "Expected mnemonic seed language") assert_true("fingerprint" in file_lines[6], "Expected mnemonic seed fingerprint") + mnemonic = file_lines[4].split("=")[1].strip() + print(mnemonic) (t_keys, i) = parse_wallet_file_lines(file_lines, 0) (sprout_keys, i) = parse_wallet_file_lines(file_lines, i) (sapling_keys, i) = parse_wallet_file_lines(file_lines, i) - return (t_keys, sprout_keys, sapling_keys) + return (mnemonic, t_keys, sprout_keys, sapling_keys) def parse_wallet_file_lines(file_lines, i): keys = [] diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d9a7cb182e1..4df45f935cb 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -729,6 +729,9 @@ class CRegTestParams : public CChainParams { // Founders reward script expects a vector of 2-of-3 multisig addresses vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight(0)); + + // do not require the wallet backup to be confirmed in regtest mode + fRequireWalletBackup = false; } void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight) diff --git a/src/chainparams.h b/src/chainparams.h index cf106277852..8ff3632f645 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -50,7 +50,7 @@ class CBaseKeyConstants : public KeyConstants { * a regression test mode which is intended for private networks only. It has * minimal difficulty to ensure that blocks can be found instantly. */ -class CChainParams: public KeyConstants +class CChainParams: public KeyConstants { public: const Consensus::Params& GetConsensus() const { return consensus; } @@ -62,6 +62,7 @@ class CChainParams: public KeyConstants CAmount SproutValuePoolCheckpointBalance() const { return nSproutValuePoolCheckpointBalance; } uint256 SproutValuePoolCheckpointBlockHash() const { return hashSproutValuePoolCheckpointBlock; } bool ZIP209Enabled() const { return fZIP209Enabled; } + bool RequireWalletBackup() const { return fRequireWalletBackup; } const CBlock& GenesisBlock() const { return genesis; } /** Make miner wait to have peers to avoid wasting work */ @@ -80,11 +81,11 @@ class CChainParams: public KeyConstants /** Return the BIP70 network string (main, test or regtest) */ std::string NetworkIDString() const { return keyConstants.NetworkIDString(); } const std::vector& DNSSeeds() const { return vSeeds; } - const std::vector& Base58Prefix(Base58Type type) const { - return keyConstants.Base58Prefix(type); + const std::vector& Base58Prefix(Base58Type type) const { + return keyConstants.Base58Prefix(type); } - const std::string& Bech32HRP(Bech32Type type) const { - return keyConstants.Bech32HRP(type); + const std::string& Bech32HRP(Bech32Type type) const { + return keyConstants.Bech32HRP(type); } const std::vector& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } @@ -121,6 +122,7 @@ class CChainParams: public KeyConstants CAmount nSproutValuePoolCheckpointBalance = 0; uint256 hashSproutValuePoolCheckpointBlock; bool fZIP209Enabled = false; + bool fRequireWalletBackup = true; }; /** diff --git a/src/key_constants.h b/src/key_constants.h index 58ac61e4b25..aa999dd5810 100644 --- a/src/key_constants.h +++ b/src/key_constants.h @@ -5,7 +5,7 @@ #ifndef ZCASH_KEY_CONSTANTS_H #define ZCASH_KEY_CONSTANTS_H -class KeyConstants +class KeyConstants { public: enum Base58Type { diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 0557975fcc9..83c1ef79c70 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -75,6 +75,7 @@ enum RPCErrorCode RPC_WALLET_WRONG_ENC_STATE = -15, //! Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) RPC_WALLET_ENCRYPTION_FAILED = -16, //! Failed to encrypt the wallet RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked + RPC_WALLET_BACKUP_REQUIRED = -18, //! User must acknowledge backup of the mnemonic seed. }; std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 82477411e1b..9b8f8692d8e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -37,6 +37,8 @@ #include #include +#include +#include #include #include @@ -53,6 +55,7 @@ using namespace libzcash; const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; +const bool DEFAULT_WALLET_REQUIRE_BACKUP = true; extern UniValue TxJoinSplitToJSON(const CTransaction& tx); @@ -81,6 +84,16 @@ bool EnsureWalletIsAvailable(bool avoidException) return true; } +void EnsureWalletIsBackedUp(const CChainParams& params) +{ + if (GetBoolArg("-walletrequirebackup", params.RequireWalletBackup()) && !pwalletMain->MnemonicVerified()) + throw JSONRPCError( + RPC_WALLET_BACKUP_REQUIRED, + "Error: Please acknowledge that you have backed up the wallet's emergency recovery phrase " + "with walletconfirmbackup first." + ); +} + void EnsureWalletIsUnlocked() { if (pwalletMain->IsLocked()) @@ -163,6 +176,9 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + const CChainParams& chainparams = Params(); + EnsureWalletIsBackedUp(chainparams); + if (!pwalletMain->IsLocked()) pwalletMain->TopUpKeyPool(); @@ -175,7 +191,7 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) std::string dummy_account; pwalletMain->SetAddressBook(keyID, dummy_account, "receive"); - KeyIO keyIO(Params()); + KeyIO keyIO(chainparams); return keyIO.EncodeDestination(keyID); } @@ -198,6 +214,9 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + const CChainParams& chainparams = Params(); + EnsureWalletIsBackedUp(chainparams); + if (!pwalletMain->IsLocked()) pwalletMain->TopUpKeyPool(); @@ -210,7 +229,7 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) CKeyID keyID = vchPubKey.GetID(); - KeyIO keyIO(Params()); + KeyIO keyIO(chainparams); return keyIO.EncodeDestination(keyID); } @@ -1586,6 +1605,8 @@ UniValue keypoolrefill(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsBackedUp(Params()); + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool unsigned int kpSize = 0; if (params.size() > 0) { @@ -1718,6 +1739,47 @@ UniValue walletpassphrasechange(const UniValue& params, bool fHelp) return NullUniValue; } +UniValue walletconfirmbackup(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 1) + throw runtime_error( + "walletconfirmbackup \"emergency recovery phrase\"\n" + "\nNotify the wallet that the user has backed up the emergency recovery phrase,\n" + "which can be obtained by making a call to z_exportwallet. The zcashd embedded wallet\n" + "requires confirmation that the emergency recovery phrase has been backed up before it\n" + "will permit new spending keys or addresses to be generated.\n" + "\nArguments:\n" + "1. \"emergency recovery phrase\" (string, required) The full recovery phrase returned as part\n" + " of the data returned by z_exportwallet. An error will be returned if the value provided\n" + " does not match the wallet's existing emergency recovery phrase.\n" + "\nExamples:\n" + + HelpExampleCli("walletconfirmbackup", "\"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + auto strMnemonicPhrase = params[0].get_str(); + boost::erase_all(strMnemonicPhrase, "\""); + boost::trim(strMnemonicPhrase); + if (strMnemonicPhrase.length() > 0) { + if (!pwalletMain->VerifyMnemonicSeed(strMnemonicPhrase)) + throw JSONRPCError( + RPC_WALLET_PASSPHRASE_INCORRECT, + "Error: The emergency recovery phrase entered was incorrect."); + } else { + throw runtime_error( + "walletconfirmbackup \"emergency recovery phrase\"\n" + "Notify the wallet that the user has backed up the emergency recovery phrase"); + } + + return NullUniValue; +} + UniValue walletlock(const UniValue& params, bool fHelp) { @@ -2929,14 +2991,17 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + const CChainParams& chainparams = Params(); + EnsureWalletIsUnlocked(); + EnsureWalletIsBackedUp(chainparams); auto addrType = defaultType; if (params.size() > 0) { addrType = params[0].get_str(); } - KeyIO keyIO(Params()); + KeyIO keyIO(chainparams); if (addrType == ADDR_TYPE_SPROUT) { return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()); } else if (addrType == ADDR_TYPE_SAPLING) { @@ -4910,6 +4975,7 @@ static const CRPCCommand commands[] = { "wallet", "walletlock", &walletlock, true }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, true }, { "wallet", "walletpassphrase", &walletpassphrase, true }, + { "wallet", "walletconfirmbackup", &walletconfirmbackup, true }, { "wallet", "zcbenchmark", &zc_benchmark, true }, { "wallet", "zcrawkeygen", &zc_raw_keygen, true }, { "wallet", "zcrawjoinsplit", &zc_raw_joinsplit, true }, diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index c12546a826d..28c94e9c28b 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -197,6 +197,11 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) *********************************/ BOOST_CHECK_NO_THROW(CallRPC("listaddressgroupings")); + /********************************* + * walletconfirmbackup + *********************************/ + BOOST_CHECK_THROW(CallRPC(string("walletconfirmbackup \"badmnemonic\"")), runtime_error); + /********************************* * getrawchangeaddress *********************************/ @@ -798,12 +803,6 @@ void CheckHaveAddr(const libzcash::PaymentAddress& addr) { BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { using namespace libzcash; - if (!pwalletMain->HaveLegacyHDSeed()) { - // fake a legacy seed by creating a separate mnemonic seed - auto seed = MnemonicSeed::Random(1); - pwalletMain->LoadLegacyHDSeed(seed); - } - UniValue addr; KeyIO keyIO(Params()); @@ -1562,12 +1561,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys) { LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->HaveLegacyHDSeed()) { - // fake a legacy seed by creating a separate mnemonic seed - auto seed = MnemonicSeed::Random(1); - pwalletMain->LoadLegacyHDSeed(seed); - } - UniValue retValue; int n = 100; @@ -1625,12 +1618,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) { LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->HaveLegacyHDSeed()) { - // fake a legacy seed by creating a separate mnemonic seed - auto seed = MnemonicSeed::Random(1); - pwalletMain->LoadLegacyHDSeed(seed); - } - UniValue retValue; int n = 100; @@ -1694,12 +1681,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_listunspent_parameters) { SelectParams(CBaseChainParams::TESTNET); - if (!pwalletMain->HaveLegacyHDSeed()) { - // fake a legacy seed by creating a separate mnemonic seed - auto seed = MnemonicSeed::Random(1); - pwalletMain->LoadLegacyHDSeed(seed); - } - LOCK2(cs_main, pwalletMain->cs_wallet); UniValue retValue; diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index dd591af56a3..0b5694f47e0 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -11,8 +11,10 @@ WalletTestingSetup::WalletTestingSetup(): TestingSetup() bool fFirstRun; pwalletMain = new CWallet(Params(), "wallet_test.dat"); pwalletMain->LoadWallet(fFirstRun); - if (!pwalletMain->HaveMnemonicSeed()) + if (!pwalletMain->HaveMnemonicSeed()) { pwalletMain->GenerateNewSeed(); + pwalletMain->VerifyMnemonicSeed(pwalletMain->GetMnemonicSeed().value().GetMnemonic()); + } RegisterValidationInterface(pwalletMain); RegisterWalletRPCCommands(tableRPC); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 67ef4f91117..77ad2fa1cb6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -110,14 +110,13 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() return addr; } -// Generate a new Sapling spending key (generate a new account) based upon -// the legacy HD seed associated with this wallet and return its -// public payment address. +// Generate a new Sapling spending key as a child of the legacy Sapling account +// return its public payment address. // -// The z_getnewaddress API must use the legacy HD seed, and fail if that seed -// is not present. When using legacy HD seeds, the account index is determined -// by trial of legacyHDChain.GetAccountCounter(); for unified addresses this must use -// valued derived from legacyHDChain.unifiedAccountCounter +// The z_getnewaddress API must use the mnemonic HD seed, and fail if that seed +// is not present. The account index is determined by trial of values of +// mnemonicHDChain.GetLegacySaplingKeyCounter() until one is found that produces +// a valid Sapling key. SaplingPaymentAddress CWallet::GenerateNewLegacySaplingZKey() { AssertLockHeld(cs_wallet); @@ -2351,6 +2350,31 @@ bool CWallet::SetCryptedMnemonicSeed(const uint256& seedFp, const std::vectorGetMnemonicSeed(); if (!seed.has_value()) { @@ -4783,6 +4807,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + strUsage += HelpMessageOpt("-walletrequirebackup=false", _("Allow generation of new spending keys & addresses from the mnemonic seed, even if the backup of that seed has not yet been confirmed with `walletconfirmbackup`.")); if (showDebug) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index cbfe6fce1f5..615bb5cbc39 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -806,8 +806,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* the hd chain metadata for keys derived from the mnemonic seed */ std::optional mnemonicHDChain; - /* the hd chain metadata for keys derived from the legacy seed */ - std::optional legacyHDChain; /* the network ID string for the network for which this wallet was created */ std::string networkIdString; @@ -1306,6 +1304,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SetMnemonicSeed(const MnemonicSeed& seed); bool SetCryptedMnemonicSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + /* Checks the wallet's seed against the specified mnemonic, and marks the + * wallet's seed as having been backed up if the phrases match. */ + bool VerifyMnemonicSeed(std::string mnemonic); + bool MnemonicVerified(); /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ bool LoadMnemonicSeed(const MnemonicSeed& seed); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2f808822d14..4896d608de5 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -51,6 +51,7 @@ class CHDChain uint32_t accountCounter; uint32_t legacyTKeyCounter; uint32_t legacySaplingKeyCounter; + bool mnemonicSeedBackupConfirmed; CHDChain() { SetNull(); } @@ -62,12 +63,13 @@ class CHDChain accountCounter = 0; legacyTKeyCounter = 0; legacySaplingKeyCounter = 0; + mnemonicSeedBackupConfirmed = false; } public: static const int VERSION_HD_BASE = 1; static const int CURRENT_VERSION = VERSION_HD_BASE; - CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0), legacySaplingKeyCounter(0) {} + CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0), legacySaplingKeyCounter(0), mnemonicSeedBackupConfirmed(false) {} ADD_SERIALIZE_METHODS; @@ -80,6 +82,7 @@ class CHDChain READWRITE(accountCounter); READWRITE(legacyTKeyCounter); READWRITE(legacySaplingKeyCounter); + READWRITE(mnemonicSeedBackupConfirmed); } template @@ -116,6 +119,14 @@ class CHDChain void IncrementLegacySaplingKeyCounter() { legacySaplingKeyCounter += 1; } + + void SetMnemonicSeedBackupConfirmed() { + mnemonicSeedBackupConfirmed = true; + } + + bool IsMnemonicSeedBackupConfirmed() { + return mnemonicSeedBackupConfirmed; + } }; /** Access to the wallet database */ diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index a9fdab331c6..29489c70288 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -42,10 +42,12 @@ EXPECTED_BOOST_INCLUDES=( boost/algorithm/string.hpp boost/algorithm/string/case_conv.hpp boost/algorithm/string/classification.hpp + boost/algorithm/string/erase.hpp boost/algorithm/string/join.hpp boost/algorithm/string/predicate.hpp boost/algorithm/string/replace.hpp boost/algorithm/string/split.hpp + boost/algorithm/string/trim.hpp boost/assert.hpp boost/assign/list_of.hpp boost/assign/std/vector.hpp From 477a166565a53931512d7bc37a6e2f45e4b45db6 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 28 Oct 2021 13:07:58 -0600 Subject: [PATCH 091/514] Use hardened derivation for the legacy Sapling key at the address index level. --- src/wallet/test/rpc_wallet_tests.cpp | 4 ++-- src/zcash/address/zip32.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 28c94e9c28b..46ebacd57f5 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -846,8 +846,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { for (auto saplingObj : sapling_addr_sets.getValues()) { auto keypath = find_value(saplingObj, "zip32KeyPath").get_str(); - saplingSpendAuth0 |= (keypath == "m/32'/133'/2147483647'/0"); - saplingSpendAuth1 |= (keypath == "m/32'/133'/2147483647'/1"); + saplingSpendAuth0 |= (keypath == "m/32'/133'/2147483647'/0'"); + saplingSpendAuth1 |= (keypath == "m/32'/133'/2147483647'/1'"); auto saplingAddrs = find_value(saplingObj, "addresses").get_array(); saplingCountMismatch &= (saplingAddrs.size() != 1); } diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index e72899d3af3..052fd50b62c 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -285,8 +285,8 @@ std::pair SaplingExtendedSpendingKey:: // Derive account key at the legacy account index auto m_32h_cth_l = m_32h_cth.Derive(ZCASH_LEGACY_ACCOUNT | ZIP32_HARDENED_KEY_LIMIT); - // Derive key at the specified address index (non-hardened) - auto xsk = m_32h_cth_l.Derive(addressIndex); + // Derive key at the specified address index + auto xsk = m_32h_cth_l.Derive(addressIndex | ZIP32_HARDENED_KEY_LIMIT); // Create new metadata int64_t nCreationTime = GetTime(); @@ -294,7 +294,7 @@ std::pair SaplingExtendedSpendingKey:: metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(ZCASH_LEGACY_ACCOUNT) + "'/" - + std::to_string(addressIndex); + + std::to_string(addressIndex) + "'"; metadata.seedFp = seed.Fingerprint(); return std::make_pair(xsk, metadata); From b67e62d9776e313a6178da5227b1f229c294da20 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 28 Oct 2021 13:55:18 -0600 Subject: [PATCH 092/514] Address comments from code review. --- src/gtest/test_zip32.cpp | 9 +++++ src/key.cpp | 13 +++---- src/key.h | 6 ++-- src/keystore.cpp | 8 +---- src/keystore.h | 1 - src/sendalert.cpp | 7 ++-- src/test/alert_tests.cpp | 7 ++-- src/test/arith_uint256_tests.cpp | 8 ----- src/uint256.h | 42 ---------------------- src/wallet/crypter.cpp | 10 ------ src/wallet/crypter.h | 1 - src/wallet/gtest/test_wallet_zkeys.cpp | 10 +++--- src/wallet/wallet.h | 1 - src/zcash/address/zip32.h | 49 ++++++++++++++++++++++---- 14 files changed, 73 insertions(+), 99 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index fc7a7275869..a6fce09a9fc 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -153,3 +153,12 @@ TEST(ZIP32, ParseZip32KeypathAccount) { EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 2147483646); } + +TEST(ZIP32, diversifier_index_t_increment) +{ + libzcash::diversifier_index_t d_zero(0); + libzcash::diversifier_index_t d_one(1); + EXPECT_TRUE(d_zero.increment()); + EXPECT_EQ(d_zero, d_one); +} + diff --git a/src/key.cpp b/src/key.cpp index 75dbcd33cea..dfbd3019c1a 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -166,12 +166,13 @@ CKey CKey::TestOnlyRandomKey(bool fCompressedIn) { return key; } -bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { - if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) - return false; - fCompressed = fCompressedIn; - fValid = true; - return true; +std::optional CKey::FromPrivKey(const CPrivKey &privkey, bool fCompressedIn) { + CKey key; + if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)key.begin(), &privkey[0], privkey.size())) + return std::nullopt; + key.fCompressed = fCompressedIn; + key.fValid = true; + return key; } CPrivKey CKey::GetPrivKey() const { diff --git a/src/key.h b/src/key.h index 654e8bcd70d..d5354d61fe2 100644 --- a/src/key.h +++ b/src/key.h @@ -68,7 +68,8 @@ class CKey */ static CKey TestOnlyRandomKey(bool fCompressedIn); - static std::optional FromEntropy(std::vector> keydata, bool fComporessedIn); + //! Initialize from a CPrivKey (serialized OpenSSL-format private key data). + static std::optional FromPrivKey(const CPrivKey& vchPrivKey, bool fCompressed); /** Check that required EC support is available at runtime. */ static bool ECC_InitSanityCheck(); @@ -106,9 +107,6 @@ class CKey //! Check whether the public key corresponding to this private key is (to be) compressed. bool IsCompressed() const { return fCompressed; } - //! Initialize from a CPrivKey (serialized OpenSSL-format private key data). - bool SetPrivKey(const CPrivKey& vchPrivKey, bool fCompressed); - /** * Convert the private key to a CPrivKey (serialized OpenSSL-format private key data). * This is expensive. diff --git a/src/keystore.cpp b/src/keystore.cpp index 9287787be6f..e17ac4f0ca3 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -56,6 +56,7 @@ std::optional CBasicKeyStore::GetMnemonicSeed() const bool CBasicKeyStore::SetLegacyHDSeed(const HDSeed& seed) { + LOCK(cs_KeyStore); if (legacySeed.has_value()) { // Don't allow an existing seed to be changed. return false; @@ -64,12 +65,6 @@ bool CBasicKeyStore::SetLegacyHDSeed(const HDSeed& seed) return true; } -bool CBasicKeyStore::HaveLegacyHDSeed() const -{ - LOCK(cs_KeyStore); - return legacySeed.has_value(); -} - std::optional CBasicKeyStore::GetLegacyHDSeed() const { LOCK(cs_KeyStore); @@ -201,7 +196,6 @@ bool CBasicKeyStore::AddSaplingFullViewingKey( auto ivk = extfvk.fvk.in_viewing_key(); mapSaplingFullViewingKeys[ivk] = extfvk; - // TODO: check whether DefaultAddress is the right thing to use here. return CBasicKeyStore::AddSaplingIncomingViewingKey(ivk, extfvk.DefaultAddress()); } diff --git a/src/keystore.h b/src/keystore.h index 1774d37bf34..7bad4694362 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -144,7 +144,6 @@ class CBasicKeyStore : public CKeyStore std::optional GetMnemonicSeed() const; bool SetLegacyHDSeed(const HDSeed& seed); - bool HaveLegacyHDSeed() const; std::optional GetLegacyHDSeed() const; bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); diff --git a/src/sendalert.cpp b/src/sendalert.cpp index 05cbdf9f57f..d1b839e740f 100644 --- a/src/sendalert.cpp +++ b/src/sendalert.cpp @@ -117,13 +117,12 @@ void ThreadSendAlert() CDataStream sMsg(SER_NETWORK, CLIENT_VERSION); sMsg << *(CUnsignedAlert*)&alert; alert.vchMsg = std::vector(sMsg.begin(), sMsg.end()); - CKey key; - if (!key.SetPrivKey(vchPrivKey, false)) - { + std::optional key = CKey::FromPrivKey(vchPrivKey, false); + if (!key.has_value()) { printf("ThreadSendAlert() : key.SetPrivKey failed\n"); return; } - if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + if (!key.value().Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) { printf("ThreadSendAlert() : key.Sign failed\n"); return; diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index ec428f45165..fbfc7fa1611 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -117,13 +117,12 @@ bool SignAlert(CAlert &alert) // sign alert std::vector vchTmp(ParseHex(pszPrivKey)); CPrivKey vchPrivKey(vchTmp.begin(), vchTmp.end()); - CKey key; - if (!key.SetPrivKey(vchPrivKey, false)) - { + std::optional key = CKey::FromPrivKey(SetPrivKey(vchPrivKey, false); + if (!key.has_value()) { printf("key.SetPrivKey failed\n"); return false; } - if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + if (!key.value().Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) { printf("SignAlert() : key.Sign failed\n"); return false; diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp index 8928871bf87..a1b7e26257c 100644 --- a/src/test/arith_uint256_tests.cpp +++ b/src/test/arith_uint256_tests.cpp @@ -564,12 +564,4 @@ BOOST_AUTO_TEST_CASE( getmaxcoverage ) // some more tests just to get 100% cover CHECKBITWISEOPERATOR(R1,~R2,&) } -BOOST_AUTO_TEST_CASE( blob88_increment ) -{ - blob88 b_zero(0); - blob88 b_one(1); - BOOST_CHECK(b_zero.increment()); - BOOST_CHECK(b_zero == b_one); -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/uint256.h b/src/uint256.h index 68000108d0a..cf150f2c5a7 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -102,47 +101,6 @@ class base_blob } }; -/** 88-bit opaque blob. - */ -class blob88 : public base_blob<88> { -public: - blob88() {} - blob88(const base_blob<88>& b) : base_blob<88>(b) {} - blob88(uint64_t i): base_blob<88>() { - data[0] = (uint8_t) i; - data[1] = (uint8_t) (i >> 8); - data[2] = (uint8_t) (i >> 16); - data[3] = (uint8_t) (i >> 24); - data[4] = (uint8_t) (i >> 32); - data[5] = (uint8_t) (i >> 40); - data[6] = (uint8_t) (i >> 48); - data[7] = (uint8_t) (i >> 56); - } - explicit blob88(const std::vector& vch) : base_blob<88>(vch) {} - - bool increment() { - for (int i = 0; i < 11; i++) { - this->data[i] += 1; - if (this->data[i] != 0) { - return true; // no overflow - } - } - - return false; //overflow - } - - // treat as little-endian for numeric comparison - bool less_than_le(const blob88& other) const { - for (int i = 10; i >= 0; i--) { - if (data[i] < other.data[i]) { - return true; - } - } - - return false; - } -}; - /** 160-bit opaque blob. * @note This type is called uint160 for historical reasons only. It is an opaque * blob of 160 bits and has no integer operations. diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 3f5d1c7b793..0439ec48052 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -447,16 +447,6 @@ bool CCryptoKeyStore::SetCryptedLegacyHDSeed( } } -bool CCryptoKeyStore::HaveLegacyHDSeed() const -{ - LOCK(cs_KeyStore); - if (fUseCrypto) { - return cryptedLegacySeed.has_value(); - } else { - return CBasicKeyStore::HaveLegacyHDSeed(); - } -} - std::optional CCryptoKeyStore::GetLegacyHDSeed() const { LOCK(cs_KeyStore); if (fUseCrypto) { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 6a11c5a527b..434e97cbef2 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -180,7 +180,6 @@ class CCryptoKeyStore : public CBasicKeyStore bool SetLegacyHDSeed(const HDSeed& seed); virtual bool SetCryptedLegacyHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); - bool HaveLegacyHDSeed() const; std::optional GetLegacyHDSeed() const; virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 26fa8b45c1b..4a9a5b06ab8 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -71,8 +71,8 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // Generate a diversified address different to the default // If we can't get an early diversified address, we are very unlucky - blob88 diversifier(2); - auto dpa = sk.ToXFVK().FindAddress(diversifier).first; + libzcash::diversifier_index_t j(2); + auto dpa = sk.ToXFVK().FindAddress(j).first; // verify wallet only has the default address EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.ToXFVK().DefaultAddress())); @@ -100,7 +100,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { ASSERT_EQ(wallet.mapSaplingZKeyMetadata[ivk2].nCreateTime, now); // Load a diversified address for the third key into the wallet - auto dpa2 = sk2.ToXFVK().FindAddress(diversifier).first; + auto dpa2 = sk2.ToXFVK().FindAddress(j).first; EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk2.ToXFVK().DefaultAddress())); EXPECT_FALSE(wallet.HaveSaplingIncomingViewingKey(dpa2)); EXPECT_TRUE(wallet.LoadSaplingPaymentAddress(dpa2, ivk2)); @@ -454,8 +454,8 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { // If we can't get an early diversified address, we are very unlucky libzcash::SaplingExtendedSpendingKey extsk; EXPECT_TRUE(wallet.GetSaplingExtendedSpendingKey(address, extsk)); - blob88 diversifier(2); - auto dpa = extsk.ToXFVK().FindAddress(diversifier).first; + libzcash::diversifier_index_t j(2); + auto dpa = extsk.ToXFVK().FindAddress(j).first; // Add diversified address to the wallet auto ivk = extsk.expsk.full_viewing_key().in_viewing_key(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 615bb5cbc39..4da9935ecc4 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -827,7 +827,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapKeyMetadata; std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; - //std::map mapUnifiedKeyMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index b9867814bd0..0aa2a54d7c4 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -11,7 +11,7 @@ #include "uint256.h" #include "utiltime.h" #include "zcash/address/sapling.hpp" -#include "rust/zip339.h" +#include #include #include @@ -28,9 +28,6 @@ const size_t ZIP32_XSK_SIZE = 169; */ const uint32_t ZCASH_LEGACY_ACCOUNT = 0x7FFFFFFF; - -class CWalletDB; - typedef std::vector> RawHDSeed; class HDSeed { @@ -39,7 +36,6 @@ class HDSeed { HDSeed() {} public: - // HDSeed(RawHDSeed& seedIn) : seed(seedIn) {} template @@ -204,7 +200,48 @@ class CKeyMetadata namespace libzcash { -typedef blob88 diversifier_index_t; +/** + * 88-bit diversifier index. This would ideally derive from base_uint + * but those values must have bit widths that are multiples of 32. + */ +class diversifier_index_t : public base_blob<88> { +public: + diversifier_index_t() {} + diversifier_index_t(const base_blob<88>& b) : base_blob<88>(b) {} + diversifier_index_t(uint64_t i): base_blob<88>() { + data[0] = (uint8_t) i; + data[1] = (uint8_t) (i >> 8); + data[2] = (uint8_t) (i >> 16); + data[3] = (uint8_t) (i >> 24); + data[4] = (uint8_t) (i >> 32); + data[5] = (uint8_t) (i >> 40); + data[6] = (uint8_t) (i >> 48); + data[7] = (uint8_t) (i >> 56); + } + explicit diversifier_index_t(const std::vector& vch) : base_blob<88>(vch) {} + + bool increment() { + for (int i = 0; i < 11; i++) { + this->data[i] += 1; + if (this->data[i] != 0) { + return true; // no overflow + } + } + + return false; //overflow + } + + // treat as little-endian for numeric comparison + bool less_than_le(const diversifier_index_t& other) const { + for (int i = 10; i >= 0; i--) { + if (data[i] < other.data[i]) { + return true; + } + } + + return false; + } +}; struct SaplingExtendedFullViewingKey { uint8_t depth; From 22fa6b22ea31b8457dd0492995d2ad056ad2315e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 28 Oct 2021 15:07:11 -0600 Subject: [PATCH 093/514] Restore legacy HD seed storage & retrieval tests. --- src/gtest/test_keystore.cpp | 29 ++++++++++++++++++++++++++++- src/zcash/address/zip32.h | 10 ++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 5f4ea3ab0ea..8c8dff1d30b 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -16,7 +16,7 @@ const uint32_t SLIP44_TESTNET_TYPE = 1; -TEST(KeystoreTests, StoreAndRetrieveHDSeed) { +TEST(KeystoreTests, StoreAndRetrieveMnemonicSeed) { CBasicKeyStore keyStore; // When we haven't set a seed, we shouldn't get one @@ -45,6 +45,33 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeed) { EXPECT_EQ(seed, seedOut.value()); } +TEST(KeystoreTests, StoreAndRetrieveLegacyHDSeed) { + CBasicKeyStore keyStore; + + // When we haven't set a seed, we shouldn't get one + std::optional seedOut = keyStore.GetLegacyHDSeed(); + EXPECT_FALSE(seedOut.has_value()); + + // Generate a random seed + HDSeed seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + + // We should be able to set and retrieve the seed + ASSERT_TRUE(keyStore.SetLegacyHDSeed(seed)); + seedOut = keyStore.GetLegacyHDSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); + + // Generate another random seed + HDSeed seed2 = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + EXPECT_NE(seed, seed2); + + // We should not be able to set and retrieve a different seed + EXPECT_FALSE(keyStore.SetLegacyHDSeed(seed2)); + seedOut = keyStore.GetLegacyHDSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); +} + TEST(KeystoreTests, SaplingKeys) { // ["sk, ask, nsk, ovk, ak, nk, ivk, default_d, default_pk_d, note_v, note_r, note_cm, note_pos, note_nf"], UniValue sapling_keys = read_json(MAKE_STRING(json_tests::sapling_key_components)); diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 0aa2a54d7c4..86184b10b3b 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -48,6 +48,16 @@ class HDSeed { uint256 Fingerprint() const; RawHDSeed RawSeed() const { return seed; } + + friend bool operator==(const HDSeed& a, const HDSeed& b) + { + return a.seed == b.seed; + } + + friend bool operator!=(const HDSeed& a, const HDSeed& b) + { + return !(a == b); + } }; From dbcdc560dee94d655d9e96792903d99ddfac1005 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 28 Oct 2021 17:15:32 -0600 Subject: [PATCH 094/514] Fix spurious test passage. --- src/wallet/test/rpc_wallet_tests.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 46ebacd57f5..3861a72555b 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -1145,28 +1145,28 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // duplicate address BOOST_CHECK_THROW(CallRPC("z_sendmany " "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " - "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}," - " {\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":12.0} ]" + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\",\"amount\":50.0}," + "{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\",\"amount\":12.0}]" ), runtime_error); // invalid fee amount, cannot be negative BOOST_CHECK_THROW(CallRPC("z_sendmany " "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " - "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\",\"amount\":50.0}] " "1 -0.00001" ), runtime_error); // invalid fee amount, bigger than MAX_MONEY BOOST_CHECK_THROW(CallRPC("z_sendmany " "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " - "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\",\"amount\":50.0}] " "1 21000001" ), runtime_error); // fee amount is bigger than sum of outputs BOOST_CHECK_THROW(CallRPC("z_sendmany " "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " - "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\",\"amount\":50.0}] " "1 50.00000001" ), runtime_error); @@ -1178,7 +1178,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) KeyIO keyIO(Params()); std::string zaddr1 = keyIO.EncodePaymentAddress(pa); BOOST_CHECK_THROW(CallRPC(string("z_sendmany tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ ") - + "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), runtime_error); + + "[{\"address\":\"" + zaddr1 + "\",\"amount\":123.456}]"), runtime_error); // Mutable tx containing contextual information we need to build tx UniValue retValue = CallRPC("getblockcount"); From 30517a002b4680d49b77df3a010bcb3b809cc417 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 29 Oct 2021 12:28:47 -0600 Subject: [PATCH 095/514] Move CKeyMetadata back to wallet.h --- src/wallet/wallet.cpp | 13 +++++--- src/wallet/walletdb.h | 43 +++++++++++++++++++++++++++ src/zcash/address/zip32.cpp | 47 ++++++++++------------------- src/zcash/address/zip32.h | 59 +++++-------------------------------- 4 files changed, 76 insertions(+), 86 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 77ad2fa1cb6..80aa1e416ad 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -159,7 +159,10 @@ std::pair CWallet::GenerateLegacySaplingZKey(u auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy(seed, BIP44CoinType(), addrIndex); if (!HaveSaplingSpendingKey(xsk.first.ToXFVK())) { auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key(); - mapSaplingZKeyMetadata[ivk] = xsk.second; + CKeyMetadata keyMeta(GetTime()); + keyMeta.hdKeypath = xsk.second; + keyMeta.seedFp = seed.Fingerprint(); + mapSaplingZKeyMetadata[ivk] = keyMeta; if (!AddSaplingZKey(xsk.first)) { throw std::runtime_error("CWallet::GenerateLegacySaplingZKey(): AddSaplingZKey failed."); @@ -264,7 +267,7 @@ CPubKey CWallet::GenerateNewKey() auto seed = seedOpt.value(); if (!mnemonicHDChain.has_value()) { - mnemonicHDChain = CHDChain(seedOpt.value().Fingerprint(), GetTime()); + mnemonicHDChain = CHDChain(seed.Fingerprint(), GetTime()); } CHDChain& hdChain = mnemonicHDChain.value(); @@ -272,7 +275,7 @@ CPubKey CWallet::GenerateNewKey() // a valid spending key for the account ZCASH_LEGACY_ACCOUNT; // therefore, the `value()` call here is safe. BIP32AccountChains accountChains = BIP32AccountChains::ForAccount( - seedOpt.value(), + seed, BIP44CoinType(), ZCASH_LEGACY_ACCOUNT).value(); @@ -287,7 +290,9 @@ CPubKey CWallet::GenerateNewKey() assert(secret.VerifyPubKey(pubkey)); // Create new metadata - const CKeyMetadata& keyMeta = extKey.value().second; + CKeyMetadata keyMeta(GetTime()); + keyMeta.hdKeypath = extKey.value().second; + keyMeta.seedFp = seed.Fingerprint(); mapKeyMetadata[pubkey.GetID()] = keyMeta; if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) nTimeFirstKey = keyMeta.nCreateTime; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 4896d608de5..f87fcb44b0a 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -129,6 +129,49 @@ class CHDChain } }; +class CKeyMetadata +{ +public: + static const int VERSION_BASIC=1; + static const int VERSION_WITH_HDDATA=10; + static const int CURRENT_VERSION=VERSION_WITH_HDDATA; + int nVersion; + int64_t nCreateTime; // 0 means unknown + std::string hdKeypath; //optional HD/zip32 keypath + uint256 seedFp; + + CKeyMetadata() + { + SetNull(); + } + CKeyMetadata(int64_t nCreateTime_) + { + SetNull(); + nCreateTime = nCreateTime_; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(this->nVersion); + READWRITE(nCreateTime); + if (this->nVersion >= VERSION_WITH_HDDATA) + { + READWRITE(hdKeypath); + READWRITE(seedFp); + } + } + + void SetNull() + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = 0; + hdKeypath.clear(); + seedFp.SetNull(); + } +}; + /** Access to the wallet database */ class CWalletDB : public CDB { diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 052fd50b62c..dd1b4b62cb0 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -76,7 +76,7 @@ namespace libzcash { // Transparent // -std::optional> DeriveZip32TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> DeriveZip32TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { auto rawSeed = seed.RawSeed(); auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); @@ -93,12 +93,9 @@ std::optional> DeriveZip32TransparentAccountKey auto result = m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); if (!result.has_value()) return std::nullopt; - int64_t nCreationTime = GetTime(); - auto keyMeta = CKeyMetadata(nCreationTime); - keyMeta.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - keyMeta.seedFp = seed.Fingerprint(); + auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - return std::make_pair(result.value(), keyMeta); + return std::make_pair(result.value(), hdKeypath); } std::optional BIP32AccountChains::ForAccount( @@ -117,36 +114,30 @@ std::optional BIP32AccountChains::ForAccount( return BIP32AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); } -std::optional> BIP32AccountChains::DeriveExternal(uint32_t addrIndex) { +std::optional> BIP32AccountChains::DeriveExternal(uint32_t addrIndex) { auto childKey = external.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; - int64_t nCreationTime = GetTime(); - auto keyMeta = CKeyMetadata(nCreationTime); - keyMeta.hdKeypath = "m/32'/" + auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'/" + "0/" + std::to_string(addrIndex); - keyMeta.seedFp = seedFp; - return std::make_pair(childKey.value(), keyMeta); + return std::make_pair(childKey.value(), hdKeypath); } -std::optional> BIP32AccountChains::DeriveInternal(uint32_t addrIndex) { +std::optional> BIP32AccountChains::DeriveInternal(uint32_t addrIndex) { auto childKey = internal.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; - int64_t nCreationTime = GetTime(); - auto keyMeta = CKeyMetadata(nCreationTime); - keyMeta.hdKeypath = "m/32'/" + auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'/" + "1/" + std::to_string(addrIndex); - keyMeta.seedFp = seedFp; - return std::make_pair(childKey.value(), keyMeta); + return std::make_pair(childKey.value(), hdKeypath); } // @@ -251,7 +242,7 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const return xsk_i; } -std::pair SaplingExtendedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::pair SaplingExtendedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/account' @@ -264,15 +255,12 @@ std::pair SaplingExtendedSpendingKey:: auto xsk = m_32h_cth.Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); // Create new metadata - int64_t nCreationTime = GetTime(); - CKeyMetadata keyMeta(nCreationTime); - keyMeta.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - keyMeta.seedFp = seed.Fingerprint(); + auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - return std::make_pair(xsk, keyMeta); + return std::make_pair(xsk, hdKeypath); } -std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { +std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/account'/addressIndex' @@ -289,15 +277,12 @@ std::pair SaplingExtendedSpendingKey:: auto xsk = m_32h_cth_l.Derive(addressIndex | ZIP32_HARDENED_KEY_LIMIT); // Create new metadata - int64_t nCreationTime = GetTime(); - CKeyMetadata metadata(nCreationTime); - metadata.hdKeypath = "m/32'/" + auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(ZCASH_LEGACY_ACCOUNT) + "'/" + std::to_string(addressIndex) + "'"; - metadata.seedFp = seed.Fingerprint(); - return std::make_pair(xsk, metadata); + return std::make_pair(xsk, hdKeypath); } SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const @@ -316,7 +301,7 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const // Unified // -std::optional> UnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> UnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { UnifiedSpendingKey usk; usk.accountId = accountId; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 86184b10b3b..4d9e61413cb 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -30,6 +30,8 @@ const uint32_t ZCASH_LEGACY_ACCOUNT = 0x7FFFFFFF; typedef std::vector> RawHDSeed; +typedef std::string HDKeyPath; + class HDSeed { protected: RawHDSeed seed; @@ -163,51 +165,6 @@ class MnemonicSeed: public HDSeed { // This is not part of ZIP 32, but is here because it's linked to the HD seed. uint256 ovkForShieldingFromTaddr(HDSeed& seed); -// Key derivation metadata -class CKeyMetadata -{ -public: - static const int VERSION_BASIC=1; - static const int VERSION_WITH_HDDATA=10; - static const int CURRENT_VERSION=VERSION_WITH_HDDATA; - int nVersion; - int64_t nCreateTime; // 0 means unknown - std::string hdKeypath; //optional HD/zip32 keypath - uint256 seedFp; - - CKeyMetadata() - { - SetNull(); - } - CKeyMetadata(int64_t nCreateTime_) - { - SetNull(); - nCreateTime = nCreateTime_; - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(nCreateTime); - if (this->nVersion >= VERSION_WITH_HDDATA) - { - READWRITE(hdKeypath); - READWRITE(seedFp); - } - } - - void SetNull() - { - nVersion = CKeyMetadata::CURRENT_VERSION; - nCreateTime = 0; - hdKeypath.clear(); - seedFp.SetNull(); - } -}; - - namespace libzcash { /** @@ -329,8 +286,8 @@ struct SaplingExtendedSpendingKey { } static SaplingExtendedSpendingKey Master(const HDSeed& seed); - static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); - static std::pair Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); + static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + static std::pair Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); SaplingExtendedSpendingKey Derive(uint32_t i) const; @@ -408,7 +365,7 @@ class UnifiedSpendingKey { UnifiedSpendingKey() {} public: - static std::optional> ForAccount( + static std::optional> ForAccount( const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); @@ -426,7 +383,7 @@ class UnifiedSpendingKey { std::optional ParseZip32KeypathAccount(const std::string& keyPath); -std::optional> DeriveZip32TransparentMasterKey( +std::optional> DeriveZip32TransparentMasterKey( const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); @@ -447,8 +404,8 @@ class BIP32AccountChains { uint32_t bip44CoinType, uint32_t accountId); - std::optional> DeriveExternal(uint32_t addrIndex); - std::optional> DeriveInternal(uint32_t addrIndex); + std::optional> DeriveExternal(uint32_t addrIndex); + std::optional> DeriveInternal(uint32_t addrIndex); }; } From d09a0c44f33688b113415a2a9611c03d4bbf2278 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 29 Oct 2021 12:49:26 -0600 Subject: [PATCH 096/514] Clean up format of recovery information in the wallet dump. --- qa/rpc-tests/wallet_import_export.py | 9 ++++----- src/wallet/rpcdump.cpp | 10 ++++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/qa/rpc-tests/wallet_import_export.py b/qa/rpc-tests/wallet_import_export.py index f0c01bf6e76..b28c8f1a4ce 100755 --- a/qa/rpc-tests/wallet_import_export.py +++ b/qa/rpc-tests/wallet_import_export.py @@ -78,11 +78,10 @@ def run_test(self): def parse_wallet_file(dump_path): file_lines = open(dump_path, "r", encoding="utf8").readlines() # We expect information about the HDSeed and fingerpring in the header - assert_true("Emergency Recovery Phrase" in file_lines[4], "Expected Emergency Recovery Phrase") - assert_true("language" in file_lines[5], "Expected mnemonic seed language") - assert_true("fingerprint" in file_lines[6], "Expected mnemonic seed fingerprint") - mnemonic = file_lines[4].split("=")[1].strip() - print(mnemonic) + assert_true("recovery_phrase" in file_lines[5], "Expected emergency recovery phrase") + assert_true("language" in file_lines[6], "Expected mnemonic seed language") + assert_true("fingerprint" in file_lines[7], "Expected mnemonic seed fingerprint") + mnemonic = file_lines[5].split("=")[1].strip() (t_keys, i) = parse_wallet_file_lines(file_lines, 0) (sprout_keys, i) = parse_wallet_file_lines(file_lines, i) (sapling_keys, i) = parse_wallet_file_lines(file_lines, i) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 177ae373f4f..153024bf5e9 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -614,7 +614,10 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys) if (hdSeed.has_value()) { auto mSeed = hdSeed.value(); file << strprintf( - "# Emergency Recovery Phrase=\"%s\" \n# language=%s \n# fingerprint=%s\n", + "# Emergency Recovery Information\n" + "# - recovery_phrase=\"%s\"\n" + "# - language=%s\n" + "# - fingerprint=%s\n", mSeed.GetMnemonic(), MnemonicSeed::LanguageName(mSeed.GetLanguage()), mSeed.Fingerprint().GetHex() @@ -624,7 +627,10 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys) std::optional legacySeed = pwalletMain->GetLegacyHDSeed(); if (legacySeed.has_value()) { auto rawSeed = legacySeed.value().RawSeed(); - file << strprintf("# Legacy HDSeed=%s fingerprint=%s\n", + file << strprintf( + "# Legacy HD Seed\n" + "# - seed=%s\n" + "# - fingerprint=%s\n", HexStr(rawSeed.begin(), rawSeed.end()), legacySeed.value().Fingerprint().GetHex() ); From 67557df165babd631f2d2fbdbd23666ffd8fdb58 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 29 Oct 2021 15:15:33 -0600 Subject: [PATCH 097/514] Use SecureString for mnemonic phrase. --- Cargo.lock | 1 + Cargo.toml | 1 + src/rust/src/zip339_ffi.rs | 5 ++++- src/serialize.h | 29 ++++++++++++++------------ src/utiltest.cpp | 2 +- src/wallet/gtest/test_wallet_zkeys.cpp | 4 ++-- src/wallet/rpcwallet.cpp | 2 +- src/wallet/wallet.cpp | 2 +- src/wallet/wallet.h | 2 +- src/zcash/address/zip32.cpp | 2 +- src/zcash/address/zip32.h | 19 +++++++---------- 11 files changed, 37 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfcf268f5b6..9814651bead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,6 +882,7 @@ dependencies = [ "zcash_note_encryption", "zcash_primitives", "zcash_proofs", + "zeroize", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c21d2701fed..b3157f60e95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ zcash_note_encryption = "0.0" zcash_primitives = "0.5" zcash_proofs = "0.5" ed25519-zebra = "2.2.0" +zeroize = "1.4.2" # Metrics hyper = { version = "=0.14.2", default-features = false, features = ["server", "tcp", "http1"] } diff --git a/src/rust/src/zip339_ffi.rs b/src/rust/src/zip339_ffi.rs index eeafa5bcd0f..32005c2dbef 100644 --- a/src/rust/src/zip339_ffi.rs +++ b/src/rust/src/zip339_ffi.rs @@ -4,6 +4,7 @@ use std::{ ffi::{CStr, CString}, ptr, slice, }; +use zeroize::Zeroize; use zcash_primitives::zip339; @@ -63,7 +64,9 @@ pub extern "C" fn zip339_free_phrase(phrase: *const c_char) { if !phrase.is_null() { unsafe { // It is correct to cast away const here; the memory is not actually immutable. - CString::from_raw(phrase as *mut c_char); + CString::from_raw(phrase as *mut c_char) + .into_bytes() + .zeroize(); } } } diff --git a/src/serialize.h b/src/serialize.h index 2c933bc0f39..c56c2d12e23 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -65,7 +65,7 @@ inline T* NCONST_PTR(const T* val) return const_cast(val); } -/** +/** * Get begin pointer of vector (non-const version). * @note These functions avoid the undefined case of indexing into an empty * vector, as well as that of indexing after the end of the vector. @@ -197,11 +197,11 @@ enum #define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action)) #define READWRITEMANY(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) -/** +/** * Implement two methods, "Serialize" and "Unserialize", for serializable objects. These are * actually wrappers over the "SerializationOp" template method, which implements the body of each * classes' serialization code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these - * wrapper methods to be added as members. + * wrapper methods to be added as members. */ #define ADD_SERIALIZE_METHODS \ template \ @@ -324,16 +324,16 @@ uint64_t ReadCompactSize(Stream& is) * sure the encoding is one-to-one, one is subtracted from all but the last digit. * Thus, the byte sequence a[] with length len, where all but the last byte * has bit 128 set, encodes the number: - * + * * (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) - * + * * Properties: * * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) * * Every integer has exactly one encoding * * Encoding does not depend on size of original integer type * * No redundancy: every (infinite) byte sequence corresponds to a list * of encoded integers. - * + * * 0: [0x00] 256: [0x81 0x00] * 1: [0x01] 16383: [0xFE 0x7F] * 127: [0x7F] 16384: [0xFF 0x00] @@ -394,7 +394,7 @@ I ReadVarInt(Stream& is) #define COMPACTSIZE(obj) REF(CCompactSize(REF(obj))) #define LIMITED_STRING(obj,n) REF(LimitedString< n >(REF(obj))) -/** +/** * Wrapper for serializing arrays and POD. */ class CFlatData @@ -510,8 +510,11 @@ CVarInt WrapVarInt(I& n) { return CVarInt(n); } /** * string */ -template void Serialize(Stream& os, const std::basic_string& str); -template void Unserialize(Stream& is, std::basic_string& str); +template +void Serialize(Stream& os, const std::basic_string& str); + +template +void Unserialize(Stream& is, std::basic_string& str); /** * prevector @@ -625,16 +628,16 @@ inline void Unserialize(Stream& is, T& a) /** * string */ -template -void Serialize(Stream& os, const std::basic_string& str) +template +void Serialize(Stream& os, const std::basic_string& str) { WriteCompactSize(os, str.size()); if (!str.empty()) os.write((char*)&str[0], str.size() * sizeof(str[0])); } -template -void Unserialize(Stream& is, std::basic_string& str) +template +void Unserialize(Stream& is, std::basic_string& str) { unsigned int nSize = ReadCompactSize(is); str.resize(nSize); diff --git a/src/utiltest.cpp b/src/utiltest.cpp index be0e5ecf2a6..afd60ae35bf 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -316,7 +316,7 @@ void RegtestDeactivateNU5() { } libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() { - std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + SecureString mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); return libzcash::SaplingExtendedSpendingKey::Master(seed); } diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 4a9a5b06ab8..c7242632d0b 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -32,7 +32,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); // Load the all-zeroes seed - std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + SecureString mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); // The legacy seed used to be automatically derived from randomness; since // non-mnemonic random generation has been removed we just use the @@ -433,7 +433,7 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_FALSE(wallet.HaveMnemonicSeed()); // Load the all-zeroes seed as the legacy seed - std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + SecureString mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); MnemonicSeed seed(English, mnemonic); wallet.LoadMnemonicSeed(seed); ASSERT_TRUE(wallet.HaveMnemonicSeed()); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9b8f8692d8e..b3eb44df0fa 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1763,7 +1763,7 @@ UniValue walletconfirmbackup(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - auto strMnemonicPhrase = params[0].get_str(); + SecureString strMnemonicPhrase(params[0].get_str()); boost::erase_all(strMnemonicPhrase, "\""); boost::trim(strMnemonicPhrase); if (strMnemonicPhrase.length() > 0) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 80aa1e416ad..5dabde9fe57 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2355,7 +2355,7 @@ bool CWallet::SetCryptedMnemonicSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); /* Checks the wallet's seed against the specified mnemonic, and marks the * wallet's seed as having been backed up if the phrases match. */ - bool VerifyMnemonicSeed(std::string mnemonic); + bool VerifyMnemonicSeed(const SecureString& mnemonic); bool MnemonicVerified(); /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index dd1b4b62cb0..78d25679ea1 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -28,7 +28,7 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz std::vector entropy(entropyLen, 0); GetRandBytes(entropy.data(), entropyLen); const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); - std::string mnemonic(phrase); + SecureString mnemonic(phrase); zip339_free_phrase(phrase); MnemonicSeed seed(language, mnemonic); diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 4d9e61413cb..ce15a0b313e 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -66,21 +66,18 @@ class HDSeed { class MnemonicSeed: public HDSeed { private: Language language; - std::string mnemonic; + SecureString mnemonic; MnemonicSeed() {} - void Init() { - unsigned char buf[64]; - zip339_phrase_to_seed(language, mnemonic.c_str(), &buf); - seed.assign(buf, std::end(buf)); + void SetSeedFromMnemonic() { + seed.resize(64); + zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); } public: - MnemonicSeed(Language languageIn, std::string& mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { - unsigned char buf[64]; - zip339_phrase_to_seed(languageIn, mnemonicIn.c_str(), &buf); - seed.assign(buf, std::end(buf)); + MnemonicSeed(Language languageIn, SecureString mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { + SetSeedFromMnemonic(); } /** @@ -127,7 +124,7 @@ class MnemonicSeed: public HDSeed { READWRITE(language0); READWRITE(mnemonic); language = (Language) language0; - Init(); + SetSeedFromMnemonic(); } else { uint32_t language0 = (uint32_t) language; @@ -147,7 +144,7 @@ class MnemonicSeed: public HDSeed { return language; } - const std::string GetMnemonic() const { + const SecureString& GetMnemonic() const { return mnemonic; } From 0951fe22d8301c342440e52eb030d239bdb74986 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 29 Oct 2021 16:16:15 -0600 Subject: [PATCH 098/514] Apply suggestions from code review Co-authored-by: str4d --- src/gtest/test_zip32.cpp | 3 --- src/keystore.h | 3 +++ src/zcash/address/zip32.h | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index a6fce09a9fc..3f06b341a6f 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -11,9 +11,6 @@ TEST(ZIP32, TestVectors) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; HDSeed seed(rawSeed); - // TODO: Regenerate test vectors using a BIP-44-derived seed. - // std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); - // HDSeed seed(English, mnemonic); auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); EXPECT_EQ(m.depth, 0); diff --git a/src/keystore.h b/src/keystore.h index 7bad4694362..78bf5c400e0 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -124,6 +124,9 @@ typedef std::map mnemonicSeed; std::optional legacySeed; KeyMap mapKeys; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index ce15a0b313e..bcbb1a03c90 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -83,7 +83,7 @@ class MnemonicSeed: public HDSeed { /** * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible * to check that the generated seed can produce valid transparent and unified addresses at account - * numbers 0x7FFFFFFF and 0x0 respectively. + * numbers 0x7FFFFFFF and 0x00 respectively. */ static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); @@ -173,14 +173,14 @@ class diversifier_index_t : public base_blob<88> { diversifier_index_t() {} diversifier_index_t(const base_blob<88>& b) : base_blob<88>(b) {} diversifier_index_t(uint64_t i): base_blob<88>() { - data[0] = (uint8_t) i; - data[1] = (uint8_t) (i >> 8); - data[2] = (uint8_t) (i >> 16); - data[3] = (uint8_t) (i >> 24); - data[4] = (uint8_t) (i >> 32); - data[5] = (uint8_t) (i >> 40); - data[6] = (uint8_t) (i >> 48); - data[7] = (uint8_t) (i >> 56); + data[0] = i & 0xFF; + data[1] = (i >> 8) & 0xFF; + data[2] = (i >> 16) & 0xFF; + data[3] = (i >> 24) & 0xFF; + data[4] = (i >> 32) & 0xFF; + data[5] = (i >> 40) & 0xFF; + data[6] = (i >> 48) & 0xFF; + data[7] = (i >> 56) & 0xFF; } explicit diversifier_index_t(const std::vector& vch) : base_blob<88>(vch) {} @@ -229,7 +229,7 @@ struct SaplingExtendedFullViewingKey { std::optional Derive(uint32_t i) const; - // Attempt to construct a valid payment address with diversifier index + // Attempts to construct a valid payment address with diversifier index // `j`; returns std::nullopt if `j` does not result in a valid diversifier // given this xfvk. std::optional Address(diversifier_index_t j) const; From e4e77eb389d1cedfb58b0540975a89e894f67059 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 29 Oct 2021 16:33:19 -0600 Subject: [PATCH 099/514] Fix diversifier_index_t less than operator. --- src/gtest/test_zip32.cpp | 11 +++++++++++ src/zcash/address/zip32.cpp | 2 +- src/zcash/address/zip32.h | 9 +++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index 3f06b341a6f..081a51c427c 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -159,3 +159,14 @@ TEST(ZIP32, diversifier_index_t_increment) EXPECT_EQ(d_zero, d_one); } +TEST(ZIP32, diversifier_index_t_lt) +{ + EXPECT_TRUE(libzcash::diversifier_index_t(0) < libzcash::diversifier_index_t(1)); + EXPECT_FALSE(libzcash::diversifier_index_t(1) < libzcash::diversifier_index_t(0)); + EXPECT_FALSE(libzcash::diversifier_index_t(0) < libzcash::diversifier_index_t(0)); + EXPECT_TRUE(libzcash::diversifier_index_t(0xfffffffe) < libzcash::diversifier_index_t(0xffffffff)); + EXPECT_FALSE(libzcash::diversifier_index_t(0xffffffff) < libzcash::diversifier_index_t(0xfffffffe)); + EXPECT_TRUE(libzcash::diversifier_index_t(0x01) < libzcash::diversifier_index_t(0xffffffff)); + EXPECT_FALSE(libzcash::diversifier_index_t(0xffffffff) < libzcash::diversifier_index_t(0x01)); +} + diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 78d25679ea1..19686b4f7c3 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -334,7 +334,7 @@ std::optional UnifiedFullViewingKey::Address(diversifier_i if (transparentKey.has_value()) { // ensure that the diversifier index is small enough for a t-addr - if (MAX_TRANSPARENT_CHILD_IDX.less_than_le(j)) return std::nullopt; + if (MAX_TRANSPARENT_CHILD_IDX < j) return std::nullopt; CExtPubKey changeKey; if (!transparentKey.value().Derive(changeKey, 0)) { diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index bcbb1a03c90..43ce1d0c505 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -195,11 +195,12 @@ class diversifier_index_t : public base_blob<88> { return false; //overflow } - // treat as little-endian for numeric comparison - bool less_than_le(const diversifier_index_t& other) const { + friend bool operator<(const diversifier_index_t& a, const diversifier_index_t& b) { for (int i = 10; i >= 0; i--) { - if (data[i] < other.data[i]) { - return true; + if (a.data[i] == b.data[i]) { + continue; + } else { + return a.data[i] < b.data[i]; } } From b54d4cac7dba4fb9516392753e7763700ac02fa3 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 1 Nov 2021 10:13:19 -0600 Subject: [PATCH 100/514] Restore FindAddress comment --- src/zcash/address/zip32.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 43ce1d0c505..6b97ccb6d68 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -235,6 +235,9 @@ struct SaplingExtendedFullViewingKey { // given this xfvk. std::optional Address(diversifier_index_t j) const; + // Returns the first index starting from j that generates a valid + // payment address, along with the corresponding address. Throws + // a runtime error if the diversifier space is exhausted. std::pair FindAddress(diversifier_index_t j) const { auto addr = Address(j); while (!addr.has_value()) { From cd01b2d9bf591a3c863de3bb1346f7735df914d7 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 1 Nov 2021 20:55:31 -0600 Subject: [PATCH 101/514] Fix transparent BIP-44 keypaths. --- src/wallet/wallet.cpp | 2 +- src/zcash/address/zip32.cpp | 32 ++++++++++++++++---------------- src/zcash/address/zip32.h | 11 +++-------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5dabde9fe57..539ae3009df 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -274,7 +274,7 @@ CPubKey CWallet::GenerateNewKey() // All mnemonic seeds are checked at construction to ensure that we can obtain // a valid spending key for the account ZCASH_LEGACY_ACCOUNT; // therefore, the `value()` call here is safe. - BIP32AccountChains accountChains = BIP32AccountChains::ForAccount( + Bip44AccountChains accountChains = Bip44AccountChains::ForAccount( seed, BIP44CoinType(), ZCASH_LEGACY_ACCOUNT).value(); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 19686b4f7c3..b0030f45214 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -39,7 +39,7 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz // for a valid diversifier; unlike in the unified spending key case, diversifier // indices don't need to line up with anything. if (libzcash::UnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && - libzcash::BIP32AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { + libzcash::Bip44AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { return seed; } } @@ -76,33 +76,33 @@ namespace libzcash { // Transparent // -std::optional> DeriveZip32TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { auto rawSeed = seed.RawSeed(); auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); - // We use a fixed keypath scheme of m/32'/coin_type'/account' - // Derive m/32' - auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + // We use a fixed keypath scheme of m/44'/coin_type'/account' + // Derive m/44' + auto m_32h = m.Derive(44 | ZIP32_HARDENED_KEY_LIMIT); if (!m_32h.has_value()) return std::nullopt; - // Derive m/32'/coin_type' + // Derive m/44'/coin_type' auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); if (!m_32h_cth.has_value()) return std::nullopt; - // Derive m/32'/coin_type'/account_id' + // Derive m/44'/coin_type'/account_id' auto result = m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); if (!result.has_value()) return std::nullopt; - auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; return std::make_pair(result.value(), hdKeypath); } -std::optional BIP32AccountChains::ForAccount( +std::optional Bip44AccountChains::ForAccount( const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { - auto accountKeyOpt = DeriveZip32TransparentAccountKey(seed, bip44CoinType, accountId); + auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); if (!accountKeyOpt.has_value()) return std::nullopt; auto accountKey = accountKeyOpt.value(); @@ -111,14 +111,14 @@ std::optional BIP32AccountChains::ForAccount( if (!(external.has_value() && internal.has_value())) return std::nullopt; - return BIP32AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); + return Bip44AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); } -std::optional> BIP32AccountChains::DeriveExternal(uint32_t addrIndex) { +std::optional> Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { auto childKey = external.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; - auto hdKeypath = "m/32'/" + auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'/" + "0/" @@ -127,11 +127,11 @@ std::optional> BIP32AccountChains::DeriveExternal( return std::make_pair(childKey.value(), hdKeypath); } -std::optional> BIP32AccountChains::DeriveInternal(uint32_t addrIndex) { +std::optional> Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { auto childKey = internal.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; - auto hdKeypath = "m/32'/" + auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'/" + "1/" @@ -305,7 +305,7 @@ std::optional> UnifiedSpendingKey::ForA UnifiedSpendingKey usk; usk.accountId = accountId; - auto transparentKey = DeriveZip32TransparentAccountKey(seed, bip44CoinType, accountId); + auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); if (!transparentKey.has_value()) return std::nullopt; usk.transparentKey = transparentKey.value().first; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 6b97ccb6d68..16394a6eb85 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -384,12 +384,7 @@ class UnifiedSpendingKey { std::optional ParseZip32KeypathAccount(const std::string& keyPath); -std::optional> DeriveZip32TransparentMasterKey( - const HDSeed& seed, - uint32_t bip44CoinType, - uint32_t accountId); - -class BIP32AccountChains { +class Bip44AccountChains { private: uint256 seedFp; uint32_t accountId; @@ -397,10 +392,10 @@ class BIP32AccountChains { CExtKey external; CExtKey internal; - BIP32AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, uint32_t accountIdIn, CExtKey externalIn, CExtKey internalIn): + Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, uint32_t accountIdIn, CExtKey externalIn, CExtKey internalIn): seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} public: - static std::optional ForAccount( + static std::optional ForAccount( const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); From 87f7e5fbba54408a8271a724c0653376a9a0634b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 1 Nov 2021 21:12:41 -0600 Subject: [PATCH 102/514] Fix naming of unified spending & full viewing keys --- src/wallet/wallet.cpp | 6 +++--- src/wallet/wallet.h | 4 ++-- src/zcash/address/zip32.cpp | 12 ++++++------ src/zcash/address/zip32.h | 20 ++++++++++---------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 539ae3009df..ff0d8132464 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -406,7 +406,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi return false; } -UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { +ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); CHDChain& hdChain = mnemonicHDChain.value(); @@ -426,13 +426,13 @@ UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { } } -std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) { +std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) { auto seed = GetMnemonicSeed(); if (!seed.has_value()) { throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet."); } - auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); + auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); if (usk.has_value()) { // TODO: Save the unified full viewing key & metadata to the wallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 5f8b9f50104..38c2e5c8337 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1096,8 +1096,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** * Unified keys & addresses */ - libzcash::UnifiedSpendingKey GenerateNewUnifiedSpendingKey(); - std::optional GenerateUnifiedSpendingKeyForAccount(uint32_t accountId); + libzcash::ZcashdUnifiedSpendingKey GenerateNewUnifiedSpendingKey(); + std::optional GenerateUnifiedSpendingKeyForAccount(uint32_t accountId); /** * Increment the next transaction order id diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index b0030f45214..e1b0480b6b4 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -38,7 +38,7 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz // account 0x7FFFFFFF because derivation via the legacy path can simply search // for a valid diversifier; unlike in the unified spending key case, diversifier // indices don't need to line up with anything. - if (libzcash::UnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && + if (libzcash::ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && libzcash::Bip44AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { return seed; } @@ -301,8 +301,8 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const // Unified // -std::optional> UnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { - UnifiedSpendingKey usk; +std::optional> ZcashdUnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { + ZcashdUnifiedSpendingKey usk; usk.accountId = accountId; auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); @@ -315,8 +315,8 @@ std::optional> UnifiedSpendingKey::ForA return std::make_pair(usk, saplingKey.second); } -UnifiedFullViewingKey UnifiedSpendingKey::ToFullViewingKey() const { - UnifiedFullViewingKey ufvk; +ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { + ZcashdUnifiedFullViewingKey ufvk; if (transparentKey.has_value()) { ufvk.transparentKey = transparentKey.value().Neuter(); @@ -329,7 +329,7 @@ UnifiedFullViewingKey UnifiedSpendingKey::ToFullViewingKey() const { return ufvk; } -std::optional UnifiedFullViewingKey::Address(diversifier_index_t j) const { +std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { ZcashdUnifiedAddress ua; if (transparentKey.has_value()) { diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 16394a6eb85..365c3654f53 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -306,8 +306,8 @@ struct SaplingExtendedSpendingKey { } }; -class UnifiedSpendingKey; -class UnifiedFullViewingKey; +class ZcashdUnifiedSpendingKey; +class ZcashdUnifiedFullViewingKey; class ZcashdUnifiedAddress { private: @@ -315,7 +315,7 @@ class ZcashdUnifiedAddress { std::optional transparentKey; //TODO: should this just be the public key hash? std::optional saplingAddress; - friend class UnifiedFullViewingKey; + friend class ZcashdUnifiedFullViewingKey; ZcashdUnifiedAddress() {} public: @@ -328,14 +328,14 @@ class ZcashdUnifiedAddress { } }; -class UnifiedFullViewingKey { +class ZcashdUnifiedFullViewingKey { private: std::optional transparentKey; std::optional saplingKey; - UnifiedFullViewingKey() {} + ZcashdUnifiedFullViewingKey() {} - friend class UnifiedSpendingKey; + friend class ZcashdUnifiedSpendingKey; public: const std::optional& GetTransparentKey() const { return transparentKey; @@ -358,15 +358,15 @@ class UnifiedFullViewingKey { } }; -class UnifiedSpendingKey { +class ZcashdUnifiedSpendingKey { private: uint32_t accountId; std::optional transparentKey; std::optional saplingKey; - UnifiedSpendingKey() {} + ZcashdUnifiedSpendingKey() {} public: - static std::optional> ForAccount( + static std::optional> ForAccount( const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); @@ -379,7 +379,7 @@ class UnifiedSpendingKey { return saplingKey; } - UnifiedFullViewingKey ToFullViewingKey() const; + ZcashdUnifiedFullViewingKey ToFullViewingKey() const; }; std::optional ParseZip32KeypathAccount(const std::string& keyPath); From 4080f4886898f8e7d7889fab68c09c141161b053 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 1 Nov 2021 21:18:14 -0600 Subject: [PATCH 103/514] Fix max transparent diversifier index. --- src/zcash/address/zip32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index e1b0480b6b4..c804c5aba97 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -19,7 +19,7 @@ const unsigned char ZCASH_HD_SEED_FP_PERSONAL[BLAKE2bPersonalBytes] = const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = {'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'}; -const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x40000000); +const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x7FFFFFFF); MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) { From 66e71c45b30bc5f93b68bc27aa871a1d215d761b Mon Sep 17 00:00:00 2001 From: Marshall Gaucher <36639405+mdr0id@users.noreply.github.com> Date: Tue, 2 Nov 2021 17:54:52 -0700 Subject: [PATCH 104/514] Update entrypoint.sh Add in config option for prometheusport and metricsallowip via entrypoint.sh mechanism --- contrib/docker/entrypoint.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/docker/entrypoint.sh b/contrib/docker/entrypoint.sh index 0dc336851fe..adcbbf8fca3 100755 --- a/contrib/docker/entrypoint.sh +++ b/contrib/docker/entrypoint.sh @@ -40,6 +40,8 @@ if [[ -n "${ZCASHD_RPCPORT}" ]];then ZCASHD_CMD+=" -rpcport=${ZCASHD_RPCPORT}";f if [[ -n "${ZCASHD_ALLOWIP}" ]];then ZCASHD_CMD+=" -rpcallowip=${ZCASHD_ALLOWIP}";fi if [[ -n "${ZCASHD_TXINDEX}" ]];then ZCASHD_CMD+=" -txindex";fi if [[ -n "${ZCASHD_INSIGHTEXPLORER}" ]];then ZCASHD_CMD+=" -insightexplorer";fi +if [[ -n "${ZCASHD_PROMETHEUSPORT}" ]];then ZCASHD_CMD+=" -prometheusport=${ZCASHD_PROMETHEUSPORT}";fi +if [[ -n "${ZCASHD_METRICSIP}" ]];then ZCASHD_CMD+=" -metricsallowip=${ZCASHD_METRICSIP}";fi if [[ -n "${ZCASHD_ZMQPORT}" && -n "${ZCASHD_ZMQBIND}" ]];then ZCASHD_CMD+=" -zmqpubhashblock=tcp://${ZCASHD_ZMQBIND}:${ZCASHD_ZMQPORT}" ZCASHD_CMD+=" -zmqpubhashtx=tcp://${ZCASHD_ZMQBIND}:${ZCASHD_ZMQPORT}" From 2005c91d4128be65a49673b0535558ead89e81ae Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 5 Nov 2021 16:20:58 +0000 Subject: [PATCH 105/514] cargo update --- Cargo.lock | 82 +++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2c60440176..fb93a4b32f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom 0.2.3", "once_cell", @@ -66,9 +66,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "atomic-shim" @@ -231,9 +231,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.7.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" [[package]] name = "byteorder" @@ -567,9 +567,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fpe" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf3e40fc9accc7218e082db8a75aeea244b8f5db73e591774ef93b4276365e6" +checksum = "cd910db5f9ca4dc3116f8c46367825807aa2b942f72565f16b4be0b208a00a9e" dependencies = [ "block-modes", "cipher", @@ -724,9 +724,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes", "http", @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] @@ -838,9 +838,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" [[package]] name = "libm" @@ -1025,9 +1025,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", @@ -1071,9 +1071,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -1127,7 +1127,7 @@ version = "0.0.0" source = "git+https://github.com/zcash/orchard.git?rev=2c8241f25b943aa05203eacf9905db117c69bd29#2c8241f25b943aa05203eacf9905db117c69bd29" dependencies = [ "aes", - "arrayvec 0.7.1", + "arrayvec 0.7.2", "bigint", "bitvec", "blake2b_simd", @@ -1271,9 +1271,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "proc-macro-hack" @@ -1283,9 +1283,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] @@ -1517,9 +1517,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] @@ -1561,9 +1561,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" dependencies = [ "proc-macro2", "quote", @@ -1572,9 +1572,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", @@ -1590,18 +1590,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -1644,9 +1644,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" dependencies = [ "autocfg", "libc", @@ -1658,9 +1658,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb" +checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" dependencies = [ "proc-macro2", "quote", @@ -1993,18 +1993,18 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970" +checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" +checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" dependencies = [ "proc-macro2", "quote", From ca7160ebab88bbf3b4e121f624af91e67067f6d5 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 5 Nov 2021 16:27:43 +0000 Subject: [PATCH 106/514] tracing-subscriber 0.3 --- Cargo.lock | 35 +++++++++++------------------------ Cargo.toml | 6 +++--- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb93a4b32f1..ce38c4237e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,19 +284,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time", - "winapi", -] - [[package]] name = "cipher" version = "0.3.0" @@ -913,9 +900,9 @@ dependencies = [ [[package]] name = "matchers" -version = "0.0.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata", ] @@ -1619,12 +1606,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "99beeb0daeac2bd1e86ac2c21caddecb244b39a093594da1a661ec2060c7aedd" dependencies = [ + "itoa", "libc", - "winapi", ] [[package]] @@ -1687,12 +1674,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a" +checksum = "94571df2eae3ed4353815ea5a90974a594a1792d8782ff2cbcc9392d1101f366" dependencies = [ - "chrono", "crossbeam-channel 0.5.1", + "time", "tracing-subscriber", ] @@ -1718,17 +1705,17 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.25" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +checksum = "80a4ddde70311d8da398062ecf6fc2c309337de6b0f77d6c27aff8d53f6fca52" dependencies = [ "ansi_term", - "chrono", "lazy_static", "matchers", "regex", "sharded-slab", "thread_local", + "time", "tracing", "tracing-core", ] diff --git a/Cargo.toml b/Cargo.toml index 70a5122cbeb..7ab5db03a12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ subtle = "2.2" rand_core = "0.6" tracing = "0.1" tracing-core = "0.1" -tracing-appender = "0.1" +tracing-appender = "0.2" zcash_address = "0.0" zcash_history = "0.2" zcash_note_encryption = "0.0" @@ -58,9 +58,9 @@ thiserror = "1" tokio = { version = "1.0", features = ["rt", "net", "time", "macros"] } [dependencies.tracing-subscriber] -version = "0.2.12" +version = "0.3" default-features = false -features = ["ansi", "chrono", "env-filter"] +features = ["ansi", "env-filter", "time"] [profile.release] lto = true From 1364db2deddc4f7be1b97476b7abb56eb709fd1a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 5 Nov 2021 16:28:41 +0000 Subject: [PATCH 107/514] Bump all postponed dependencies --- qa/zcash/postponed-updates.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index e7186c32db8..fdf78c71485 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -5,23 +5,23 @@ # # Ccache 4.0 requires adding CMake to the depends system. -native_ccache 4.0 2021-11-01 -native_ccache 4.1 2021-11-01 -native_ccache 4.2 2021-11-01 -native_ccache 4.2.1 2021-11-01 -native_ccache 4.3 2021-11-01 -native_ccache 4.4 2021-11-01 -native_ccache 4.4.1 2021-11-01 -native_ccache 4.4.2 2021-11-01 +native_ccache 4.0 2022-02-01 +native_ccache 4.1 2022-02-01 +native_ccache 4.2 2022-02-01 +native_ccache 4.2.1 2022-02-01 +native_ccache 4.3 2022-02-01 +native_ccache 4.4 2022-02-01 +native_ccache 4.4.1 2022-02-01 +native_ccache 4.4.2 2022-02-01 # Clang is currently pinned to LLVM 12 -native_clang 13.0.0 2021-11-01 -libcxx 13.0.0 2021-11-01 +native_clang 13.0.0 2022-02-01 +libcxx 13.0.0 2022-02-01 # Rust is currently pinned to 1.55.0 bdb 18.1.40 2022-02-01 # Google Test 1.10.0 requires adding CMake to the depends system. -googletest 1.10.0 2021-11-01 -googletest 1.11.0 2021-11-01 +googletest 1.10.0 2022-02-01 +googletest 1.11.0 2022-02-01 From 373206027fd883b07a3b0f42f63c39807ca5ba38 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 5 Nov 2021 16:39:12 +0000 Subject: [PATCH 108/514] depends: Update Rust to 1.56.1 --- Cargo.toml | 1 + depends/packages/native_rust.mk | 16 ++++++++-------- qa/zcash/postponed-updates.txt | 2 +- rust-toolchain | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ab5db03a12..31a0df3d618 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ repository = "https://github.com/zcash/zcash" readme = "README.md" license = "MIT OR Apache-2.0" edition = "2018" +rust-version = "1.56" [lib] name = "rustzcash" diff --git a/depends/packages/native_rust.mk b/depends/packages/native_rust.mk index a48d683b455..045df44444c 100644 --- a/depends/packages/native_rust.mk +++ b/depends/packages/native_rust.mk @@ -1,14 +1,14 @@ package=native_rust -$(package)_version=1.55.0 +$(package)_version=1.56.1 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=2080253a2ec36ac8ed6e060d30802d888533124b8d16545cfd4af898b365eaac +$(package)_sha256_hash_linux=a6be5d045183a0b12dddf0d81633e2a64e63e4c2dfa44eb7593970c1ef93a98f $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=2e345ac7724c192c9487a2c6bd4f6c52c884d791981510288830d27d9a0bf2f3 +$(package)_sha256_hash_darwin=8d65ef02a123c23be00101fb204d28b60498b9145dd2ee8edabf0afde6e01e55 $(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz -$(package)_sha256_hash_freebsd=7ddb8ec4d431f64dd6428df93d46f726516970b0ca83c71c3efbfe34a42d3113 +$(package)_sha256_hash_freebsd=94e2c8b44af125ca8ba1a1ded7e7b9c5acb27e52acec4ab483d5ed9a8528c5a9 $(package)_file_name_aarch64_linux=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_aarch64_linux=eebdb2e659ed14884a49f0457d44e5e8c9f89fca3414533752c6dbb96232c156 +$(package)_sha256_hash_aarch64_linux=69792887357c8dd78c5424f0b4a624578296796d99edf6c30ebe2acc2b939aa3 # Mapping from GCC canonical hosts to Rust targets # If a mapping is not present, we assume they are identical, unless $host_os is @@ -17,9 +17,9 @@ $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu $(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu # Mapping from Rust targets to SHA-256 hashes -$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=e30063a259e32cd0e31baadcee82112ef840e0f654d5128dd79fc715ede92058 -$(package)_rust_std_sha256_hash_x86_64-apple-darwin=8888fb0a1cbc645f86e1551d27cc127697361fecab9cd414691e434976412733 -$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=8dfab5489b485417d76a7d266fc795608ba61f9c423f8a71616e01f33e146487 +$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=d577c25879cf160ec1a04d5101971dd684f9b4f87b3cb463a7521b676dc3df89 +$(package)_rust_std_sha256_hash_x86_64-apple-darwin=a1cedfaea1508bf3bfc8a77d82d15c693b41e70e56fad930d24f21f0bce5052a +$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=8c5d425d2882a93827850672a70bfc2e643cae425aaffa9dafa6808fcd4bc798 define rust_target $(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2)))) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index fdf78c71485..403c2409ca4 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -18,7 +18,7 @@ native_ccache 4.4.2 2022-02-01 native_clang 13.0.0 2022-02-01 libcxx 13.0.0 2022-02-01 -# Rust is currently pinned to 1.55.0 +# Rust is currently pinned to 1.56.1 bdb 18.1.40 2022-02-01 diff --git a/rust-toolchain b/rust-toolchain index 094d6ad00ce..43c989b5531 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.55.0 +1.56.1 From 5eca8d34bab7afe12b6b05acf93bb2dbf12f5c20 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 6 Nov 2021 21:44:24 +0000 Subject: [PATCH 109/514] depends: Update Clang / libcxx to LLVM 13 --- depends/packages/libcxx.mk | 16 ++++++++-------- depends/packages/native_clang.mk | 13 ++++++------- qa/zcash/postponed-updates.txt | 4 +--- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/depends/packages/libcxx.mk b/depends/packages/libcxx.mk index 0acb979298f..c40317a4d9f 100644 --- a/depends/packages/libcxx.mk +++ b/depends/packages/libcxx.mk @@ -8,10 +8,10 @@ ifneq ($(host_os),mingw32) $(package)_download_path=$(native_clang_download_path) $(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz $(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz -$(package)_sha256_hash_aarch64_linux=3d4ad804b7c85007686548cbc917ab067bf17eaedeab43d9eb83d3a683d8e9d4 +$(package)_sha256_hash_aarch64_linux=968d65d2593850ee9b37fcda074fb7641529bd45d2f976af6c8197de3c22612f $(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz $(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz -$(package)_sha256_hash_linux=6b3cc55d3ef413be79785c4dc02828ab3bd6b887872b143e3091692fc6acefe7 +$(package)_sha256_hash_linux=76d0bf002ede7a893f69d9ad2c4e101d15a8f4186fbfe24e74856c8449acd7c1 define $(package)_stage_cmds mkdir -p $($(package)_staging_prefix_dir)/lib && \ @@ -22,13 +22,13 @@ endef else # For Windows cross-compilation, use the MSYS2 binaries. $(package)_download_path=https://repo.msys2.org/mingw/x86_64 -$(package)_download_file=mingw-w64-x86_64-libc++-12.0.1-1-any.pkg.tar.zst -$(package)_file_name=mingw-w64-x86_64-libcxx-12.0.1-1-any.pkg.tar.zst -$(package)_sha256_hash=847d86435c35f12b4bc779c91c800a86499ba5c9d419d6ed5e2386c7582c2e81 +$(package)_download_file=mingw-w64-x86_64-libc++-13.0.0-3-any.pkg.tar.zst +$(package)_file_name=mingw-w64-x86_64-libcxx-13.0.0-3-any.pkg.tar.zst +$(package)_sha256_hash=0f8819e88273579f7c9262456c6b8f4d73e1693095c2364d1192c61c5f6a1a4f -$(package)_libcxxabi_download_file=mingw-w64-x86_64-libc++abi-12.0.1-1-any.pkg.tar.zst -$(package)_libcxxabi_file_name=mingw-w64-x86_64-libcxxabi-12.0.1-1-any.pkg.tar.zst -$(package)_libcxxabi_sha256_hash=a60294cb611916f4b9db27b7a6f3d6dc0f75d9006ad6a5bb830427df9b5bc9d2 +$(package)_libcxxabi_download_file=mingw-w64-x86_64-libc++abi-13.0.0-3-any.pkg.tar.zst +$(package)_libcxxabi_file_name=mingw-w64-x86_64-libcxxabi-13.0.0-3-any.pkg.tar.zst +$(package)_libcxxabi_sha256_hash=7224a7252a566938afe91ea8f130682abd29b10e13c9a3c2347af523ca0d7c42 $(package)_extra_sources += $($(package)_libcxxabi_file_name) diff --git a/depends/packages/native_clang.mk b/depends/packages/native_clang.mk index b3cc5b65a84..ac42cd3e1cd 100644 --- a/depends/packages/native_clang.mk +++ b/depends/packages/native_clang.mk @@ -1,23 +1,22 @@ package=native_clang -$(package)_major_version=12 -$(package)_version=12.0.1 +$(package)_major_version=13 +$(package)_version=13.0.0 $(package)_download_path=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) $(package)_download_path_linux=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) $(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz $(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz -$(package)_sha256_hash_linux=6b3cc55d3ef413be79785c4dc02828ab3bd6b887872b143e3091692fc6acefe7 +$(package)_sha256_hash_linux=76d0bf002ede7a893f69d9ad2c4e101d15a8f4186fbfe24e74856c8449acd7c1 $(package)_download_path_darwin=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_major_version).0.0 $(package)_download_file_darwin=clang+llvm-$($(package)_major_version).0.0-x86_64-apple-darwin.tar.xz $(package)_file_name_darwin=clang-llvm-$($(package)_major_version).0.0-x86_64-apple-darwin.tar.xz -$(package)_sha256_hash_darwin=7bc2259bf75c003f644882460fc8e844ddb23b27236fe43a2787870a4cd8ab50 +$(package)_sha256_hash_darwin=d051234eca1db1f5e4bc08c64937c879c7098900f7a0370f3ceb7544816a8b09 $(package)_download_path_freebsd=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) $(package)_download_file_freebsd=clang+llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz $(package)_file_name_freebsd=clang-llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz -$(package)_sha256_hash_freebsd=38857da36489880b0504ae7142b74abe41cf18711a6bb25ca96792d8190e8b0e - +$(package)_sha256_hash_freebsd=e579747a36ff78aa0a5533fe43bc1ed1f8ed449c9bfec43c358d953ffbbdcf76 $(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz $(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz -$(package)_sha256_hash_aarch64_linux=3d4ad804b7c85007686548cbc917ab067bf17eaedeab43d9eb83d3a683d8e9d4 +$(package)_sha256_hash_aarch64_linux=968d65d2593850ee9b37fcda074fb7641529bd45d2f976af6c8197de3c22612f # Ensure we have clang native to the builder, not the target host ifneq ($(canonical_host),$(build)) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index 403c2409ca4..98d1657f716 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -14,9 +14,7 @@ native_ccache 4.4 2022-02-01 native_ccache 4.4.1 2022-02-01 native_ccache 4.4.2 2022-02-01 -# Clang is currently pinned to LLVM 12 -native_clang 13.0.0 2022-02-01 -libcxx 13.0.0 2022-02-01 +# Clang is currently pinned to LLVM 13 # Rust is currently pinned to 1.56.1 From d84845d199d0943361c212eb84be9452c35b1785 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 11:36:57 -0600 Subject: [PATCH 110/514] Clean up handling of mnemonic seed metadata. --- src/wallet/gtest/test_wallet_zkeys.cpp | 16 +++++----------- src/wallet/wallet.cpp | 17 ++++++++++------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index c7242632d0b..538f6ebcdc9 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -31,13 +31,9 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // No HD seed in the wallet EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey()); - // Load the all-zeroes seed - SecureString mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); - MnemonicSeed seed(English, mnemonic); - // The legacy seed used to be automatically derived from randomness; since - // non-mnemonic random generation has been removed we just use the - // all-zeros mnemonic for these tests. - wallet.LoadMnemonicSeed(seed); + // Add a random seed to the wallet. + wallet.GenerateNewSeed(); + auto seed = wallet.GetMnemonicSeed().value(); // Now this call succeeds auto address = wallet.GenerateNewLegacySaplingZKey(); @@ -432,10 +428,8 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { ASSERT_FALSE(wallet.HaveMnemonicSeed()); - // Load the all-zeroes seed as the legacy seed - SecureString mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); - MnemonicSeed seed(English, mnemonic); - wallet.LoadMnemonicSeed(seed); + // Add a mnemonic seed to the wallet. + wallet.GenerateNewSeed(); ASSERT_TRUE(wallet.HaveMnemonicSeed()); // wallet should be empty diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ff0d8132464..5dc58e87e4f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -121,7 +121,8 @@ SaplingPaymentAddress CWallet::GenerateNewLegacySaplingZKey() { AssertLockHeld(cs_wallet); if (!mnemonicHDChain.has_value()) { - mnemonicHDChain = CHDChain(GetMnemonicSeed().value().Fingerprint(), GetTime()); + throw std::runtime_error( + "CWallet::GenerateNewLegacySaplingZKey(): Wallet is missing mnemonic seed metadata."); } CHDChain& hdChain = mnemonicHDChain.value(); @@ -140,7 +141,7 @@ SaplingPaymentAddress CWallet::GenerateNewLegacySaplingZKey() { // Update the persisted chain information if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { throw std::runtime_error( - "CWallet::GenerateLegacySaplingZKey(): Writing HD chain model failed"); + "CWallet::GenerateNewLegacySaplingZKey(): Writing HD chain model failed"); } return xfvk.DefaultAddress(); @@ -267,7 +268,8 @@ CPubKey CWallet::GenerateNewKey() auto seed = seedOpt.value(); if (!mnemonicHDChain.has_value()) { - mnemonicHDChain = CHDChain(seed.Fingerprint(), GetTime()); + throw std::runtime_error( + "CWallet::GenerateNewKey(): Wallet is missing mnemonic seed metadata."); } CHDChain& hdChain = mnemonicHDChain.value(); @@ -409,6 +411,10 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); + if (!mnemonicHDChain.has_value()) { + throw std::runtime_error( + "CWallet::GenerateNewUnifiedSpendingKey(): Wallet is missing mnemonic seed metadata."); + } CHDChain& hdChain = mnemonicHDChain.value(); while (true) { auto usk = GenerateUnifiedSpendingKeyForAccount(hdChain.GetAccountCounter()); @@ -2359,10 +2365,7 @@ bool CWallet::VerifyMnemonicSeed(const SecureString& mnemonic) { LOCK(cs_wallet); auto seed = GetMnemonicSeed(); - if (seed.has_value() && seed.value().GetMnemonic() == mnemonic) { - if (!mnemonicHDChain.has_value()) { - mnemonicHDChain = CHDChain(seed.value().Fingerprint(), GetTime()); - } + if (seed.has_value() && mnemonicHDChain.has_value() && seed.value().GetMnemonic() == mnemonic) { CHDChain& hdChain = mnemonicHDChain.value(); hdChain.SetMnemonicSeedBackupConfirmed(); // Update the persisted chain information From 6fdded20bc5dc6265fe0239ac773b0f6ffc37800 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 12:03:37 -0600 Subject: [PATCH 111/514] Restore legacy HDSeed encryption tests. --- src/gtest/test_keystore.cpp | 63 ++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 8c8dff1d30b..e3473806f54 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -312,7 +312,7 @@ class TestCCryptoKeyStore : public CCryptoKeyStore bool Unlock(const CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::Unlock(vMasterKeyIn); } }; -TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { +TEST(KeystoreTests, StoreAndRetrieveMnemonicSeedInEncryptedStore) { TestCCryptoKeyStore keyStore; CKeyingMaterial vMasterKey(32, 0); GetRandBytes(vMasterKey.data(), 32); @@ -378,6 +378,67 @@ TEST(KeystoreTests, StoreAndRetrieveHDSeedInEncryptedStore) { EXPECT_EQ(seed3, seedOut.value()); } +TEST(KeystoreTests, StoreAndRetrieveLegacyHDSeedInEncryptedStore) { + TestCCryptoKeyStore keyStore; + CKeyingMaterial vMasterKey(32, 0); + GetRandBytes(vMasterKey.data(), 32); + + // 1) Test adding a seed to an unencrypted key store, then encrypting it + // We use a mnemonic seed, then disregard the mnemonic itself. + auto seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + auto seedOut = keyStore.GetLegacyHDSeed(); + EXPECT_FALSE(seedOut.has_value()); + + ASSERT_TRUE(keyStore.SetLegacyHDSeed(seed)); + seedOut = keyStore.GetLegacyHDSeed(); + ASSERT_TRUE(seedOut.has_value()); + + ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey)); + seedOut = keyStore.GetLegacyHDSeed(); + EXPECT_FALSE(seedOut.has_value()); + + // Unlocking with a random key should fail + CKeyingMaterial vRandomKey(32, 0); + GetRandBytes(vRandomKey.data(), 32); + EXPECT_FALSE(keyStore.Unlock(vRandomKey)); + + // Unlocking with a slightly-modified vMasterKey should fail + CKeyingMaterial vModifiedKey(vMasterKey); + vModifiedKey[0] += 1; + EXPECT_FALSE(keyStore.Unlock(vModifiedKey)); + + // Unlocking with vMasterKey should succeed + ASSERT_TRUE(keyStore.Unlock(vMasterKey)); + seedOut = keyStore.GetLegacyHDSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); + + // 2) Test replacing the seed in an already-encrypted key store fails + auto seed2 = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + EXPECT_FALSE(keyStore.SetLegacyHDSeed(seed2)); + seedOut = keyStore.GetLegacyHDSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed, seedOut.value()); + + // 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)); + + seedOut = keyStore2.GetLegacyHDSeed(); + EXPECT_FALSE(seedOut.has_value()); + + auto seed3 = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + ASSERT_TRUE(keyStore2.SetLegacyHDSeed(seed3)); + seedOut = keyStore2.GetLegacyHDSeed(); + ASSERT_TRUE(seedOut.has_value()); + EXPECT_EQ(seed3, seedOut.value()); +} + TEST(KeystoreTests, StoreAndRetrieveSpendingKeyInEncryptedStore) { TestCCryptoKeyStore keyStore; uint256 r {GetRandHash()}; From 551072165a47d91b8ee6abefb27ed2f1991bdc68 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 12:56:04 -0600 Subject: [PATCH 112/514] Style fix in BIP 32 path account parsing test. --- src/gtest/test_zip32.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index 081a51c427c..062667e4ece 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -134,21 +134,23 @@ TEST(ZIP32, TestVectors) { } TEST(ZIP32, ParseZip32KeypathAccount) { + auto expect_account = [](std::string sAccount, long expected) { + auto result = libzcash::ParseZip32KeypathAccount(sAccount); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(result.value(), expected); + }; + std::string sAccount = "m/32'/1234'/5'"; - EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); - EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 5); + expect_account(sAccount, 5); sAccount = "m/32'/1234'/50'"; - EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); - EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 50); + expect_account(sAccount, 50); sAccount = "m/32'/1234'/5'/0"; - EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); - EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 5); + expect_account(sAccount, 5); sAccount = "m/32'/133'/2147483646'/1"; - EXPECT_TRUE(libzcash::ParseZip32KeypathAccount(sAccount).has_value()); - EXPECT_EQ(libzcash::ParseZip32KeypathAccount(sAccount).value(), 2147483646); + expect_account(sAccount, 2147483646); } TEST(ZIP32, diversifier_index_t_increment) From 399ed5b4d706ce5b058424e6653228bda18824b1 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 16:08:24 -0600 Subject: [PATCH 113/514] Do not strip quotes when verifying mnemonic seed. --- qa/rpc-tests/wallet_import_export.py | 4 ++-- src/wallet/rpcwallet.cpp | 17 +++++------------ test/lint/lint-includes.sh | 1 - 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/qa/rpc-tests/wallet_import_export.py b/qa/rpc-tests/wallet_import_export.py index b28c8f1a4ce..d4d481fd7d9 100755 --- a/qa/rpc-tests/wallet_import_export.py +++ b/qa/rpc-tests/wallet_import_export.py @@ -32,7 +32,7 @@ def run_test(self): errorString = e.error['message'] assert_equal("Error: Please acknowledge that you have backed up" in errorString, True) dump_path0 = self.nodes[0].z_exportwallet('walletdumpmnem') - (mnemonic, t_keys0, sprout_keys0, sapling_keys0) = parse_wallet_file(dump_path0) + (mnemonic, _, _, _) = parse_wallet_file(dump_path0) self.nodes[0].walletconfirmbackup(mnemonic) # Now that we've confirmed backup, we can generate addresses @@ -81,7 +81,7 @@ def parse_wallet_file(dump_path): assert_true("recovery_phrase" in file_lines[5], "Expected emergency recovery phrase") assert_true("language" in file_lines[6], "Expected mnemonic seed language") assert_true("fingerprint" in file_lines[7], "Expected mnemonic seed fingerprint") - mnemonic = file_lines[5].split("=")[1].strip() + mnemonic = file_lines[5].split("=")[1].replace("\"", "").strip() (t_keys, i) = parse_wallet_file_lines(file_lines, 0) (sprout_keys, i) = parse_wallet_file_lines(file_lines, i) (sapling_keys, i) = parse_wallet_file_lines(file_lines, i) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b3eb44df0fa..3c27e0eeb7c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -38,7 +38,6 @@ #include #include -#include #include #include @@ -1764,20 +1763,14 @@ UniValue walletconfirmbackup(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); SecureString strMnemonicPhrase(params[0].get_str()); - boost::erase_all(strMnemonicPhrase, "\""); boost::trim(strMnemonicPhrase); - if (strMnemonicPhrase.length() > 0) { - if (!pwalletMain->VerifyMnemonicSeed(strMnemonicPhrase)) - throw JSONRPCError( - RPC_WALLET_PASSPHRASE_INCORRECT, - "Error: The emergency recovery phrase entered was incorrect."); + if (pwalletMain->VerifyMnemonicSeed(strMnemonicPhrase)) { + return NullUniValue; } else { - throw runtime_error( - "walletconfirmbackup \"emergency recovery phrase\"\n" - "Notify the wallet that the user has backed up the emergency recovery phrase"); + throw JSONRPCError( + RPC_WALLET_PASSPHRASE_INCORRECT, + "Error: The emergency recovery phrase entered was incorrect."); } - - return NullUniValue; } diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 29489c70288..ece95d7a5d6 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -42,7 +42,6 @@ EXPECTED_BOOST_INCLUDES=( boost/algorithm/string.hpp boost/algorithm/string/case_conv.hpp boost/algorithm/string/classification.hpp - boost/algorithm/string/erase.hpp boost/algorithm/string/join.hpp boost/algorithm/string/predicate.hpp boost/algorithm/string/replace.hpp From a67fb00d0a46e0452515d7cb7a8e94194649c3de Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 16:36:42 -0600 Subject: [PATCH 114/514] Apply suggestions from code review Co-authored-by: str4d --- src/gtest/test_keystore.cpp | 1 + src/test/alert_tests.cpp | 2 +- src/wallet/asyncrpcoperation_saplingmigration.cpp | 5 ++--- src/wallet/crypter.cpp | 6 ++++-- src/wallet/gtest/test_wallet_zkeys.cpp | 9 +++++---- src/wallet/rpcdump.cpp | 4 ++-- src/wallet/rpcwallet.cpp | 3 +-- src/wallet/wallet.cpp | 13 +++++-------- src/wallet/wallet.h | 6 +++--- src/wallet/walletdb.cpp | 14 +++++++------- src/zcash/address/zip32.cpp | 11 +++++++---- src/zcash/address/zip32.h | 4 ++-- 12 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index e3473806f54..c9b230b5934 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -53,6 +53,7 @@ TEST(KeystoreTests, StoreAndRetrieveLegacyHDSeed) { EXPECT_FALSE(seedOut.has_value()); // Generate a random seed + // (We use MnemonicSeed purely to generate a seed, and then drop the mnemonic part.) HDSeed seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); // We should be able to set and retrieve the seed diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index fbfc7fa1611..ba4fae55e08 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -117,7 +117,7 @@ bool SignAlert(CAlert &alert) // sign alert std::vector vchTmp(ParseHex(pszPrivKey)); CPrivKey vchPrivKey(vchTmp.begin(), vchTmp.end()); - std::optional key = CKey::FromPrivKey(SetPrivKey(vchPrivKey, false); + std::optional key = CKey::FromPrivKey(vchPrivKey, false); if (!key.has_value()) { printf("key.SetPrivKey failed\n"); return false; diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index db6ae3a3d95..ee2cdcb9a5e 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -191,9 +191,8 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl return amount; } -// Unless otherwise specified, the migration destination address is the address -// the legacy Sapling account (0x7FFFFFFF) at the smallest diversifier index -// that produces a valid diversified address. +// Unless otherwise specified, the migration destination address is the +// default address for the key at m/32'/coin_type'/0x7FFFFFFF'/0' libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { KeyIO keyIO(Params()); if (mapArgs.count("-migrationdestaddress")) { diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 0439ec48052..5ef7cabf38a 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -372,7 +372,8 @@ bool CCryptoKeyStore::SetCryptedMnemonicSeed( { LOCK(cs_KeyStore); if (!fUseCrypto || !cryptedMnemonicSeed.first.IsNull()) { - // Require encryption & don't allow an existing seed to be changed. + // Either wallet encryption is disabled, or the wallet already has an + // existing mnemonic. In either case, don't allow a new mnemonic. return false; } else { cryptedMnemonicSeed = std::make_pair(seedFp, vchCryptedSecret); @@ -439,7 +440,8 @@ bool CCryptoKeyStore::SetCryptedLegacyHDSeed( { LOCK(cs_KeyStore); if (!fUseCrypto || cryptedLegacySeed.has_value()) { - // Require encryption & don't allow an existing seed to be changed. + // Either wallet encryption is disabled, or the wallet already has an + // existing legacy seed. In either case, don't allow a new seed. return false; } else { cryptedLegacySeed = std::make_pair(seedFp, vchCryptedSecret); diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 538f6ebcdc9..4e2bc5abb6d 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -65,9 +65,10 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { EXPECT_EQ(1, addrs.count(address)); EXPECT_EQ(1, addrs.count(sk.ToXFVK().DefaultAddress())); - // Generate a diversified address different to the default - // If we can't get an early diversified address, we are very unlucky - libzcash::diversifier_index_t j(2); + // Find a diversified address that does not use the same diversifier as the default address. + // By starting our search at `10` we ensure there's no more than a 2^-10 chance that we + // collide with the default diversifier. + libzcash::diversifier_index_t j(10); auto dpa = sk.ToXFVK().FindAddress(j).first; // verify wallet only has the default address @@ -448,7 +449,7 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) { // If we can't get an early diversified address, we are very unlucky libzcash::SaplingExtendedSpendingKey extsk; EXPECT_TRUE(wallet.GetSaplingExtendedSpendingKey(address, extsk)); - libzcash::diversifier_index_t j(2); + libzcash::diversifier_index_t j(10); auto dpa = extsk.ToXFVK().FindAddress(j).first; // Add diversified address to the wallet diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 153024bf5e9..4e302c95ab1 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -614,7 +614,7 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys) if (hdSeed.has_value()) { auto mSeed = hdSeed.value(); file << strprintf( - "# Emergency Recovery Information\n" + "# Emergency Recovery Information:\n" "# - recovery_phrase=\"%s\"\n" "# - language=%s\n" "# - fingerprint=%s\n", @@ -628,7 +628,7 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys) if (legacySeed.has_value()) { auto rawSeed = legacySeed.value().RawSeed(); file << strprintf( - "# Legacy HD Seed\n" + "# Legacy HD Seed:\n" "# - seed=%s\n" "# - fingerprint=%s\n", HexStr(rawSeed.begin(), rawSeed.end()), diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3c27e0eeb7c..090459fc2a7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -54,7 +54,6 @@ using namespace libzcash; const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; -const bool DEFAULT_WALLET_REQUIRE_BACKUP = true; extern UniValue TxJoinSplitToJSON(const CTransaction& tx); @@ -89,7 +88,7 @@ void EnsureWalletIsBackedUp(const CChainParams& params) throw JSONRPCError( RPC_WALLET_BACKUP_REQUIRED, "Error: Please acknowledge that you have backed up the wallet's emergency recovery phrase " - "with walletconfirmbackup first." + "by using the walletconfirmbackup RPC method first." ); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5dc58e87e4f..716b7206137 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -110,8 +110,8 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() return addr; } -// Generate a new Sapling spending key as a child of the legacy Sapling account -// return its public payment address. +// Generates a new Sapling spending key as a child of the legacy Sapling account, +// and returns its public payment address. // // The z_getnewaddress API must use the mnemonic HD seed, and fail if that seed // is not present. The account index is determined by trial of values of @@ -435,7 +435,7 @@ ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) { auto seed = GetMnemonicSeed(); if (!seed.has_value()) { - throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet."); + throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed."); } auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); @@ -4240,8 +4240,7 @@ bool CWallet::NewKeyPool() for (int i = 0; i < nKeys; i++) { int64_t nIndex = i+1; - auto key = GenerateNewKey(); - walletdb.WritePool(nIndex, CKeyPool(key)); + walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); @@ -4271,8 +4270,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) int64_t nEnd = 1; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; - auto newKey = GenerateNewKey(); - if (!walletdb.WritePool(nEnd, CKeyPool(newKey))) + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) throw runtime_error("TopUpKeyPool(): writing generated key failed"); setKeyPool.insert(nEnd); LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); @@ -4898,7 +4896,6 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches walletInstance->SetMaxVersion(nMaxVersion); } - // TODO: should this go in `LoadWallet` instead? if (!walletInstance->HaveMnemonicSeed()) { // We can't set the new HD seed until the wallet is decrypted. diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 38c2e5c8337..16dd7141b9d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1060,7 +1060,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface libzcash::SaplingPaymentAddress GenerateNewLegacySaplingZKey(); //! Generates Sapling key at the specified address index, and stores the newly generated //! spending key to the wallet if it has not alreay been persisted. - //! Returns the newly created key, its metadata, and a flag distinguishing + //! Returns the newly created key, and a flag distinguishing //! whether or not the key was already known by the wallet. std::pair GenerateLegacySaplingZKey(uint32_t addrIndex); //! Adds Sapling spending key to the store, and saves it to disk @@ -1308,12 +1308,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool VerifyMnemonicSeed(const SecureString& mnemonic); bool MnemonicVerified(); - /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ + /* Set the current mnemonic phrase, without saving it to disk (used by LoadWallet) */ bool LoadMnemonicSeed(const MnemonicSeed& seed); /* Set the legacy HD seed, without saving it to disk (used by LoadWallet) */ bool LoadLegacyHDSeed(const HDSeed& seed); - /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ + /* Set the current encrypted mnemonic phrase, without saving it to disk (used by LoadWallet) */ bool LoadCryptedMnemonicSeed(const uint256& seedFp, const std::vector& seed); /* Set the legacy encrypted HD seed, without saving it to disk (used by LoadWallet) */ bool LoadCryptedLegacyHDSeed(const uint256& seedFp, const std::vector& seed); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 417a5f8d8ca..dfe8ebf691a 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -685,7 +685,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> pwallet->nWitnessCacheSize; } - else if (strType == "mnemonicseed") + else if (strType == "mnemonicphrase") { uint256 seedFp; ssKey >> seedFp; @@ -693,17 +693,17 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, if (seed.Fingerprint() != seedFp) { - strErr = "Error reading wallet database: HDSeed corrupt"; + strErr = "Error reading wallet database: mnemonic phrase corrupt"; return false; } if (!pwallet->LoadMnemonicSeed(seed)) { - strErr = "Error reading wallet database: LoadHDSeed failed"; + strErr = "Error reading wallet database: LoadMnemonicSeed failed"; return false; } } - else if (strType == "chdmnemonicseed") + else if (strType == "cmnemonicphrase") { uint256 seedFp; vector vchCryptedSecret; @@ -774,7 +774,7 @@ static bool IsKeyType(string strType) { return (strType== "key" || strType == "wkey" || strType == "hdseed" || strType == "chdseed" || - strType == "mnemonicseed" || strType == "chdmnemonicseed" || + strType == "mnemonicphrase" || strType == "cmnemonicphrase" || strType == "zkey" || strType == "czkey" || strType == "sapzkey" || strType == "csapzkey" || strType == "vkey" || strType == "sapextfvk" || @@ -1194,13 +1194,13 @@ bool CWalletDB::WriteNetworkInfo(const std::string& networkId) bool CWalletDB::WriteMnemonicSeed(const MnemonicSeed& seed) { nWalletDBUpdateCounter++; - return Write(std::make_pair(std::string("mnemonicseed"), seed.Fingerprint()), seed); + return Write(std::make_pair(std::string("mnemonicphrase"), seed.Fingerprint()), seed); } bool CWalletDB::WriteCryptedMnemonicSeed(const uint256& seedFp, const std::vector& vchCryptedSecret) { nWalletDBUpdateCounter++; - return Write(std::make_pair(std::string("chdmnemonicseed"), seedFp), vchCryptedSecret); + return Write(std::make_pair(std::string("cmnemonicphrase"), seedFp), vchCryptedSecret); } bool CWalletDB::WriteMnemonicHDChain(const CHDChain& chain) diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index c804c5aba97..180a5872aa5 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -25,7 +25,7 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz { assert(entropyLen >= 32); while (true) { // loop until we find usable entropy - std::vector entropy(entropyLen, 0); + RawHDSeed entropy(entropyLen, 0); GetRandBytes(entropy.data(), entropyLen); const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); SecureString mnemonic(phrase); @@ -99,7 +99,7 @@ std::optional> DeriveBip44TransparentAccountKey(co } std::optional Bip44AccountChains::ForAccount( - const HDSeed& seed, + const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); @@ -263,7 +263,10 @@ std::pair SaplingExtendedSpendingKey::For std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { auto m = Master(seed); - // We use a fixed keypath scheme of m/32'/coin_type'/account'/addressIndex' + // We use a fixed keypath scheme of m/32'/coin_type'/0x7FFFFFFF'/addressIndex' + // This is not a "standard" path, but instead is a predictable location for + // legacy zcashd-derived keys that is minimally different from the UA account + // path, while unlikely to collide with normal UA account usage. // Derive m/32' auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); @@ -301,7 +304,7 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const // Unified // -std::optional> ZcashdUnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { ZcashdUnifiedSpendingKey usk; usk.accountId = accountId; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 365c3654f53..7e8697cc094 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -367,7 +367,7 @@ class ZcashdUnifiedSpendingKey { ZcashdUnifiedSpendingKey() {} public: static std::optional> ForAccount( - const HDSeed& seed, + const MnemonicSeed& mnemonic, uint32_t bip44CoinType, uint32_t accountId); @@ -396,7 +396,7 @@ class Bip44AccountChains { seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} public: static std::optional ForAccount( - const HDSeed& seed, + const MnemonicSeed& mnemonic, uint32_t bip44CoinType, uint32_t accountId); From ea8e9438af383dd18f053f805552acb9c07db500 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 16:50:57 -0600 Subject: [PATCH 115/514] Fix name of menmonic entropy length constant. --- src/wallet/wallet.cpp | 2 +- src/wallet/wallet.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 716b7206137..69ef4ff007e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2306,7 +2306,7 @@ void CWallet::GenerateNewSeed(Language language) { LOCK(cs_wallet); - auto seed = MnemonicSeed::Random(BIP44CoinType(), language, HD_WALLET_SEED_LENGTH); + auto seed = MnemonicSeed::Random(BIP44CoinType(), language, WALLET_MNEMONIC_ENTROPY_LENGTH); int64_t nCreationTime = GetTime(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 16dd7141b9d..4bf1a2b302a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -71,8 +71,8 @@ static const bool DEFAULT_WALLETBROADCAST = true; // unless there is some exceptional network disruption. static const unsigned int WITNESS_CACHE_SIZE = MAX_REORG_LENGTH + 1; -//! Size of HD seed in bytes -static const size_t HD_WALLET_SEED_LENGTH = 32; +//! Amount of entropy used in generation of the mnemonic seed, in bytes. +static const size_t WALLET_MNEMONIC_ENTROPY_LENGTH = 32; extern const char * DEFAULT_WALLET_DAT; From f626673b89ed3cd44935f553b8e5c9c4ab5cb737 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Nov 2021 16:56:11 -0600 Subject: [PATCH 116/514] Fix polymorphism of string serialization. Co-authored-by: str4d --- src/serialize.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serialize.h b/src/serialize.h index c56c2d12e23..db3f752760b 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -511,10 +511,10 @@ CVarInt WrapVarInt(I& n) { return CVarInt(n); } * string */ template -void Serialize(Stream& os, const std::basic_string& str); +void Serialize(Stream& os, const std::basic_string& str); template -void Unserialize(Stream& is, std::basic_string& str); +void Unserialize(Stream& is, std::basic_string& str); /** * prevector From e05c1ddf8afbede401635e0feb42b35fa4fa336a Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Fri, 12 Nov 2021 12:02:19 +0200 Subject: [PATCH 117/514] Fix typos --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- doc/book/src/dev/rust.md | 2 +- doc/release-process.md | 2 +- qa/rpc-tests/finalsaplingroot.py | 2 +- qa/rpc-tests/fundrawtransaction.py | 2 +- qa/rpc-tests/merkle_blocks.py | 2 +- qa/rpc-tests/nodehandling.py | 2 +- qa/rpc-tests/p2p-fullblocktest.py | 2 +- qa/rpc-tests/pruning.py | 4 ++-- qa/rpc-tests/smartfees.py | 2 +- qa/rpc-tests/upgrade_golden.py | 2 +- qa/rpc-tests/wallet_import_export.py | 4 ++-- qa/rpc-tests/wallet_persistence.py | 2 +- qa/rpc-tests/zapwallettxes.py | 2 +- qa/zcash/checksec.sh | 16 ++++++++-------- src/bench/perf.cpp | 2 +- src/init.cpp | 2 +- src/main.cpp | 6 +++--- src/mempool_limit.cpp | 2 +- src/miner.cpp | 2 +- src/net.h | 2 +- src/policy/fees.h | 6 +++--- src/pow/tromp/equi_miner.h | 2 +- src/rust/include/rust/VA_OPT.hpp | 2 +- src/support/cleanse.cpp | 2 +- src/test/addrman_tests.cpp | 2 +- src/test/bitcoin-util-test.py | 2 +- src/test/checkqueue_tests.cpp | 2 +- src/test/data/script_valid.json | 2 +- src/test/data/tx_invalid.json | 2 +- src/test/data/tx_valid.json | 2 +- src/test/limitedmap_tests.cpp | 2 +- src/test/miner_tests.cpp | 2 +- src/torcontrol.cpp | 2 +- src/wallet/gtest/test_wallet.cpp | 2 +- src/wallet/rpcwallet.cpp | 4 ++-- src/wallet/test/rpc_wallet_tests.cpp | 2 +- 37 files changed, 51 insertions(+), 51 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 9a101d34448..44778740470 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -36,7 +36,7 @@ Please provide a general summary of the issue you're experiencing Tell us what should happen ### Actual behaviour + errors -Tell us what happens instead including any noticable error output (any messages +Tell us what happens instead including any noticeable error output (any messages displayed on-screen when e.g. a crash occurred) ### The version of Zcash you were using: diff --git a/doc/book/src/dev/rust.md b/doc/book/src/dev/rust.md index eb318e8dde3..cae406f3ac8 100644 --- a/doc/book/src/dev/rust.md +++ b/doc/book/src/dev/rust.md @@ -5,7 +5,7 @@ where possible. ## Adding new dependencies in online-Rust mode -The `zcashd` build system pins all dependencies, and in order to faciliate +The `zcashd` build system pins all dependencies, and in order to facilitate deterministic builds, `cargo` is configured to run in offline mode with vendored crates. This means that if, for example, you add the `foobar` crate to `Cargo.toml`, you will likely see an error similar to this: diff --git a/doc/release-process.md b/doc/release-process.md index cb22989ba37..ebd28446bc6 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -32,7 +32,7 @@ Check that dependencies are up-to-date or have been postponed: $ ./qa/zcash/updatecheck.py ``` -If you are missing the `.updatecheck-token` file requried to run this script, +If you are missing the `.updatecheck-token` file required to run this script, please ask Taylor or another Zcash developer for a copy, or create an unprivileged personal access token for a github account and save it to the file in the format `username:hex-token`. diff --git a/qa/rpc-tests/finalsaplingroot.py b/qa/rpc-tests/finalsaplingroot.py index 257d9893d04..ae8a4c5f2f3 100755 --- a/qa/rpc-tests/finalsaplingroot.py +++ b/qa/rpc-tests/finalsaplingroot.py @@ -43,7 +43,7 @@ def run_test(self): self.nodes[0].generate(200) self.sync_all() - # Verfify genesis block contains null field for what is now called the final sapling root field. + # Verify genesis block contains null field for what is now called the final sapling root field. blk = self.nodes[0].getblock("0") assert_equal(blk["finalsaplingroot"], NULL_FIELD) treestate = self.nodes[0].z_gettreestate("0") diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index de8dc39e435..f75470306c4 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -73,7 +73,7 @@ def run_test(self): rawtxfund = self.nodes[2].fundrawtransaction(rawtx) fee = rawtxfund['fee'] dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) - assert_equal(len(dec_tx['vin']) > 0, True) #test if we have enought inputs + assert_equal(len(dec_tx['vin']) > 0, True) #test if we have enough inputs ############################## # simple test with two coins # diff --git a/qa/rpc-tests/merkle_blocks.py b/qa/rpc-tests/merkle_blocks.py index 4b7f1468075..2945861b39a 100755 --- a/qa/rpc-tests/merkle_blocks.py +++ b/qa/rpc-tests/merkle_blocks.py @@ -75,7 +75,7 @@ def run_test(self): txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 - # We cant find the block from a fully-spent tx + # We can't find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index 40614d2e810..59cdbd60c45 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -27,7 +27,7 @@ def run_test(self): ########################### assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point self.nodes[2].setban("127.0.0.1", "add") - time.sleep(3) #wait till the nodes are disconected + time.sleep(3) #wait till the nodes are disconnected assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point assert_equal(len(self.nodes[2].listbanned()), 1) self.nodes[2].clearbanned() diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index 2307cfcebc5..5634418172f 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -159,7 +159,7 @@ def tip(number): yield test - # Start by bulding a couple of blocks on top (which output is spent is in parentheses): + # Start by building a couple of blocks on top (which output is spent is in parentheses): # genesis -> b1 (0) -> b2 (1) out0 = get_spendable_output() block(1, spend=out0) diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 2916f76fefd..95a18ca920e 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -152,7 +152,7 @@ def reorg_test(self): print("Invalidating block at height:",invalidheight,badhash) self.nodes[1].invalidateblock(badhash) - # We've now switched to our previously mined-24 block fork on node 1, but thats not what we want + # We've now switched to our previously mined-24 block fork on node 1, but that's not what we want # So invalidate that fork as well, until we're on the same chain as node 0/2 (but at an ancestor 288 blocks ago) mainchainhash = self.nodes[0].getblockhash(invalidheight - 1) curhash = self.nodes[1].getblockhash(invalidheight - 1) @@ -210,7 +210,7 @@ def reorg_back(self): goalbesthash = self.mainchainhash2 # As of 0.10 the current block download logic is not able to reorg to the original chain created in - # create_chain_with_stale_blocks because it doesn't know of any peer thats on that chain from which to + # create_chain_with_stale_blocks because it doesn't know of any peer that's on that chain from which to # redownload its missing blocks. # Invalidate the reorg_test chain in node 0 as well, it can successfully switch to the original chain # because it has all the block data. diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 44086fa4fce..cc3659dad5d 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -29,7 +29,7 @@ def satoshi_round(amount): def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): ''' Create and send a transaction with a random fee. - The transaction pays to a trival P2SH script, and assumes that its inputs + The transaction pays to a trivial P2SH script, and assumes that its inputs are of the same form. The function takes a list of confirmed outputs and unconfirmed outputs and attempts to use the confirmed list first for its inputs. diff --git a/qa/rpc-tests/upgrade_golden.py b/qa/rpc-tests/upgrade_golden.py index 5e2e41eee23..b115b22a01a 100755 --- a/qa/rpc-tests/upgrade_golden.py +++ b/qa/rpc-tests/upgrade_golden.py @@ -44,7 +44,7 @@ def setup_network(self, split=False): logging.info("Initializing the network in "+self.options.tmpdir) # Node 0 will always be running with the most recent network upgrade version. - # The remaining nodes start with the nework upgrade versions in order that they + # The remaining nodes start with the network upgrade versions in order that they # are specified in the upgrades list. upgrade_args = [self.upgrades[-1].extra_args] + [u.extra_args for u in self.upgrades] self.nodes = start_nodes(len(self.upgrades) + 1, self.options.tmpdir, extra_args=upgrade_args) diff --git a/qa/rpc-tests/wallet_import_export.py b/qa/rpc-tests/wallet_import_export.py index 67e43842c6d..da26bf9c660 100755 --- a/qa/rpc-tests/wallet_import_export.py +++ b/qa/rpc-tests/wallet_import_export.py @@ -51,14 +51,14 @@ def run_test(self): assert_true(sapling_address0 in sapling_keys1) assert_true(sapling_address2 in sapling_keys1) - # make sure we have perserved the metadata + # make sure we have preserved the metadata for sapling_key0 in sapling_keys0.splitlines(): assert_true(sapling_key0 in sapling_keys1) # Helper functions def parse_wallet_file(dump_path): file_lines = open(dump_path, "r", encoding="utf8").readlines() - # We expect information about the HDSeed and fingerpring in the header + # We expect information about the HDSeed and fingerprint in the header assert_true("HDSeed" in file_lines[4], "Expected HDSeed") assert_true("fingerprint" in file_lines[4], "Expected fingerprint") seed_comment_line = file_lines[4][2:].split() # ["HDSeed=...", "fingerprint=..."] diff --git a/qa/rpc-tests/wallet_persistence.py b/qa/rpc-tests/wallet_persistence.py index 171201be2e7..7c8c348388a 100755 --- a/qa/rpc-tests/wallet_persistence.py +++ b/qa/rpc-tests/wallet_persistence.py @@ -36,7 +36,7 @@ def run_test(self): # Verify Sapling address is persisted in wallet sapling_addr = self.nodes[0].z_getnewaddress('sapling') - # Make sure the node has the addresss + # Make sure the node has the address addresses = self.nodes[0].z_listaddresses() assert_true(sapling_addr in addresses, "Should contain address before restart") diff --git a/qa/rpc-tests/zapwallettxes.py b/qa/rpc-tests/zapwallettxes.py index 9985eb89b44..1f02234dbf8 100755 --- a/qa/rpc-tests/zapwallettxes.py +++ b/qa/rpc-tests/zapwallettxes.py @@ -75,7 +75,7 @@ def run_test (self): print(e) aException = True - assert_equal(aException, True) # there must be a expection because the unconfirmed wallettx0 must be gone by now + assert_equal(aException, True) # there must be an exception because the unconfirmed wallet tx0 must be gone by now tx0 = self.nodes[0].gettransaction(txid0) assert_equal(tx0['txid'], txid0) # tx0 (confirmed) must still be available because it was confirmed diff --git a/qa/zcash/checksec.sh b/qa/zcash/checksec.sh index ba382062724..be594ea6193 100755 --- a/qa/zcash/checksec.sh +++ b/qa/zcash/checksec.sh @@ -39,7 +39,7 @@ # Name : checksec.sh # Version : 1.7.0 # Author : Brian Davis -# Date : Feburary 2014 +# Date : February 2014 # Download: https://github.com/slimm609/checksec.sh # # --- Modified Version --- @@ -298,9 +298,9 @@ echo_message() { # check selinux status getsestatus() { local status - ${debug} && echo -e "\n***fuction getsestatus" + ${debug} && echo -e "\n***function getsestatus" if (command_exists getenforce); then - ${debug} && echo "***fuction getsestatus->getenforce" + ${debug} && echo "***function getsestatus->getenforce" sestatus=$(getenforce) if [[ "${sestatus}" == "Disabled" ]]; then status=0 @@ -310,7 +310,7 @@ getsestatus() { status=2 fi elif (command_exists sestatus); then - ${debug} && echo "***fuction getsestatus->sestatus" + ${debug} && echo "***function getsestatus->sestatus" sestatus=$(sestatus | grep "SELinux status" | awk '{ print $3}') if [[ "${sestatus}" == "disabled" ]]; then status=0 @@ -328,7 +328,7 @@ return ${status} # check if directory exists dir_exists () { - ${debug} && echo "fuction dir_exists" + ${debug} && echo "function dir_exists" if [[ -d "${1}" ]] ; then return 0 else @@ -842,7 +842,7 @@ kernelcheck() { fi echo_message " Kernel heap randomization: " "" "" "" - # NOTE: y means it turns off kernel heap randomization for backwards compatability (libc5) + # NOTE: y means it turns off kernel heap randomization for backwards compatibility (libc5) if ${kconfig} | grep -qi 'CONFIG_COMPAT_BRK=y'; then echo_message "\033[31mDisabled\033[m\n" "Disabled," " kernel_heap_randomization='no'" ', "kernel_heap_randomization":"no"' else @@ -1123,7 +1123,7 @@ kernelcheck() { sestatus=$? if [[ ${sestatus} == 0 ]]; then echo_message "\033[31mDisabled\033[m\n" "Disabled,," " (1 << 21) && !GetBoolArg("-txindex", DEFAULT_TXINDEX)) nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB diff --git a/src/main.cpp b/src/main.cpp index b2ad430120f..0ff01a1c059 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6181,7 +6181,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if ((mi->second->nVersion <= 4) != (inv.type == MSG_TX)) { Misbehaving(pfrom->GetId(), 100); LogPrint("net", "Wrong INV message type used for v%d tx", mi->second->nVersion); - // Break so that this inv mesage will be erased from the queue + // Break so that this inv message will be erased from the queue // (otherwise the peer would repeatedly hit this case until its // Misbehaving level rises above -banscore, no matter what the // user set it to). @@ -6202,7 +6202,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if ((txinfo.tx->nVersion <= 4) != (inv.type == MSG_TX)) { Misbehaving(pfrom->GetId(), 100); LogPrint("net", "Wrong INV message type used for v%d tx", txinfo.tx->nVersion); - // Break so that this inv mesage will be erased from the queue. + // Break so that this inv message will be erased from the queue. break; } // Ensure we only reply with a transaction if it is exactly what @@ -7201,7 +7201,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); } catch (const std::ios_base::failure&) { // Avoid feedback loops by preventing reject messages from triggering a new reject message. - LogPrint("net", "Unparseable reject message received\n"); + LogPrint("net", "Unparsable reject message received\n"); } } } diff --git a/src/mempool_limit.cpp b/src/mempool_limit.cpp index c2137e0e3e7..d4c1a0455bd 100644 --- a/src/mempool_limit.cpp +++ b/src/mempool_limit.cpp @@ -102,7 +102,7 @@ void WeightedTxTree::remove(const uint256& txId) size_t removeIndex = txIdToIndexMap[txId]; // We reduce the size at the start of this method to avoid saying size - 1 - // when refering to the last element of the array below + // when referring to the last element of the array below size -= 1; TxWeight lastChildWeight = txIdAndWeights[size].txWeight; diff --git a/src/miner.cpp b/src/miner.cpp index a1dc0fa3a11..f48137860b6 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -331,7 +331,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: + // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); // How much of the block should be dedicated to high-priority transactions, diff --git a/src/net.h b/src/net.h index 05152e6b803..6e9e27b7bd7 100644 --- a/src/net.h +++ b/src/net.h @@ -690,7 +690,7 @@ class CNode static bool BannedSetIsDirty(); //!set the "dirty" flag for the banlist static void SetBannedSetDirty(bool dirty=true); - //!clean unused entires (if bantime has expired) + //!clean unused entries (if bantime has expired) static void SweepBanned(); void copyStats(CNodeStats &stats); diff --git a/src/policy/fees.h b/src/policy/fees.h index 1389ae83382..383d629d1d3 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -93,13 +93,13 @@ class TxConfirmStats // Count the total # of txs in each bucket // Track the historical moving average of this total over blocks std::vector txCtAvg; - // and calcuate the total for the current block to update the moving average + // and calculate the total for the current block to update the moving average std::vector curBlockTxCt; // Count the total # of txs confirmed within Y blocks in each bucket - // Track the historical moving average of theses totals over blocks + // Track the historical moving average of these totals over blocks std::vector > confAvg; // confAvg[Y][X] - // and calcuate the totals for the current block to update the moving averages + // and calculate the totals for the current block to update the moving averages std::vector > curBlockConf; // curBlockConf[Y][X] // Sum the total priority/fee of all txs in each bucket diff --git a/src/pow/tromp/equi_miner.h b/src/pow/tromp/equi_miner.h index aeed8239dc5..5efbbaa6933 100644 --- a/src/pow/tromp/equi_miner.h +++ b/src/pow/tromp/equi_miner.h @@ -374,7 +374,7 @@ struct equi { struct collisiondata { #ifdef XBITMAP #if NSLOTS > 64 -#error cant use XBITMAP with more than 64 slots +#error can't use XBITMAP with more than 64 slots #endif u64 xhashmap[NRESTS]; u64 xmap; diff --git a/src/rust/include/rust/VA_OPT.hpp b/src/rust/include/rust/VA_OPT.hpp index 553b637ac7a..8f84c88e8e7 100644 --- a/src/rust/include/rust/VA_OPT.hpp +++ b/src/rust/include/rust/VA_OPT.hpp @@ -56,7 +56,7 @@ ------------- Clang -std=c++2a enables it. GCC has it enabled without -std=c++2a but warns "__VA_OPT__ is not available until C++2a" if another -std - flag is supplied along with -pedantic (dont know how to supress it). + flag is supplied along with -pedantic (dont know how to suppress it). MSVC TBD Credits diff --git a/src/support/cleanse.cpp b/src/support/cleanse.cpp index 9fa2fc182e3..8e02a2b08fe 100644 --- a/src/support/cleanse.cpp +++ b/src/support/cleanse.cpp @@ -21,7 +21,7 @@ void memory_cleanse(void *ptr, size_t len) /* In order to prevent the compiler from optimizing out the memset, this uses an * unremovable (see https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile ) - * asm block that the compiler must assume could access arbitary memory, including + * asm block that the compiler must assume could access arbitrary memory, including * the zero bytes written by std::memset. * * Quoting Adam Langley in commit ad1907fe73334d6c696c8539646c21b11178f20f diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 39640745384..711cd018bc8 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -392,7 +392,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) size_t percent23 = (addrman.size() * 23) / 100; BOOST_CHECK(vAddr.size() == percent23); BOOST_CHECK(vAddr.size() == 461); - // (Addrman.size() < number of addresses added) due to address collisons. + // (Addrman.size() < number of addresses added) due to address collisions. BOOST_CHECK(addrman.size() == 2007); } diff --git a/src/test/bitcoin-util-test.py b/src/test/bitcoin-util-test.py index fd8824febb3..b0282376dec 100755 --- a/src/test/bitcoin-util-test.py +++ b/src/test/bitcoin-util-test.py @@ -14,7 +14,7 @@ Runs automatically during `make check`. -Can also be run manually from the src directory by specifiying the source directory: +Can also be run manually from the src directory by specifying the source directory: test/bitcoin-util-test.py --srcdir='srcdir' [--verbose] """ diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 260a86408aa..e52fd127831 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) } -// Test that blocks which might allocate lots of memory free their memory agressively. +// Test that blocks which might allocate lots of memory free their memory aggressively. // // This test attempts to catch a pathological case where by lazily freeing // checks might mean leaving a check un-swapped out, and decreasing by 1 each diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 572814c8090..3ccc192e2c7 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -665,7 +665,7 @@ ["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", ""], ["While not really correctly DER encoded, the empty signature is allowed by"], -["STRICTENC to provide a compact way to provide a delibrately invalid signature."], +["STRICTENC to provide a compact way to provide a deliberately invalid signature."], ["0", "0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 CHECKSIG NOT", "STRICTENC"], ["0 0", "1 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 1 CHECKMULTISIG NOT", "STRICTENC"], diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index b99a72da9b9..9c6718f79bb 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -158,7 +158,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], -["By-time locks, with argument just beyond tx nLockTime (but within numerical boundries)"], +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 NOP2 1"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 265ba5de66c..762e1449ee2 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -102,7 +102,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], -["By-time locks, with argument just beyond tx nLockTime (but within numerical boundries)"], +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp index faaddffad8a..e3531486aa3 100644 --- a/src/test/limitedmap_tests.cpp +++ b/src/test/limitedmap_tests.cpp @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(limitedmap_test) // make sure the item is present BOOST_CHECK(map.count(i) == 1); - // use the iterator to check for the expected key adn value + // use the iterator to check for the expected key and value BOOST_CHECK(it->first == i); BOOST_CHECK(it->second == i + 1); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 82f104fb4bc..a2c674b8d5c 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) equi eq(1); eq.setstate(curr_state.state); - // Intialization done, start algo driver. + // Initialization done, start algo driver. eq.digit0(0); eq.xfull = eq.bfull = eq.hfull = 0; eq.showbsizes(0); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index acfbe45fa64..21f7467659a 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -360,7 +360,7 @@ static std::map ParseTorReplyMapping(const std::string /** Read full contents of a file and return them in a std::string. * Returns a pair . - * If an error occured, status will be false, otherwise status will be true and the data will be returned in string. + * If an error occurred, status will be false, otherwise status will be true and the data will be returned in string. * * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data * (with len > maxsize) will be returned. diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index edc06058558..33361a22bb3 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -1232,7 +1232,7 @@ TEST(WalletTests, CachedWitnessesEmptyChain) { EXPECT_TRUE((bool) sproutWitnesses[1]); EXPECT_TRUE((bool) saplingWitnesses[0]); - // Until #1302 is implemented, this should triggger an assertion + // Until #1302 is implemented, this should trigger an assertion EXPECT_DEATH(wallet.DecrementNoteWitnesses(&index), ".*nWitnessCacheSize > 0.*"); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e4011d211e9..a23336794bc 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2210,7 +2210,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) " \"spendable\" : true|false, (boolean) true if note can be spent by wallet, false if address is watchonly\n" " \"address\" : \"address\", (string) the shielded address\n" " \"amount\": xxxxx, (numeric) the amount of value in the note\n" - " \"memo\": xxxxx, (string) hexademical string representation of memo field\n" + " \"memo\": xxxxx, (string) hexadecimal string representation of memo field\n" " \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n" " }\n" " ,...\n" @@ -3359,7 +3359,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"outgoing\" : true|false (boolean, sapling) True if the output is not for an address in the wallet\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" - " \"memo\" : \"hexmemo\", (string) Hexademical string representation of the memo field\n" + " \"memo\" : \"hexmemo\", (string) hexadecimal string representation of the memo field\n" " \"memoStr\" : \"memo\", (string) Only returned if memo contains valid UTF-8 text.\n" " }\n" " ,...\n" diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 93cfda21468..2d411842f93 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -875,7 +875,7 @@ class MockSleepOperation : public AsyncRPCOperation { /* - * Test Aysnc RPC queue and operations. + * Test Async RPC queue and operations. */ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations) { From ae9266a1cd078cd680b9590bd91327fb337d808e Mon Sep 17 00:00:00 2001 From: Janito Vaqueiro Ferreira Filho Date: Tue, 5 Oct 2021 21:27:33 +0000 Subject: [PATCH 118/514] Move `CurrentTxVersionInfo` into a new file Don't compile it for `zcash_script`. --- src/Makefile.am | 2 ++ src/primitives/transaction.cpp | 42 -------------------------- src/primitives/tx_version_info.cpp | 48 ++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 src/primitives/tx_version_info.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 03485657962..9477cd0338d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -393,6 +393,7 @@ libbitcoin_common_a_SOURCES = \ netbase.cpp \ primitives/block.cpp \ primitives/transaction.cpp \ + primitives/tx_version_info.cpp \ proof_verifier.cpp \ protocol.cpp \ pubkey.cpp \ @@ -558,6 +559,7 @@ libzcash_script_la_SOURCES = \ crypto/sha512.cpp \ hash.cpp \ primitives/transaction.cpp \ + primitives/tx_version_info.cpp \ pubkey.cpp \ script/zcash_script.cpp \ script/interpreter.cpp \ diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 8239cfc982e..2263ab91ba6 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -425,45 +425,3 @@ std::string CTransaction::ToString() const str += " " + vout[i].ToString() + "\n"; return str; } - -/** - * Returns the most recent supported transaction version and version group id, - * as of the specified activation height and active features. - */ -TxVersionInfo CurrentTxVersionInfo( - const Consensus::Params& consensus, - int nHeight, - bool requireSprout) -{ - if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_ZFUTURE)) { - return { - .fOverwintered = true, - .nVersionGroupId = ZFUTURE_VERSION_GROUP_ID, - .nVersion = ZFUTURE_TX_VERSION - }; - } else if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5) && !requireSprout) { - return { - .fOverwintered = true, - .nVersionGroupId = ZIP225_VERSION_GROUP_ID, - .nVersion = ZIP225_TX_VERSION - }; - } else if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING)) { - return { - .fOverwintered = true, - .nVersionGroupId = SAPLING_VERSION_GROUP_ID, - .nVersion = SAPLING_TX_VERSION - }; - } else if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_OVERWINTER)) { - return { - .fOverwintered = true, - .nVersionGroupId = OVERWINTER_VERSION_GROUP_ID, - .nVersion = OVERWINTER_TX_VERSION - }; - } else { - return { - .fOverwintered = false, - .nVersionGroupId = 0, - .nVersion = CTransaction::SPROUT_MIN_CURRENT_VERSION - }; - } -} diff --git a/src/primitives/tx_version_info.cpp b/src/primitives/tx_version_info.cpp new file mode 100644 index 00000000000..80b59ac10a6 --- /dev/null +++ b/src/primitives/tx_version_info.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "primitives/transaction.h" + +/** + * Returns the most recent supported transaction version and version group id, + * as of the specified activation height and active features. + */ +TxVersionInfo CurrentTxVersionInfo( + const Consensus::Params& consensus, + int nHeight, + bool requireSprout) +{ + if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_ZFUTURE)) { + return { + .fOverwintered = true, + .nVersionGroupId = ZFUTURE_VERSION_GROUP_ID, + .nVersion = ZFUTURE_TX_VERSION + }; + } else if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5) && !requireSprout) { + return { + .fOverwintered = true, + .nVersionGroupId = ZIP225_VERSION_GROUP_ID, + .nVersion = ZIP225_TX_VERSION + }; + } else if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING)) { + return { + .fOverwintered = true, + .nVersionGroupId = SAPLING_VERSION_GROUP_ID, + .nVersion = SAPLING_TX_VERSION + }; + } else if (consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_OVERWINTER)) { + return { + .fOverwintered = true, + .nVersionGroupId = OVERWINTER_VERSION_GROUP_ID, + .nVersion = OVERWINTER_TX_VERSION + }; + } else { + return { + .fOverwintered = false, + .nVersionGroupId = 0, + .nVersion = CTransaction::SPROUT_MIN_CURRENT_VERSION + }; + } +} From 31bd2ba4f1f85814c34c252a198906875c8df8cb Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 9 Nov 2021 12:45:36 +1000 Subject: [PATCH 119/514] Add sigop count functions to zcash_script library --- src/script/zcash_script.cpp | 55 ++++++++++++++++++++++++++++++++++++- src/script/zcash_script.h | 21 ++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/script/zcash_script.cpp b/src/script/zcash_script.cpp index 9ea67fcc5ee..a60ea7890b8 100644 --- a/src/script/zcash_script.cpp +++ b/src/script/zcash_script.cpp @@ -5,6 +5,7 @@ #include "zcash_script.h" +#include "consensus/consensus.h" #include "consensus/upgrades.h" #include "primitives/transaction.h" #include "pubkey.h" @@ -69,6 +70,21 @@ struct ECCryptoClosure }; ECCryptoClosure instance_of_eccryptoclosure; + +/// Copy of GetLegacySigOpCount from main.cpp commit c4b2ef7c4 +unsigned int GetLegacySigOpCount(const CTransaction& tx) +{ + unsigned int nSigOps = 0; + for (const CTxIn& txin : tx.vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + for (const CTxOut& txout : tx.vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} } struct PrecomputedTransaction { @@ -92,7 +108,7 @@ void* zcash_script_new_precomputed_tx( return nullptr; } - // Regardless of the verification result, the tx did not error. + // Deserializing the tx did not error. set_error(err, zcash_script_ERR_OK); auto preTx = new PrecomputedTransaction(tx); return preTx; @@ -166,6 +182,43 @@ int zcash_script_verify( } } +unsigned int zcash_script_legacy_sigop_count_precomputed( + const void* pre_preTx, + zcash_script_error* err) +{ + const PrecomputedTransaction* preTx = static_cast(pre_preTx); + const CTransaction& tx = preTx->tx; + + // The current implementation of this method never errors. + set_error(err, zcash_script_ERR_OK); + + return GetLegacySigOpCount(preTx->tx); +} + +unsigned int zcash_script_legacy_sigop_count( + const unsigned char *txTo, + unsigned int txToLen, + zcash_script_error* err) +{ + try { + TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + CTransaction tx; + stream >> tx; + if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) { + set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH); + return UINT_MAX; + } + + // Deserializing the tx did not error. + set_error(err, zcash_script_ERR_OK); + + return GetLegacySigOpCount(tx); + } catch (const std::exception&) { + set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing + return UINT_MAX; + } +} + unsigned int zcash_script_version() { // Just use the API version for now diff --git a/src/script/zcash_script.h b/src/script/zcash_script.h index a34963cba54..12c8e06cba5 100644 --- a/src/script/zcash_script.h +++ b/src/script/zcash_script.h @@ -99,6 +99,27 @@ EXPORT_SYMBOL int zcash_script_verify( uint32_t consensusBranchId, zcash_script_error* err); +/// Returns the number of transparent signature operations in the +/// transparent inputs and outputs of the precomputed transaction +/// pointed to by preTx. +/// +/// Returns UINT_MAX on error. +/// If not NULL, err will contain an error/success code for the operation. +EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count_precomputed( + const void* preTx, + zcash_script_error* err); + +/// Returns the number of transparent signature operations in the +/// transparent inputs and outputs of the serialized transaction +/// pointed to by txTo. +/// +/// Returns UINT_MAX on error. +/// If not NULL, err will contain an error/success code for the operation. +EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count( + const unsigned char *txTo, + unsigned int txToLen, + zcash_script_error* err); + EXPORT_SYMBOL unsigned int zcash_script_version(); #ifdef __cplusplus From fa181a2d08a310861b344af97fae6d5b3392d07a Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 9 Nov 2021 12:51:57 +1000 Subject: [PATCH 120/514] Increment the zcash_script API version --- src/script/zcash_script.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/script/zcash_script.h b/src/script/zcash_script.h index 12c8e06cba5..cecca4e753d 100644 --- a/src/script/zcash_script.h +++ b/src/script/zcash_script.h @@ -33,7 +33,7 @@ extern "C" { #endif -#define ZCASH_SCRIPT_API_VER 1 +#define ZCASH_SCRIPT_API_VER 2 typedef enum zcash_script_error_t { @@ -120,6 +120,7 @@ EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count( unsigned int txToLen, zcash_script_error* err); +/// Returns the current version of the zcash_script library. EXPORT_SYMBOL unsigned int zcash_script_version(); #ifdef __cplusplus From 77ab1831a1918ef6039b7569f658c83b6342718c Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 9 Nov 2021 12:58:54 +1000 Subject: [PATCH 121/514] Remove an unused header --- src/script/zcash_script.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/script/zcash_script.cpp b/src/script/zcash_script.cpp index a60ea7890b8..2c57c86ae6c 100644 --- a/src/script/zcash_script.cpp +++ b/src/script/zcash_script.cpp @@ -5,7 +5,6 @@ #include "zcash_script.h" -#include "consensus/consensus.h" #include "consensus/upgrades.h" #include "primitives/transaction.h" #include "pubkey.h" From 3bae89c73202d6b61fd4f397355f7877167a2416 Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 16 Nov 2021 10:46:08 +1000 Subject: [PATCH 122/514] Use correct copyright header Co-authored-by: str4d --- src/primitives/tx_version_info.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/primitives/tx_version_info.cpp b/src/primitives/tx_version_info.cpp index 80b59ac10a6..bdcabcb1e87 100644 --- a/src/primitives/tx_version_info.cpp +++ b/src/primitives/tx_version_info.cpp @@ -1,5 +1,4 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2021 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . From 7d24ada7579d8a6f912c53f1b86cc5a9fe4483c1 Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 16 Nov 2021 10:47:02 +1000 Subject: [PATCH 123/514] Remove redundant variable Co-authored-by: str4d --- src/script/zcash_script.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/script/zcash_script.cpp b/src/script/zcash_script.cpp index 2c57c86ae6c..d6b425a7ade 100644 --- a/src/script/zcash_script.cpp +++ b/src/script/zcash_script.cpp @@ -186,7 +186,6 @@ unsigned int zcash_script_legacy_sigop_count_precomputed( zcash_script_error* err) { const PrecomputedTransaction* preTx = static_cast(pre_preTx); - const CTransaction& tx = preTx->tx; // The current implementation of this method never errors. set_error(err, zcash_script_ERR_OK); From f4a8dc57e0201e8da21a6b32b82f35c54433e1be Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 16 Nov 2021 10:49:37 +1000 Subject: [PATCH 124/514] Explain UINT_MAX error return value --- src/script/zcash_script.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/zcash_script.h b/src/script/zcash_script.h index cecca4e753d..d2c52b0eafd 100644 --- a/src/script/zcash_script.h +++ b/src/script/zcash_script.h @@ -103,7 +103,7 @@ EXPORT_SYMBOL int zcash_script_verify( /// transparent inputs and outputs of the precomputed transaction /// pointed to by preTx. /// -/// Returns UINT_MAX on error. +/// Returns UINT_MAX on error, so that invalid transactions don't pass the Zcash consensus rules. /// If not NULL, err will contain an error/success code for the operation. EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count_precomputed( const void* preTx, From bdbd86edb7f10cf77edb281467c8cbe1af270a8d Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 16 Nov 2021 10:51:07 +1000 Subject: [PATCH 125/514] Explain how to get rid of a tiny duplicated function --- src/script/zcash_script.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/script/zcash_script.cpp b/src/script/zcash_script.cpp index d6b425a7ade..aed1e99a9c2 100644 --- a/src/script/zcash_script.cpp +++ b/src/script/zcash_script.cpp @@ -70,7 +70,8 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; -/// Copy of GetLegacySigOpCount from main.cpp commit c4b2ef7c4 +// Copy of GetLegacySigOpCount from main.cpp commit c4b2ef7c4. +// Replace with the copy from src/consensus/tx_verify.{cpp,h} after backporting that refactor. unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; From de57da38cebe6c228f8698bd8ba6ef96c59b03ef Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 4 Nov 2021 20:15:35 -0500 Subject: [PATCH 126/514] Document mnemonic-seed-related RPC method changes & update changelog. --- doc/release-notes.md | 54 ++++++++++++++++++++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 5 +++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index a29094b5174..7daf66f305f 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,3 +4,57 @@ release-notes at release time) Notable changes =============== +Mnemonic Recovery Phrases +------------------------- + +The zcashd wallet has been modified to support ZIP 339 (derived from BIP 39) +which describes how to derive the wallet's HD seed from a mnemonic phrase. +The mnemonic phrase will be generated on load of the wallet, or the first time +the wallet is unlocked, and is available via the `z_exportwallet` RPC call. All +new addresses produced by the wallet are now derived from this seed using the +HD wallet functionality described in ZIP 32 and ZIP 316. For users upgrading an +existing Zcashd wallet, it is recommended that the wallet be backed up prior to +upgrading to the 4.5.2 Zcashd release. + +Following the upgrade to 4.5.2, Zcashd will require that the user confirm that +they have backed up their new emergency recovery phrase, which may be obtained +from the output of the `z_exportwallet` RPC call. This confirmation be can be +performed manually using the `zcashd-wallet-tool` utility that is supplied with +this release. The wallet will not allow the generation of new addresses until +this confirmation has been performed. It is recommended that after this +upgrade, that funds tied to preexisting addresses be migrated to newly +generated addresses so that all wallet funds are recoverable using the +emergency recovery phrase going forward. If you choose not to migrate funds in +this fashion, you will continue to need to securely back up the entire +`wallet.dat` file to ensure that you do not lose access to existing funds; +EXISTING FUNDS WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE +UNLESS THEY HAVE BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.5.2 +UPGRADE. + +New RPC Methods +--------------- + +- 'walletconfirmbackup' This newly created API is checks a provided emergency + recovery phrase against the wallet's emergency recovery phrase; if the phrases + match then it updates the wallet state to allow the generation of new addresses. + This backup confirmation workflow can be disabled by starting zcashd with + `-requirewalletbackup=false` but this is not recommended unless you know what + you're doing (and have otherwise backed up the wallet's recovery phrase anyway.) + For security reasons, this RPC method is not intended for use via zcash-cli + but is provided to enable `zcashd-wallet-tool` and other third-party wallet + interfaces to satisfy the backup confirmation requirement. Use of the + `walletconfirmbackup` API via zcash-cli would risk that the recovery phrase + being confirmed might be leaked via the user's shell history or the system + process table; `zcashd-wallet-tool` is specifically provided to avoid this + problem. + +RPC Changes +----------- + +- The results of the 'dumpwallet' and 'z_exportwallet' RPC methods have been modified + to now include the wallet's newly generated emergency recovery phrase as part of the + exported data. + +- The results of the 'getwalletinfo' RPC have been modified to return two new fields: + `mnemonic_seedfp` and `legacy_seedfp`, the latter of which replaces the field that + was previously named `seedfp`. diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 090459fc2a7..74938c8c30c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2062,7 +2062,10 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"seedfp\": \"uint256\", (string) the BLAKE2b-256 hash of the HD seed\n" + " \"mnemonic_seedfp\": \"uint256\", (string) the BLAKE2b-256 hash of the HD seed derived from the wallet's emergency recovery phrase\n" + " \"legacy_seedfp\": \"uint256\", (string, optional) if this wallet was created prior to release 4.5.2, this will contain the BLAKE2b-256\n" + " hash of the legacy HD seed that was used to derive Sapling addresses prior to the 4.5.2 upgrade to mnemonic\n" + " emergency recovery phrases. This field was previously named \"seedfp\".\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") From f65d6f63eeb495a09b477d088f5b1909daf9adc7 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 4 Nov 2021 21:36:40 -0500 Subject: [PATCH 127/514] Minor cleanup of the code that searches for a valid transparent key. --- src/wallet/wallet.cpp | 47 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 69ef4ff007e..782fa2dd269 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -281,35 +281,34 @@ CPubKey CWallet::GenerateNewKey() BIP44CoinType(), ZCASH_LEGACY_ACCOUNT).value(); - while (true) { - auto extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); + std::optional> extKey = std::nullopt; + do { + extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); hdChain.IncrementLegacyTKeyCounter(); - // if we did not successfully generate a key, try again. - if (extKey.has_value()) { - CKey secret = extKey.value().first.key; - CPubKey pubkey = secret.GetPubKey(); - assert(secret.VerifyPubKey(pubkey)); - - // Create new metadata - CKeyMetadata keyMeta(GetTime()); - keyMeta.hdKeypath = extKey.value().second; - keyMeta.seedFp = seed.Fingerprint(); - mapKeyMetadata[pubkey.GetID()] = keyMeta; - if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) - nTimeFirstKey = keyMeta.nCreateTime; - - if (!AddKeyPubKey(secret, pubkey)) - throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); + } while (!extKey.has_value()); - // Update the persisted chain information - if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { - throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); - } + CKey secret = extKey.value().first.key; + CPubKey pubkey = secret.GetPubKey(); + assert(secret.VerifyPubKey(pubkey)); - return pubkey; - } + // Create new metadata + CKeyMetadata keyMeta(GetTime()); + keyMeta.hdKeypath = extKey.value().second; + keyMeta.seedFp = seed.Fingerprint(); + mapKeyMetadata[pubkey.GetID()] = keyMeta; + if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) + nTimeFirstKey = keyMeta.nCreateTime; + + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); + + // Update the persisted chain information + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { + throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); } + + return pubkey; } bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) From 7860d6ede3a5f092b6c6c8a0a5caa9749b6c328e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sat, 6 Nov 2021 10:18:55 -0500 Subject: [PATCH 128/514] Generalize keypath parsing over account id. --- src/gtest/test_zip32.cpp | 4 ++-- src/zcash/address/zip32.cpp | 4 ++-- src/zcash/address/zip32.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index 062667e4ece..2d774fcd429 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -133,9 +133,9 @@ TEST(ZIP32, TestVectors) { testing::ElementsAreArray({ 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd })); } -TEST(ZIP32, ParseZip32KeypathAccount) { +TEST(ZIP32, ParseHDKeypathAccount) { auto expect_account = [](std::string sAccount, long expected) { - auto result = libzcash::ParseZip32KeypathAccount(sAccount); + auto result = libzcash::ParseHDKeypathAccount(32, sAccount); EXPECT_TRUE(result.has_value()); EXPECT_EQ(result.value(), expected); }; diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 180a5872aa5..9aa3bba9793 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -365,8 +365,8 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi return ua; } -std::optional ParseZip32KeypathAccount(const std::string& keyPath) { - std::regex pattern("m/32'/[0-9]+'/([0-9]+)'.*"); +std::optional ParseHDKeypathAccount(uint32_t accountId, const std::string& keyPath) { + std::regex pattern("m/" + std::to_string(accountId) + "'/[0-9]+'/([0-9]+)'.*"); std::smatch matches; if (std::regex_match(keyPath, matches, pattern)) { return stoul(matches[1]); diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 7e8697cc094..f5f23810a69 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -382,7 +382,7 @@ class ZcashdUnifiedSpendingKey { ZcashdUnifiedFullViewingKey ToFullViewingKey() const; }; -std::optional ParseZip32KeypathAccount(const std::string& keyPath); +std::optional ParseHDKeypathAccount(uint32_t accountId, const std::string& keyPath); class Bip44AccountChains { private: From f31b3c21907fa7fd3a26f76a82389a5b2bdb49e5 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sat, 6 Nov 2021 11:10:40 -0500 Subject: [PATCH 129/514] Update documentation for GenerateNewSeed. --- src/wallet/wallet.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4bf1a2b302a..35920fb8dad 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1296,9 +1296,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool IsHDFullyEnabled() const; /* Generates a new HD seed (will reset the chain child index counters) - Sets the seed's version based on the current wallet version (so the - caller must ensure the current wallet version is correct before calling - this function). */ + * by randomly generating a mnemonic phrase that can be used for wallet + * recovery, and deriving the HD seed from that phrase in accordance with + * BIP 39 / ZIP 339. Sets the seed's version based on the current wallet + * version (the caller must ensure the current wallet version is correct + * before calling this function). */ void GenerateNewSeed(Language language = English); bool SetMnemonicSeed(const MnemonicSeed& seed); From 6e6359229ae7b4451258e0f72bc907843925ec9a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sun, 7 Nov 2021 10:56:43 -0500 Subject: [PATCH 130/514] Use MnemonicSeed instead of HDSeed where appropriate. --- src/zcash/address/zip32.cpp | 6 +++--- src/zcash/address/zip32.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 9aa3bba9793..af41fb08583 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -76,7 +76,7 @@ namespace libzcash { // Transparent // -std::optional> DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { auto rawSeed = seed.RawSeed(); auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); @@ -242,7 +242,7 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const return xsk_i; } -std::pair SaplingExtendedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::pair SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/account' @@ -260,7 +260,7 @@ std::pair SaplingExtendedSpendingKey::For return std::make_pair(xsk, hdKeypath); } -std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { +std::pair SaplingExtendedSpendingKey::Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/0x7FFFFFFF'/addressIndex' diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index f5f23810a69..cdd281a44db 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -287,8 +287,8 @@ struct SaplingExtendedSpendingKey { } static SaplingExtendedSpendingKey Master(const HDSeed& seed); - static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId); - static std::pair Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); + static std::pair ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + static std::pair Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); SaplingExtendedSpendingKey Derive(uint32_t i) const; From cfee863644df772901cd8cc8699febed6b1af3b0 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sun, 7 Nov 2021 11:08:08 -0500 Subject: [PATCH 131/514] Add diversifier_index_t::ToTransparentChildIndex --- src/zcash/address/zip32.cpp | 16 ++++++++++++---- src/zcash/address/zip32.h | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index af41fb08583..3b3da122336 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -72,6 +72,15 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed) { namespace libzcash { +std::optional diversifier_index_t::ToTransparentChildIndex() const { + // ensure that the diversifier index is small enough for a t-addr + if (MAX_TRANSPARENT_CHILD_IDX < *this) { + return std::nullopt; + } else { + return (unsigned int) GetUint64(0); + } +} + // // Transparent // @@ -336,8 +345,8 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi ZcashdUnifiedAddress ua; if (transparentKey.has_value()) { - // ensure that the diversifier index is small enough for a t-addr - if (MAX_TRANSPARENT_CHILD_IDX < j) return std::nullopt; + auto childIndex = j.ToTransparentChildIndex(); + if (!childIndex.has_value()) return std::nullopt; CExtPubKey changeKey; if (!transparentKey.value().Derive(changeKey, 0)) { @@ -345,8 +354,7 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi } CExtPubKey childKey; - unsigned int childIndex = (unsigned int) j.GetUint64(0); - if (changeKey.Derive(childKey, childIndex)) { + if (changeKey.Derive(childKey, childIndex.value())) { ua.transparentKey = childKey.pubkey; } else { return std::nullopt; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index cdd281a44db..f159cd2a4b4 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -195,6 +195,8 @@ class diversifier_index_t : public base_blob<88> { return false; //overflow } + std::optional ToTransparentChildIndex() const; + friend bool operator<(const diversifier_index_t& a, const diversifier_index_t& b) { for (int i = 10; i >= 0; i--) { if (a.data[i] == b.data[i]) { From 0fe1134337f1ffbe15895e9ef67eb02a965861dc Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sun, 7 Nov 2021 11:26:28 -0500 Subject: [PATCH 132/514] Only maintain CKeyID for the transparent part of ZcashdUnifiedAddress --- src/zcash/address/zip32.cpp | 2 +- src/zcash/address/zip32.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 3b3da122336..f3aeb864e65 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -355,7 +355,7 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi CExtPubKey childKey; if (changeKey.Derive(childKey, childIndex.value())) { - ua.transparentKey = childKey.pubkey; + ua.transparentAddress = childKey.pubkey.GetID(); } else { return std::nullopt; } diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index f159cd2a4b4..f6a1a584fd1 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -314,15 +314,15 @@ class ZcashdUnifiedFullViewingKey; class ZcashdUnifiedAddress { private: diversifier_index_t diversifier_index; - std::optional transparentKey; //TODO: should this just be the public key hash? + std::optional transparentAddress; std::optional saplingAddress; friend class ZcashdUnifiedFullViewingKey; ZcashdUnifiedAddress() {} public: - const std::optional& GetTransparentKey() const { - return transparentKey; + const std::optional& GetTransparentAddress() const { + return transparentAddress; } const std::optional& GetSaplingPaymentAddress() const { From b722c40fe2094e57542ab88a4e3bbbd061d5909e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 17 Nov 2021 15:03:06 -0700 Subject: [PATCH 133/514] Minor naming fixes --- src/gtest/test_zip32.cpp | 8 ++++---- src/zcash/address/zip32.cpp | 24 ++++++++++++------------ src/zcash/address/zip32.h | 5 +++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index 2d774fcd429..e167f63a828 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -58,10 +58,10 @@ TEST(ZIP32, TestVectors) { m_1.ToXFVK().DefaultAddress().d, testing::ElementsAreArray({ 0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81 })); - auto m_1_2h = m_1.Derive(2 | ZIP32_HARDENED_KEY_LIMIT); + auto m_1_2h = m_1.Derive(2 | HARDENED_KEY_LIMIT); EXPECT_EQ(m_1_2h.depth, 2); EXPECT_EQ(m_1_2h.parentFVKTag, 0x079e99db); - EXPECT_EQ(m_1_2h.childIndex, 2 | ZIP32_HARDENED_KEY_LIMIT); + EXPECT_EQ(m_1_2h.childIndex, 2 | HARDENED_KEY_LIMIT); EXPECT_EQ( m_1_2h.chaincode, uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97")); @@ -84,7 +84,7 @@ TEST(ZIP32, TestVectors) { auto m_1_2hv = m_1_2h.ToXFVK(); EXPECT_EQ(m_1_2hv.depth, 2); EXPECT_EQ(m_1_2hv.parentFVKTag, 0x079e99db); - EXPECT_EQ(m_1_2hv.childIndex, 2 | ZIP32_HARDENED_KEY_LIMIT); + EXPECT_EQ(m_1_2hv.childIndex, 2 | HARDENED_KEY_LIMIT); EXPECT_EQ( m_1_2hv.chaincode, uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97")); @@ -103,7 +103,7 @@ TEST(ZIP32, TestVectors) { EXPECT_EQ(m_1_2hv.DefaultAddress(), m_1_2h.ToXFVK().DefaultAddress()); // Hardened derivation from an xfvk fails - EXPECT_FALSE(m_1_2hv.Derive(3 | ZIP32_HARDENED_KEY_LIMIT)); + EXPECT_FALSE(m_1_2hv.Derive(3 | HARDENED_KEY_LIMIT)); // Non-hardened derivation succeeds auto maybe_m_1_2hv_3 = m_1_2hv.Derive(3); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index f3aeb864e65..c7f994f5f0e 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -91,15 +91,15 @@ std::optional> DeriveBip44TransparentAccountKey(co // We use a fixed keypath scheme of m/44'/coin_type'/account' // Derive m/44' - auto m_32h = m.Derive(44 | ZIP32_HARDENED_KEY_LIMIT); - if (!m_32h.has_value()) return std::nullopt; + auto m_44h = m.Derive(44 | HARDENED_KEY_LIMIT); + if (!m_44h.has_value()) return std::nullopt; // Derive m/44'/coin_type' - auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); - if (!m_32h_cth.has_value()) return std::nullopt; + auto m_44h_cth = m_44h.value().Derive(bip44CoinType | HARDENED_KEY_LIMIT); + if (!m_44h_cth.has_value()) return std::nullopt; // Derive m/44'/coin_type'/account_id' - auto result = m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); + auto result = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT); if (!result.has_value()) return std::nullopt; auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; @@ -256,12 +256,12 @@ std::pair SaplingExtendedSpendingKey::For // We use a fixed keypath scheme of m/32'/coin_type'/account' // Derive m/32' - auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h = m.Derive(32 | HARDENED_KEY_LIMIT); // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h_cth = m_32h.Derive(bip44CoinType | HARDENED_KEY_LIMIT); // Derive account key at next index, skip keys already known to the wallet - auto xsk = m_32h_cth.Derive(accountId | ZIP32_HARDENED_KEY_LIMIT); + auto xsk = m_32h_cth.Derive(accountId | HARDENED_KEY_LIMIT); // Create new metadata auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; @@ -278,15 +278,15 @@ std::pair SaplingExtendedSpendingKey::Leg // path, while unlikely to collide with normal UA account usage. // Derive m/32' - auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h = m.Derive(32 | HARDENED_KEY_LIMIT); // Derive m/32'/coin_type' - auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h_cth = m_32h.Derive(bip44CoinType | HARDENED_KEY_LIMIT); // Derive account key at the legacy account index - auto m_32h_cth_l = m_32h_cth.Derive(ZCASH_LEGACY_ACCOUNT | ZIP32_HARDENED_KEY_LIMIT); + auto m_32h_cth_l = m_32h_cth.Derive(ZCASH_LEGACY_ACCOUNT | HARDENED_KEY_LIMIT); // Derive key at the specified address index - auto xsk = m_32h_cth_l.Derive(addressIndex | ZIP32_HARDENED_KEY_LIMIT); + auto xsk = m_32h_cth_l.Derive(addressIndex | HARDENED_KEY_LIMIT); // Create new metadata auto hdKeypath = "m/32'/" diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index f6a1a584fd1..0fa6df0a0d9 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -17,7 +17,8 @@ #include #include -const uint32_t ZIP32_HARDENED_KEY_LIMIT = 0x80000000; +// The minimum value for BIP-32 or ZIP-32 hardened key path element +const uint32_t HARDENED_KEY_LIMIT = 0x80000000; const size_t ZIP32_XFVK_SIZE = 169; const size_t ZIP32_XSK_SIZE = 169; @@ -26,7 +27,7 @@ const size_t ZIP32_XSK_SIZE = 169; * transparent and Sapling addresses via the legacy * `getnewaddress` and `z_getnewaddress` code paths, */ -const uint32_t ZCASH_LEGACY_ACCOUNT = 0x7FFFFFFF; +const uint32_t ZCASH_LEGACY_ACCOUNT = HARDENED_KEY_LIMIT - 1; typedef std::vector> RawHDSeed; From b1d6a514e235414cf775cc67aa0bdab138ec29e4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 17 Nov 2021 17:20:28 -0700 Subject: [PATCH 134/514] Parameterize HD keypath parsing by coin type. --- src/gtest/test_zip32.cpp | 12 ++++++------ src/zcash/address/zip32.cpp | 4 ++-- src/zcash/address/zip32.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index e167f63a828..2a43a192e95 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -134,23 +134,23 @@ TEST(ZIP32, TestVectors) { } TEST(ZIP32, ParseHDKeypathAccount) { - auto expect_account = [](std::string sAccount, long expected) { - auto result = libzcash::ParseHDKeypathAccount(32, sAccount); + auto expect_account = [](std::string sAccount, uint32_t coinType, long expected) { + auto result = libzcash::ParseHDKeypathAccount(32, coinType, sAccount); EXPECT_TRUE(result.has_value()); EXPECT_EQ(result.value(), expected); }; std::string sAccount = "m/32'/1234'/5'"; - expect_account(sAccount, 5); + expect_account(sAccount, 1234, 5); sAccount = "m/32'/1234'/50'"; - expect_account(sAccount, 50); + expect_account(sAccount, 1234, 50); sAccount = "m/32'/1234'/5'/0"; - expect_account(sAccount, 5); + expect_account(sAccount, 1234, 5); sAccount = "m/32'/133'/2147483646'/1"; - expect_account(sAccount, 2147483646); + expect_account(sAccount, 133, 2147483646); } TEST(ZIP32, diversifier_index_t_increment) diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index c7f994f5f0e..34b04a724c8 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -373,8 +373,8 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi return ua; } -std::optional ParseHDKeypathAccount(uint32_t accountId, const std::string& keyPath) { - std::regex pattern("m/" + std::to_string(accountId) + "'/[0-9]+'/([0-9]+)'.*"); +std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath) { + std::regex pattern("m/" + std::to_string(purpose) + "'/" + std::to_string(coinType) + "'/([0-9]+)'.*"); std::smatch matches; if (std::regex_match(keyPath, matches, pattern)) { return stoul(matches[1]); diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 0fa6df0a0d9..83bbc3443d4 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -385,7 +385,7 @@ class ZcashdUnifiedSpendingKey { ZcashdUnifiedFullViewingKey ToFullViewingKey() const; }; -std::optional ParseHDKeypathAccount(uint32_t accountId, const std::string& keyPath); +std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath); class Bip44AccountChains { private: From 2ec707345930a7a425d052e3d6fdbb745cbe506d Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 17 Nov 2021 18:03:11 -0700 Subject: [PATCH 135/514] Fix error message text to refer to zcashd-wallet-tool rather than the RPC method. --- src/wallet/rpcwallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 74938c8c30c..339feb9ae5f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -88,7 +88,7 @@ void EnsureWalletIsBackedUp(const CChainParams& params) throw JSONRPCError( RPC_WALLET_BACKUP_REQUIRED, "Error: Please acknowledge that you have backed up the wallet's emergency recovery phrase " - "by using the walletconfirmbackup RPC method first." + "by using zcashd-wallet-tool first." ); } From 675e489aff8b02242b8ff1df371164ad5aacbd23 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 16 Nov 2021 18:29:50 +0000 Subject: [PATCH 136/514] rust: Move `incremental_sinsemilla_tree_ffi` into crate root Having this be a submodule of `orchard_ffi` while following Rust 2018 module structure made it impossible to use the `include!` macro on `orchard_ffi.rs`, which is exactly what the `zcash_script` crate does. See this comment for details: https://github.com/rust-lang/rust/issues/50132#issuecomment-969450868 To resolve this, we restructure the FFI library crate to only have FFI methods in "leaf" module files. --- .../src/{orchard_ffi => }/incremental_sinsemilla_tree_ffi.rs | 4 ++-- src/rust/src/orchard_ffi.rs | 2 -- src/rust/src/rustzcash.rs | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) rename src/rust/src/{orchard_ffi => }/incremental_sinsemilla_tree_ffi.rs (98%) diff --git a/src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs b/src/rust/src/incremental_sinsemilla_tree_ffi.rs similarity index 98% rename from src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs rename to src/rust/src/incremental_sinsemilla_tree_ffi.rs index 370ae521c47..37fc07c7872 100644 --- a/src/rust/src/orchard_ffi/incremental_sinsemilla_tree_ffi.rs +++ b/src/rust/src/incremental_sinsemilla_tree_ffi.rs @@ -6,13 +6,13 @@ use std::mem::size_of_val; use std::ptr; use orchard::{bundle::Authorized, tree::MerkleHashOrchard}; - +use tracing::error; use zcash_primitives::{ merkle_tree::incremental::{read_frontier_v1, read_tree, write_frontier_v1, write_tree}, transaction::components::Amount, }; -use crate::orchard_ffi::{error, CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}; +use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}; pub const MERKLE_DEPTH: u8 = 32; pub const MAX_CHECKPOINTS: usize = 100; diff --git a/src/rust/src/orchard_ffi.rs b/src/rust/src/orchard_ffi.rs index 7bfea4080f7..dbc6649c671 100644 --- a/src/rust/src/orchard_ffi.rs +++ b/src/rust/src/orchard_ffi.rs @@ -19,8 +19,6 @@ use zcash_primitives::transaction::{ use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}; -mod incremental_sinsemilla_tree_ffi; - #[no_mangle] pub extern "C" fn orchard_bundle_clone( bundle: *const Bundle, diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index c4b1e6125bf..e4879d0c40e 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -70,6 +70,7 @@ mod tracing_ffi; mod address_ffi; mod history_ffi; +mod incremental_sinsemilla_tree_ffi; mod orchard_ffi; mod transaction_ffi; mod zip339_ffi; From 9fd44c76fad02cc82c9abb7420faa0daff1aeb97 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 18 Nov 2021 14:45:39 +0000 Subject: [PATCH 137/514] CI: Add Pyflakes to lints workflow --- .github/workflows/lints.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lints.yml b/.github/workflows/lints.yml index d44001af5b2..d2347eac34d 100644 --- a/.github/workflows/lints.yml +++ b/.github/workflows/lints.yml @@ -34,10 +34,6 @@ jobs: if: always() continue-on-error: true # Temporary until we get this passing - - name: Python UTF-8 encoding - run: ./test/lint/lint-python-utf8-encoding.sh - if: always() - - name: Shebang run: ./test/lint/lint-shebang.sh if: always() @@ -57,6 +53,22 @@ jobs: run: ./test/lint/lint-whitespace.sh if: always() + python: + name: Python + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: sudo python3 -m pip install pyflakes + + - name: Pyflakes + run: pyflakes qa src zcutil + if: always() + + - name: UTF-8 encoding + run: ./test/lint/lint-python-utf8-encoding.sh + if: always() + rust-clippy: name: Clippy (1.54.0) runs-on: ubuntu-latest From 92445ba0a5011ccb0cfdce14e4ca5d1e4e6b822a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 18 Nov 2021 10:53:08 -0700 Subject: [PATCH 138/514] Apply suggestions from code review Co-authored-by: str4d --- doc/release-notes.md | 2 +- src/wallet/rpcwallet.cpp | 2 +- src/zcash/address/zip32.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 7daf66f305f..ef6cc923121 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -7,7 +7,7 @@ Notable changes Mnemonic Recovery Phrases ------------------------- -The zcashd wallet has been modified to support ZIP 339 (derived from BIP 39) +The zcashd wallet has been modified to support ZIP 339 (to be compatible with BIP 39) which describes how to derive the wallet's HD seed from a mnemonic phrase. The mnemonic phrase will be generated on load of the wallet, or the first time the wallet is unlocked, and is available via the `z_exportwallet` RPC call. All diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 339feb9ae5f..d6c4870cd5c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1754,7 +1754,7 @@ UniValue walletconfirmbackup(const UniValue& params, bool fHelp) " of the data returned by z_exportwallet. An error will be returned if the value provided\n" " does not match the wallet's existing emergency recovery phrase.\n" "\nExamples:\n" - + HelpExampleCli("walletconfirmbackup", "\"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art\"") + + HelpExampleRpc("walletconfirmbackup", "\"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 34b04a724c8..82ae79ac15e 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -260,7 +260,7 @@ std::pair SaplingExtendedSpendingKey::For // Derive m/32'/coin_type' auto m_32h_cth = m_32h.Derive(bip44CoinType | HARDENED_KEY_LIMIT); - // Derive account key at next index, skip keys already known to the wallet + // Derive account key at the given account index auto xsk = m_32h_cth.Derive(accountId | HARDENED_KEY_LIMIT); // Create new metadata @@ -348,13 +348,13 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi auto childIndex = j.ToTransparentChildIndex(); if (!childIndex.has_value()) return std::nullopt; - CExtPubKey changeKey; - if (!transparentKey.value().Derive(changeKey, 0)) { + CExtPubKey externalKey; + if (!transparentKey.value().Derive(externalKey, 0)) { return std::nullopt; } CExtPubKey childKey; - if (changeKey.Derive(childKey, childIndex.value())) { + if (externalKey.Derive(childKey, childIndex.value())) { ua.transparentAddress = childKey.pubkey.GetID(); } else { return std::nullopt; From 2d25e08fccc89e6f59959482aa06f0a45a49b818 Mon Sep 17 00:00:00 2001 From: Sasha <2592730+superbaud@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:39:52 -0800 Subject: [PATCH 139/514] fix typo in docker run's volume argument --- contrib/docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docker/README.md b/contrib/docker/README.md index 912604517f2..69f5b7e9d8c 100644 --- a/contrib/docker/README.md +++ b/contrib/docker/README.md @@ -76,7 +76,7 @@ mkdir {./zcash-params-dir,./zcash-data-dir} sudo chown -R 2001.2001 {./zcash-params-dir,./zcash-data-dir} docker run -d --name my_zcashd \ -v $(pwd)/zcash-data-dir:/srv/zcashd/.zcash \ - -v $(pwd)/zcash-params-dir/srv/zcashd/.zcash-params \ + -v $(pwd)/zcash-params-dir:/srv/zcashd/.zcash-params \ electriccoinco/zcashd ``` From 7be1ec9c2395abc45e8d6eef7a8d8b561957bbe1 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 18 Nov 2021 11:40:23 -0700 Subject: [PATCH 140/514] Minor textual fixes to release notes. Co-authored-by: Daira Hopwood --- doc/release-notes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index ef6cc923121..6af6c00f26a 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -18,7 +18,7 @@ upgrading to the 4.5.2 Zcashd release. Following the upgrade to 4.5.2, Zcashd will require that the user confirm that they have backed up their new emergency recovery phrase, which may be obtained -from the output of the `z_exportwallet` RPC call. This confirmation be can be +from the output of the `z_exportwallet` RPC call. This confirmation can be performed manually using the `zcashd-wallet-tool` utility that is supplied with this release. The wallet will not allow the generation of new addresses until this confirmation has been performed. It is recommended that after this @@ -34,12 +34,12 @@ UPGRADE. New RPC Methods --------------- -- 'walletconfirmbackup' This newly created API is checks a provided emergency +- 'walletconfirmbackup' This newly created API checks a provided emergency recovery phrase against the wallet's emergency recovery phrase; if the phrases match then it updates the wallet state to allow the generation of new addresses. This backup confirmation workflow can be disabled by starting zcashd with `-requirewalletbackup=false` but this is not recommended unless you know what - you're doing (and have otherwise backed up the wallet's recovery phrase anyway.) + you're doing (and have otherwise backed up the wallet's recovery phrase anyway). For security reasons, this RPC method is not intended for use via zcash-cli but is provided to enable `zcashd-wallet-tool` and other third-party wallet interfaces to satisfy the backup confirmation requirement. Use of the From 14f95572fa67b3f69d62cb79a7da2eb18b1fffb7 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 18 Nov 2021 15:05:46 -0700 Subject: [PATCH 141/514] Improve documentation of the `-walletrequirebackup` zcashd wallet configuration option. --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 782fa2dd269..bbd7b7a3afe 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4812,7 +4812,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); - strUsage += HelpMessageOpt("-walletrequirebackup=false", _("Allow generation of new spending keys & addresses from the mnemonic seed, even if the backup of that seed has not yet been confirmed with `walletconfirmbackup`.")); + strUsage += HelpMessageOpt("-walletrequirebackup=", _("By default, the wallet will not allow generation of new spending keys & addresses from the mnemonic seed until the backup of that seed has been confirmed with the `zcashd-wallet-tool` utility. A user may start zcashd with `-walletrequirebackup=false` to allow generation of spending keys even if the backup has not yet been confirmed.")); if (showDebug) { From f49f4c73d881f4d9689ae3741c6cbd84f615309f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 19 Jul 2021 19:59:21 -0600 Subject: [PATCH 142/514] Rename OrchardMerkleTree -> OrchardMerkleFrontier Remove IncrementalSinsemillaTree; this will be replaced by a more full-featured OrchardWallet type which embeds the incremental merkle tree used in wallet operations. --- src/coins.cpp | 14 +- src/coins.h | 8 +- src/gtest/test_merkletree.cpp | 6 +- src/main.cpp | 2 +- src/primitives/orchard.h | 4 +- .../rust/orchard/incremental_merkle_tree.h | 87 ++++++++++ .../orchard/incremental_sinsemilla_tree.h | 149 ------------------ ..._ffi.rs => incremental_merkle_tree_ffi.rs} | 141 +---------------- src/rust/src/rustzcash.rs | 2 +- src/test/coins_tests.cpp | 14 +- src/txdb.cpp | 10 +- src/txdb.h | 2 +- src/zcash/IncrementalMerkleTree.hpp | 16 +- src/zcbenchmarks.cpp | 4 +- 14 files changed, 131 insertions(+), 328 deletions(-) create mode 100644 src/rust/include/rust/orchard/incremental_merkle_tree.h delete mode 100644 src/rust/include/rust/orchard/incremental_sinsemilla_tree.h rename src/rust/src/{incremental_sinsemilla_tree_ffi.rs => incremental_merkle_tree_ffi.rs} (52%) diff --git a/src/coins.cpp b/src/coins.cpp index 0ea164cdcd3..2a62e549d6d 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -46,7 +46,7 @@ bool CCoins::Spend(uint32_t nPos) } bool CCoinsView::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; } bool CCoinsView::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return false; } -bool CCoinsView::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const { return false; } +bool CCoinsView::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &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; } bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } @@ -75,7 +75,7 @@ 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::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const { return base->GetOrchardAnchorAt(rt, tree); } +bool CCoinsViewBacked::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { return base->GetOrchardAnchorAt(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); } bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } @@ -191,7 +191,7 @@ bool CCoinsViewCache::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &t return true; } -bool CCoinsViewCache::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const { +bool CCoinsViewCache::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { CAnchorsOrchardMap::const_iterator it = cacheOrchardAnchors.find(rt); if (it != cacheOrchardAnchors.end()) { if (it->second.entered) { @@ -321,9 +321,9 @@ template<> void CCoinsViewCache::PushAnchor(const SaplingMerkleTree &tree) ); } -template<> void CCoinsViewCache::PushAnchor(const OrchardMerkleTree &tree) +template<> void CCoinsViewCache::PushAnchor(const OrchardMerkleFrontier &tree) { - AbstractPushAnchor( + AbstractPushAnchor( tree, ORCHARD, cacheOrchardAnchors, @@ -352,7 +352,7 @@ void CCoinsViewCache::BringBestAnchorIntoCache( template<> void CCoinsViewCache::BringBestAnchorIntoCache( const uint256 ¤tRoot, - OrchardMerkleTree &tree + OrchardMerkleFrontier &tree ) { assert(GetOrchardAnchorAt(currentRoot, tree)); @@ -1069,7 +1069,7 @@ std::optional CCoinsViewCache::HaveShieldedRequirements( std::optional root = tx.GetOrchardBundle().GetAnchor(); if (root) { - OrchardMerkleTree tree; + OrchardMerkleFrontier tree; if (!GetOrchardAnchorAt(root.value(), tree)) { auto txid = tx.GetHash().ToString(); auto anchor = root.value().ToString(); diff --git a/src/coins.h b/src/coins.h index ddfe08103c3..c318d65d3b5 100644 --- a/src/coins.h +++ b/src/coins.h @@ -304,7 +304,7 @@ struct CAnchorsSaplingCacheEntry struct CAnchorsOrchardCacheEntry { bool entered; // This will be false if the anchor is removed from the cache - OrchardMerkleTree tree; // The tree itself + OrchardMerkleFrontier tree; // The tree itself unsigned char flags; enum Flags { @@ -368,7 +368,7 @@ class CCoinsView virtual bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; //! Retrieve the tree (Orchard) at a particular anchored root in the chain - virtual bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const; + virtual bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const; //! Determine whether a nullifier is spent or not virtual bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; @@ -428,7 +428,7 @@ class CCoinsViewBacked : public CCoinsView CCoinsViewBacked(CCoinsView *viewIn); bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; - bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const; + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const; bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; @@ -521,7 +521,7 @@ class CCoinsViewCache : public CCoinsViewBacked // Standard CCoinsView methods bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; - bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const; + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const; bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; diff --git a/src/gtest/test_merkletree.cpp b/src/gtest/test_merkletree.cpp index b761670ac76..a4858c7213a 100644 --- a/src/gtest/test_merkletree.cpp +++ b/src/gtest/test_merkletree.cpp @@ -293,13 +293,13 @@ TEST(orchardMerkleTree, emptyroot) { // an integer, which is converted to little-endian internally. uint256 expected = uint256S("2fd8e51a03d9bbe2dd809831b1497aeb68a6e37ddf707ced4aa2d8dff13529ae"); - ASSERT_EQ(OrchardMerkleTree::empty_root(), expected); + ASSERT_EQ(OrchardMerkleFrontier::empty_root(), expected); } TEST(orchardMerkleTree, appendBundle) { - OrchardMerkleTree newTree; + OrchardMerkleFrontier newTree; - ASSERT_EQ(newTree.root(), OrchardMerkleTree::empty_root()); + ASSERT_EQ(newTree.root(), OrchardMerkleFrontier::empty_root()); for (int i = 0; i < 1; i++) { CDataStream ssBundleData(merkle_roots_orchard[i].bundle, SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/main.cpp b/src/main.cpp index 0ff01a1c059..91863a38f6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3125,7 +3125,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin SaplingMerkleTree sapling_tree; assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); - OrchardMerkleTree orchard_tree; + OrchardMerkleFrontier orchard_tree; assert(view.GetOrchardAnchorAt(view.GetBestAnchor(ORCHARD), orchard_tree)); // Grab the consensus branch ID for this block and its parent diff --git a/src/primitives/orchard.h b/src/primitives/orchard.h index c09b429634a..c3cf7bebc37 100644 --- a/src/primitives/orchard.h +++ b/src/primitives/orchard.h @@ -10,7 +10,7 @@ #include #include -class OrchardMerkleTree; +class OrchardMerkleFrontier; /** * The Orchard component of a transaction. @@ -22,7 +22,7 @@ class OrchardBundle /// Memory is allocated by Rust. std::unique_ptr inner; - friend class OrchardMerkleTree; + friend class OrchardMerkleFrontier; public: OrchardBundle() : inner(nullptr, orchard_bundle_free) {} diff --git a/src/rust/include/rust/orchard/incremental_merkle_tree.h b/src/rust/include/rust/orchard/incremental_merkle_tree.h new file mode 100644 index 00000000000..3157ec855e5 --- /dev/null +++ b/src/rust/include/rust/orchard/incremental_merkle_tree.h @@ -0,0 +1,87 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_MERKLE_TREE_H +#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_MERKLE_TREE_H + +#include "rust/streams.h" +#include "rust/orchard.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SINSEMILLA_DIGEST_LEN 32U + +/// Pointer to an Orchard incremental merkle tree frontier +struct OrchardMerkleFrontierPtr; +typedef struct OrchardMerkleFrontierPtr OrchardMerkleFrontierPtr; + +// Create an empty Orchard Merkle frontier. +// +// Memory allocated to the resulting value must be manually freed. +OrchardMerkleFrontierPtr* orchard_merkle_frontier_empty(); + +// Clones the given Orchard Merkle frontier and returns +// a pointer to the newly created tree. Both the original +// tree's memory and the newly allocated one need to be freed +// independently. +OrchardMerkleFrontierPtr* orchard_merkle_frontier_clone( + const OrchardMerkleFrontierPtr* tree_ptr); + +// Free the memory allocated for the given Orchard Merkle frontier. +void orchard_merkle_frontier_free( + OrchardMerkleFrontierPtr* tree_ptr); + +// Parses an Orchard Merkle frontier from a stream. If parsing +// fails, this will return the null pointer. +// +// Memory allocated to the resulting value must be manually freed. +OrchardMerkleFrontierPtr* orchard_merkle_frontier_parse( + void* stream, + read_callback_t read_cb); + +// Serializes an Orchard Merkle frontier to a stream. +// +// Returns `false` if an error occurs while writing to the stream. +bool orchard_merkle_frontier_serialize( + const OrchardMerkleFrontierPtr* tree_ptr, + void* stream, + write_callback_t write_cb); + +// For each action in the provided bundle, append its +// commitment to the frontier. +// +// Returns `true` if the append succeeds, `false` if the +// tree is full. +bool orchard_merkle_frontier_append_bundle( + OrchardMerkleFrontierPtr* tree_ptr, + const OrchardBundlePtr* bundle); + +// Computes the root of the provided orchard Merkle frontier +void orchard_merkle_frontier_root( + const OrchardMerkleFrontierPtr* tree_ptr, + unsigned char* digest_ret); + +// The total number of leaves that have been appended to obtain +// the current state of the frontier. Subtract 1 from this value +// to obtain the position of the most recently appended leaf. +size_t orchard_merkle_frontier_num_leaves( + const OrchardMerkleFrontierPtr* tree_ptr); + +// Estimate the amount of memory consumed by the merkle frontier. +size_t orchard_merkle_frontier_dynamic_mem_usage( + const OrchardMerkleFrontierPtr* tree_ptr); + +// Computes the empty leaf value for the incremental Merkle tree. +void orchard_merkle_tree_empty_root( + unsigned char* digest_ret); + +#ifdef __cplusplus +} +#endif + +#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_MERKLE_TREE_H diff --git a/src/rust/include/rust/orchard/incremental_sinsemilla_tree.h b/src/rust/include/rust/orchard/incremental_sinsemilla_tree.h deleted file mode 100644 index 097c70703f2..00000000000 --- a/src/rust/include/rust/orchard/incremental_sinsemilla_tree.h +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2021 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_SINSEMILLA_TREE_H -#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_SINSEMILLA_TREE_H - -#include "rust/streams.h" -#include "rust/orchard.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SINSEMILLA_DIGEST_LEN 32U - -/// Pointer to an Orchard incremental merkle tree frontier -struct OrchardMerkleFrontierPtr; -typedef struct OrchardMerkleFrontierPtr OrchardMerkleFrontierPtr; - -// Create an empty Orchard Merkle frontier. -// -// Memory allocated to the resulting value must be manually freed. -OrchardMerkleFrontierPtr* orchard_merkle_frontier_empty(); - -// Clones the given Orchard Merkle frontier and returns -// a pointer to the newly created tree. Both the original -// tree's memory and the newly allocated one need to be freed -// independently. -OrchardMerkleFrontierPtr* orchard_merkle_frontier_clone( - const OrchardMerkleFrontierPtr* tree_ptr); - -// Free the memory allocated for the given Orchard Merkle frontier. -void orchard_merkle_frontier_free( - OrchardMerkleFrontierPtr* tree_ptr); - -// Parses an Orchard Merkle frontier from a stream. If parsing -// fails, this will return the null pointer. -// -// Memory allocated to the resulting value must be manually freed. -OrchardMerkleFrontierPtr* orchard_merkle_frontier_parse( - void* stream, - read_callback_t read_cb); - -// Serializes an Orchard Merkle frontier to a stream. -// -// Returns `false` if an error occurs while writing to the stream. -bool orchard_merkle_frontier_serialize( - const OrchardMerkleFrontierPtr* tree_ptr, - void* stream, - write_callback_t write_cb); - -// For each action in the provided bundle, append its -// commitment to the frontier. -// -// Returns `true` if the append succeeds, `false` if the -// tree is full. -bool orchard_merkle_frontier_append_bundle( - OrchardMerkleFrontierPtr* tree_ptr, - const OrchardBundlePtr* bundle); - -// Computes the root of the provided orchard Merkle frontier -void orchard_merkle_frontier_root( - const OrchardMerkleFrontierPtr* tree_ptr, - unsigned char* digest_ret); - -// The total number of leaves that have been appended to obtain -// the current state of the frontier. Subtract 1 from this value -// to obtain the position of the most recently appended leaf. -size_t orchard_merkle_frontier_num_leaves( - const OrchardMerkleFrontierPtr* tree_ptr); - -// Estimate the amount of memory consumed by the merkle frontier. -size_t orchard_merkle_frontier_dynamic_mem_usage( - const OrchardMerkleFrontierPtr* tree_ptr); - -/// Pointer to an Orchard incremental Sinsemilla tree -struct IncrementalSinsemillaTreePtr; -typedef struct IncrementalSinsemillaTreePtr IncrementalSinsemillaTreePtr; - -// Create an empty incremental Sinsemilla tree. -// -// Memory allocated to the resulting value must be manually freed. -IncrementalSinsemillaTreePtr* incremental_sinsemilla_tree_empty(); - -// Clones the given incremental Sinsemilla tree and returns -// a pointer to the newly created tree. Both the original -// tree's memory and the newly allocated one need to be freed -// independently. -IncrementalSinsemillaTreePtr* incremental_sinsemilla_tree_clone( - const IncrementalSinsemillaTreePtr* tree_ptr); - -// Free the memory allocated for the given incremental Sinsemilla tree. -void incremental_sinsemilla_tree_free( - IncrementalSinsemillaTreePtr* tree_ptr); - -// Parses an incremental Sinsemilla tree from a stream. If parsing -// fails, this will return the null pointer. -// -// Memory allocated to the resulting value must be manually freed. -IncrementalSinsemillaTreePtr* incremental_sinsemilla_tree_parse( - void* stream, - read_callback_t read_cb); - -// Serializes an incremental Sinsemilla tree to a stream. -// -// Returns false if an error occurs while writing to the stream. -bool incremental_sinsemilla_tree_serialize( - const IncrementalSinsemillaTreePtr* tree_ptr, - void* stream, - write_callback_t write_cb); - -// For each action in the provided bundle, append its -// commitment to the incremental Merkle tree. -// -// Returns `true` if the append succeeds, `false` if the -// tree is full. -bool incremental_sinsemilla_tree_append_bundle( - IncrementalSinsemillaTreePtr* tree_ptr, - const OrchardBundlePtr* bundle); - -// Save the current state of the incremental merkle tree -// as a point to which the tree can be rewound. -void incremental_sinsemilla_tree_checkpoint( - IncrementalSinsemillaTreePtr* tree_ptr); - -// Rewind the incremental merkle tree to the latest checkpoint. -// -// Returns `true` if the rewind succeeds, `false` if the attempted -// rewind would require the destruction of witness data. -bool incremental_sinsemilla_tree_rewind( - IncrementalSinsemillaTreePtr* tree_ptr); - -// Computes the root of the provided incremental Sinsemilla tree. -void incremental_sinsemilla_tree_root( - const IncrementalSinsemillaTreePtr* tree_ptr, - unsigned char* digest_ret); - -// Computes the empty leaf value for the incremental Sinsemilla tree. -void incremental_sinsemilla_tree_empty_root( - unsigned char* digest_ret); - -#ifdef __cplusplus -} -#endif - -#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_SINSEMILLA_TREE_H diff --git a/src/rust/src/incremental_sinsemilla_tree_ffi.rs b/src/rust/src/incremental_merkle_tree_ffi.rs similarity index 52% rename from src/rust/src/incremental_sinsemilla_tree_ffi.rs rename to src/rust/src/incremental_merkle_tree_ffi.rs index 37fc07c7872..aadbeda04e4 100644 --- a/src/rust/src/incremental_sinsemilla_tree_ffi.rs +++ b/src/rust/src/incremental_merkle_tree_ffi.rs @@ -1,21 +1,17 @@ -use incrementalmerkletree::{ - bridgetree::{self, BridgeTree}, - Altitude, Frontier, Hashable, Tree, -}; +use incrementalmerkletree::{bridgetree, Altitude, Frontier, Hashable}; use std::mem::size_of_val; use std::ptr; use orchard::{bundle::Authorized, tree::MerkleHashOrchard}; use tracing::error; use zcash_primitives::{ - merkle_tree::incremental::{read_frontier_v1, read_tree, write_frontier_v1, write_tree}, + merkle_tree::incremental::{read_frontier_v1, write_frontier_v1}, transaction::components::Amount, }; use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}; pub const MERKLE_DEPTH: u8 = 32; -pub const MAX_CHECKPOINTS: usize = 100; // // Operations on Merkle frontiers. @@ -148,139 +144,8 @@ pub extern "C" fn orchard_merkle_frontier_dynamic_mem_usage( size_of_val(tree) + tree.dynamic_memory_usage() } -// -// Operations on incremental merkle trees with interstitial -// witnesses. -// - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_empty( -) -> *mut BridgeTree { - let empty_tree = BridgeTree::::new(MAX_CHECKPOINTS); - Box::into_raw(Box::new(empty_tree)) -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_clone( - tree: *const BridgeTree, -) -> *mut BridgeTree { - unsafe { tree.as_ref() } - .map(|tree| Box::into_raw(Box::new(tree.clone()))) - .unwrap_or(std::ptr::null_mut()) -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_free( - tree: *mut BridgeTree, -) { - if !tree.is_null() { - drop(unsafe { Box::from_raw(tree) }); - } -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_parse( - stream: Option, - read_cb: Option, -) -> *mut BridgeTree { - let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); - - match read_tree(reader) { - Ok(parsed) => Box::into_raw(Box::new(parsed)), - Err(e) => { - error!("Failed to parse Orchard bundle: {}", e); - ptr::null_mut() - } - } -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_serialize( - tree: *const BridgeTree, - stream: Option, - write_cb: Option, -) -> bool { - let tree = unsafe { - tree.as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - let writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); - match write_tree(writer, tree) { - Ok(()) => true, - Err(e) => { - error!("{}", e); - false - } - } -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_append_bundle( - tree: *mut BridgeTree, - bundle: *const orchard::Bundle, -) -> bool { - let tree = unsafe { - tree.as_mut() - .expect("Orchard note commitment tree pointer may not be null.") - }; - if let Some(bundle) = unsafe { bundle.as_ref() } { - for action in bundle.actions().iter() { - if !tree.append(&MerkleHashOrchard::from_cmx(action.cmx())) { - error!("Orchard note commitment tree is full."); - return false; - } - } - } - - true -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_checkpoint( - tree: *mut BridgeTree, -) { - let tree = unsafe { - tree.as_mut() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - tree.checkpoint() -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_rewind( - tree: *mut BridgeTree, -) -> bool { - let tree = unsafe { - tree.as_mut() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - tree.rewind() -} - -#[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_root( - tree: *const BridgeTree, - root_ret: *mut [u8; 32], -) { - let tree = unsafe { - tree.as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - let root_ret = unsafe { - root_ret - .as_mut() - .expect("Cannot return to the null pointer.") - }; - - *root_ret = tree.root().to_bytes(); -} - #[no_mangle] -pub extern "C" fn incremental_sinsemilla_tree_empty_root(root_ret: *mut [u8; 32]) { +pub extern "C" fn orchard_merkle_tree_empty_root(root_ret: *mut [u8; 32]) { let root_ret = unsafe { root_ret .as_mut() diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index e4879d0c40e..399e806d571 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -70,7 +70,7 @@ mod tracing_ffi; mod address_ffi; mod history_ffi; -mod incremental_sinsemilla_tree_ffi; +mod incremental_merkle_tree_ffi; mod orchard_ffi; mod transaction_ffi; mod zip339_ffi; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 91dc364c4b4..fd3a51aa58b 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -32,7 +32,7 @@ class CCoinsViewTest : public CCoinsView std::map map_; std::map mapSproutAnchors_; std::map mapSaplingAnchors_; - std::map mapOrchardAnchors_; + std::map mapOrchardAnchors_; std::map mapSproutNullifiers_; std::map mapSaplingNullifiers_; std::map mapOrchardNullifiers_; @@ -41,7 +41,7 @@ class CCoinsViewTest : public CCoinsView CCoinsViewTest() { hashBestSproutAnchor_ = SproutMerkleTree::empty_root(); hashBestSaplingAnchor_ = SaplingMerkleTree::empty_root(); - hashBestOrchardAnchor_ = OrchardMerkleTree::empty_root(); + hashBestOrchardAnchor_ = OrchardMerkleFrontier::empty_root(); } bool GetSproutAnchorAt(const uint256& rt, SproutMerkleTree &tree) const { @@ -76,14 +76,14 @@ class CCoinsViewTest : public CCoinsView } } - bool GetOrchardAnchorAt(const uint256& rt, OrchardMerkleTree &tree) const { - if (rt == OrchardMerkleTree::empty_root()) { - OrchardMerkleTree new_tree; + bool GetOrchardAnchorAt(const uint256& rt, OrchardMerkleFrontier &tree) const { + if (rt == OrchardMerkleFrontier::empty_root()) { + OrchardMerkleFrontier new_tree; tree = new_tree; return true; } - std::map::const_iterator it = mapOrchardAnchors_.find(rt); + std::map::const_iterator it = mapOrchardAnchors_.find(rt); if (it == mapOrchardAnchors_.end()) { return false; } else { @@ -217,7 +217,7 @@ class CCoinsViewTest : public CCoinsView BatchWriteAnchors(mapSproutAnchors, mapSproutAnchors_); BatchWriteAnchors(mapSaplingAnchors, mapSaplingAnchors_); - BatchWriteAnchors(mapOrchardAnchors, mapOrchardAnchors_); + BatchWriteAnchors(mapOrchardAnchors, mapOrchardAnchors_); BatchWriteNullifiers(mapSproutNullifiers, mapSproutNullifiers_); BatchWriteNullifiers(mapSaplingNullifiers, mapSaplingNullifiers_); diff --git a/src/txdb.cpp b/src/txdb.cpp index 94db4f95594..965a9adf4d6 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -81,9 +81,9 @@ bool CCoinsViewDB::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree return read; } -bool CCoinsViewDB::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const { - if (rt == OrchardMerkleTree::empty_root()) { - OrchardMerkleTree new_tree; +bool CCoinsViewDB::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { + if (rt == OrchardMerkleFrontier::empty_root()) { + OrchardMerkleFrontier new_tree; tree = new_tree; return true; } @@ -141,7 +141,7 @@ uint256 CCoinsViewDB::GetBestAnchor(ShieldedType type) const { break; case ORCHARD: if (!db.Read(DB_BEST_ORCHARD_ANCHOR, hashBestAnchor)) - return OrchardMerkleTree::empty_root(); + return OrchardMerkleFrontier::empty_root(); break; default: throw runtime_error("Unknown shielded type"); @@ -291,7 +291,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, ::BatchWriteAnchors(batch, mapSproutAnchors, DB_SPROUT_ANCHOR); ::BatchWriteAnchors(batch, mapSaplingAnchors, DB_SAPLING_ANCHOR); - ::BatchWriteAnchors(batch, mapOrchardAnchors, DB_ORCHARD_ANCHOR); + ::BatchWriteAnchors(batch, mapOrchardAnchors, DB_ORCHARD_ANCHOR); ::BatchWriteNullifiers(batch, mapSproutNullifiers, DB_NULLIFIER); ::BatchWriteNullifiers(batch, mapSaplingNullifiers, DB_SAPLING_NULLIFIER); diff --git a/src/txdb.h b/src/txdb.h index 00015010337..7d51c0a759a 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -83,7 +83,7 @@ class CCoinsViewDB : public CCoinsView bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; - bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const; + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const; bool GetNullifier(const uint256 &nf, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; diff --git a/src/zcash/IncrementalMerkleTree.hpp b/src/zcash/IncrementalMerkleTree.hpp index 3116da42be7..5c6d51899ec 100644 --- a/src/zcash/IncrementalMerkleTree.hpp +++ b/src/zcash/IncrementalMerkleTree.hpp @@ -12,7 +12,7 @@ #include "zcash/util.h" #include -#include +#include namespace libzcash { @@ -259,28 +259,28 @@ typedef libzcash::IncrementalMerkleTree SaplingWitness; typedef libzcash::IncrementalWitness SaplingTestingWitness; -class OrchardMerkleTree +class OrchardMerkleFrontier { private: /// An incremental Sinsemilla tree; this pointer may never be null. /// Memory is allocated by Rust. std::unique_ptr inner; public: - OrchardMerkleTree() : inner(orchard_merkle_frontier_empty(), orchard_merkle_frontier_free) {} + OrchardMerkleFrontier() : inner(orchard_merkle_frontier_empty(), orchard_merkle_frontier_free) {} - OrchardMerkleTree(OrchardMerkleTree&& frontier) : inner(std::move(frontier.inner)) {} + OrchardMerkleFrontier(OrchardMerkleFrontier&& frontier) : inner(std::move(frontier.inner)) {} - OrchardMerkleTree(const OrchardMerkleTree& frontier) : + OrchardMerkleFrontier(const OrchardMerkleFrontier& frontier) : inner(orchard_merkle_frontier_clone(frontier.inner.get()), orchard_merkle_frontier_free) {} - OrchardMerkleTree& operator=(OrchardMerkleTree&& frontier) + OrchardMerkleFrontier& operator=(OrchardMerkleFrontier&& frontier) { if (this != &frontier) { inner = std::move(frontier.inner); } return *this; } - OrchardMerkleTree& operator=(const OrchardMerkleTree& frontier) + OrchardMerkleFrontier& operator=(const OrchardMerkleFrontier& frontier) { if (this != &frontier) { inner.reset(orchard_merkle_frontier_clone(frontier.inner.get())); @@ -323,7 +323,7 @@ class OrchardMerkleTree static uint256 empty_root() { uint256 value; - incremental_sinsemilla_tree_empty_root(value.begin()); + orchard_merkle_tree_empty_root(value.begin()); return value; } }; diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 7a864af7e59..6a03222602a 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -448,7 +448,7 @@ class FakeCoinsViewDB : public CCoinsView { uint256 hash; SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; - OrchardMerkleTree orchardTree; + OrchardMerkleFrontier orchardTree; public: FakeCoinsViewDB(std::string dbName, uint256& hash) : db(GetDataDir() / dbName, 100, false, false), hash(hash) {} @@ -469,7 +469,7 @@ class FakeCoinsViewDB : public CCoinsView { return false; } - bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleTree &tree) const { + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { if (rt == orchardTree.root()) { tree = orchardTree; return true; From 8f5591a9c1e536bb9b5ab8ba5952d11e660ba53b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 18 Nov 2021 17:40:20 -0700 Subject: [PATCH 143/514] Add libzcash::AccountId type. --- src/wallet/wallet.cpp | 2 +- src/wallet/wallet.h | 2 +- src/zcash/address/zip32.cpp | 8 ++++---- src/zcash/address/zip32.h | 14 ++++++++------ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bbd7b7a3afe..91cd9ed9b3b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -431,7 +431,7 @@ ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { } } -std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) { +std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { auto seed = GetMnemonicSeed(); if (!seed.has_value()) { throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed."); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 35920fb8dad..3bb5c55d143 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1097,7 +1097,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * Unified keys & addresses */ libzcash::ZcashdUnifiedSpendingKey GenerateNewUnifiedSpendingKey(); - std::optional GenerateUnifiedSpendingKeyForAccount(uint32_t accountId); + std::optional GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); /** * Increment the next transaction order id diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 82ae79ac15e..f88311fdecd 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -85,7 +85,7 @@ std::optional diversifier_index_t::ToTransparentChildIndex() const // Transparent // -std::optional> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { auto rawSeed = seed.RawSeed(); auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); @@ -110,7 +110,7 @@ std::optional> DeriveBip44TransparentAccountKey(co std::optional Bip44AccountChains::ForAccount( const MnemonicSeed& seed, uint32_t bip44CoinType, - uint32_t accountId) { + libzcash::AccountId accountId) { auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); if (!accountKeyOpt.has_value()) return std::nullopt; @@ -251,7 +251,7 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const return xsk_i; } -std::pair SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::pair SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/account' @@ -313,7 +313,7 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const // Unified // -std::optional> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) { +std::optional> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { ZcashdUnifiedSpendingKey usk; usk.accountId = accountId; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 83bbc3443d4..9da16b71e9a 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -165,6 +165,8 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed); namespace libzcash { +typedef uint32_t AccountId; + /** * 88-bit diversifier index. This would ideally derive from base_uint * but those values must have bit widths that are multiples of 32. @@ -290,7 +292,7 @@ struct SaplingExtendedSpendingKey { } static SaplingExtendedSpendingKey Master(const HDSeed& seed); - static std::pair ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId); + static std::pair ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); static std::pair Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); @@ -363,7 +365,7 @@ class ZcashdUnifiedFullViewingKey { class ZcashdUnifiedSpendingKey { private: - uint32_t accountId; + libzcash::AccountId accountId; std::optional transparentKey; std::optional saplingKey; @@ -372,7 +374,7 @@ class ZcashdUnifiedSpendingKey { static std::optional> ForAccount( const MnemonicSeed& mnemonic, uint32_t bip44CoinType, - uint32_t accountId); + libzcash::AccountId accountId); const std::optional& GetTransparentKey() const { return transparentKey; @@ -390,18 +392,18 @@ std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t co class Bip44AccountChains { private: uint256 seedFp; - uint32_t accountId; + libzcash::AccountId accountId; uint32_t bip44CoinType; CExtKey external; CExtKey internal; - Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, uint32_t accountIdIn, CExtKey externalIn, CExtKey internalIn): + Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, libzcash::AccountId accountIdIn, CExtKey externalIn, CExtKey internalIn): seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} public: static std::optional ForAccount( const MnemonicSeed& mnemonic, uint32_t bip44CoinType, - uint32_t accountId); + libzcash::AccountId accountId); std::optional> DeriveExternal(uint32_t addrIndex); std::optional> DeriveInternal(uint32_t addrIndex); From 59a4c84112e8155af789bc85e81ac657d94828fe Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 19 Jul 2021 16:06:52 -0600 Subject: [PATCH 144/514] Adds Orchard Address, IncomingViewingKey, FullViewingKey, and SpendingKey types. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/Makefile.am | 2 + src/Makefile.gtest.include | 3 +- src/rust/include/rust/orchard/keys.h | 216 +++++++++++++++++ src/rust/src/orchard_keys_ffi.rs | 304 ++++++++++++++++++++++++ src/rust/src/rustzcash.rs | 1 + src/wallet/gtest/test_orchard_zkeys.cpp | 46 ++++ src/zcash/Address.hpp | 1 + src/zcash/address/orchard.cpp | 40 ++++ src/zcash/address/orchard.hpp | 262 ++++++++++++++++++++ 11 files changed, 876 insertions(+), 3 deletions(-) create mode 100644 src/rust/include/rust/orchard/keys.h create mode 100644 src/rust/src/orchard_keys_ffi.rs create mode 100644 src/wallet/gtest/test_orchard_zkeys.cpp create mode 100644 src/zcash/address/orchard.cpp create mode 100644 src/zcash/address/orchard.hpp diff --git a/Cargo.lock b/Cargo.lock index 3e8b58b51cf..4b0bf6cf68f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,7 +1112,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.0.0" -source = "git+https://github.com/zcash/orchard.git?rev=2c8241f25b943aa05203eacf9905db117c69bd29#2c8241f25b943aa05203eacf9905db117c69bd29" +source = "git+https://github.com/nuttycom/orchard.git?rev=14c4b40dfc2368b904ee157bc2aeaa7a2c90cb86#14c4b40dfc2368b904ee157bc2aeaa7a2c90cb86" dependencies = [ "aes", "arrayvec 0.7.2", diff --git a/Cargo.toml b/Cargo.toml index 105054eaa76..4ddf0aeefc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ codegen-units = 1 [patch.crates-io] ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } +orchard = { git = "https://github.com/nuttycom/orchard.git", rev = "14c4b40dfc2368b904ee157bc2aeaa7a2c90cb86" } zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } diff --git a/src/Makefile.am b/src/Makefile.am index 9477cd0338d..aa3213ff6af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -109,6 +109,7 @@ LIBZCASH_H = \ zcash/Address.hpp \ zcash/address/sapling.hpp \ zcash/address/sprout.hpp \ + zcash/address/orchard.h \ zcash/address/zip32.h \ zcash/History.hpp \ zcash/JoinSplit.hpp \ @@ -537,6 +538,7 @@ libzcash_a_SOURCES = \ zcash/Address.cpp \ zcash/address/sapling.cpp \ zcash/address/sprout.cpp \ + zcash/address/orchard.cpp \ zcash/address/zip32.cpp \ zcash/History.cpp \ zcash/JoinSplit.cpp \ diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 7c088f2fa7c..fe41abc67b6 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -21,7 +21,8 @@ zcash_gtest_SOURCES = \ # depend on global state (see #1539) if ENABLE_WALLET zcash_gtest_SOURCES += \ - wallet/gtest/test_wallet_zkeys.cpp + wallet/gtest/test_wallet_zkeys.cpp \ + wallet/gtest/test_orchard_zkeys.cpp endif zcash_gtest_SOURCES += \ test/data/merkle_roots_orchard.h \ diff --git a/src/rust/include/rust/orchard/keys.h b/src/rust/include/rust/orchard/keys.h new file mode 100644 index 00000000000..07d91aca4b9 --- /dev/null +++ b/src/rust/include/rust/orchard/keys.h @@ -0,0 +1,216 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_RUST_INCLUDE_RUST_ORCHARD_KEYS_H +#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_KEYS_H + +#include "rust/streams.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A pointer to an Orchard shielded payment address, as defined in + * https://zips.z.cash/protocol/nu5.pdf#orchardpaymentaddrencoding + */ +struct OrchardRawAddressPtr; +typedef struct OrchardRawAddressPtr OrchardRawAddressPtr; + +/** + * Clones the given Orchard payment address and returns + * a pointer to the newly created value. Both the original + * one's memory and the newly allocated one need to be freed + * independently. + */ +OrchardRawAddressPtr* orchard_address_clone( + const OrchardRawAddressPtr* ptr); + +/** + * Frees the memory allocated for the given Orchard payment address + */ +void orchard_address_free(OrchardRawAddressPtr* ptr); + +// +// Incoming Viewing Keys +// + +struct OrchardIncomingViewingKeyPtr; +typedef struct OrchardIncomingViewingKeyPtr OrchardIncomingViewingKeyPtr; + +/** + * Clones the given Orchard incoming viewing key and returns + * a pointer to the newly created value. Both the original + * one's memory and the newly allocated one need to be freed + * independently. + */ +OrchardIncomingViewingKeyPtr* orchard_incoming_viewing_key_clone( + const OrchardIncomingViewingKeyPtr* ptr); + +/** + * Frees the memory allocated for the given Orchard incoming viewing key + */ +void orchard_incoming_viewing_key_free(OrchardIncomingViewingKeyPtr* ptr); + +/** + * Returns the address at the specified diversifier index. + */ +OrchardRawAddressPtr* orchard_incoming_viewing_key_to_address( + const OrchardIncomingViewingKeyPtr* incoming_viewing_key, + const unsigned char* j); + +/** + * Parses an Orchard incoming viewing key from the given stream. + * + * - If the key does not parse correctly, the returned pointer will be null. + */ +OrchardIncomingViewingKeyPtr* orchard_incoming_viewing_key_parse( + void* stream, + read_callback_t read_cb); + +/** + * Serializes an Orchard incoming viewing key to the given stream + * + * If `incoming_viewing_key == nullptr`, this serializes `nActionsOrchard = 0`. + */ +bool orchard_incoming_viewing_key_serialize( + const OrchardIncomingViewingKeyPtr* incoming_viewing_key, + void* stream, + write_callback_t write_cb); + +/** + * Implements the "less than" operation for comparing two keys. + */ +bool orchard_incoming_viewing_key_lt( + const OrchardIncomingViewingKeyPtr* k0, + const OrchardIncomingViewingKeyPtr* k1); + +/** + * Implements equality testing between incoming viewing keys. + */ +bool orchard_incoming_viewing_key_eq( + const OrchardIncomingViewingKeyPtr* k0, + const OrchardIncomingViewingKeyPtr* k1); + +// +// Full Viewing Keys +// + +struct OrchardFullViewingKeyPtr; +typedef struct OrchardFullViewingKeyPtr OrchardFullViewingKeyPtr; + +/** + * Clones the given Orchard full viewing key and returns + * a pointer to the newly created value. Both the original + * one's memory and the newly allocated one need to be freed + * independently. + */ +OrchardFullViewingKeyPtr* orchard_full_viewing_key_clone( + const OrchardFullViewingKeyPtr* ptr); + +/** + * Free the memory allocated for the given Orchard full viewing key + */ +void orchard_full_viewing_key_free(OrchardFullViewingKeyPtr* ptr); + +/** + * Parses an Orchard full viewing key from the given stream. + * + * - If the key does not parse correctly, the returned pointer will be null. + */ +OrchardFullViewingKeyPtr* orchard_full_viewing_key_parse( + void* stream, + read_callback_t read_cb); + +/** + * Serializes an Orchard full viewing key to the given stream + * + * If `full_viewing_key == nullptr`, this serializes `nActionsOrchard = 0`. + */ +bool orchard_full_viewing_key_serialize( + const OrchardFullViewingKeyPtr* full_viewing_key, + void* stream, + write_callback_t write_cb); + +/** + * Returns the incoming viewing key for the specified full viewing key. + */ +OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_incoming_viewing_key( + const OrchardFullViewingKeyPtr* key); + +/** + * Implements equality testing between full viewing keys. + */ +bool orchard_full_viewing_key_eq( + const OrchardFullViewingKeyPtr* k0, + const OrchardFullViewingKeyPtr* k1); + +// +// Spending Keys +// + +struct OrchardSpendingKeyPtr; +typedef struct OrchardSpendingKeyPtr OrchardSpendingKeyPtr; + +/** + * Constructs a spending key by ZIP-32 derivation to the account + * level from the provided seed. + */ +OrchardSpendingKeyPtr* orchard_spending_key_for_account( + const unsigned char* seed, + size_t seed_len, + uint32_t bip44_coin_type, + uint32_t account_id); + +/** + * Clones the given Orchard spending key and returns + * a pointer to the newly created value. Both the original + * one's memory and the newly allocated one need to be freed + * independently. + */ +OrchardSpendingKeyPtr* orchard_spending_key_clone( + const OrchardSpendingKeyPtr* ptr); + +/** + * Free the memory allocated for the given Orchard spending key + */ +void orchard_spending_key_free(OrchardSpendingKeyPtr* ptr); + +/** + * Parses an Orchard spending key from the given stream. + * + * - If the key does not parse correctly, the returned pointer will be null. + */ +OrchardSpendingKeyPtr* orchard_spending_key_parse( + void* stream, + read_callback_t read_cb); + +/** + * Serializes an Orchard spending key to the given stream + * + * If `spending_key == nullptr`, this will return `false` + */ +bool orchard_spending_key_serialize( + const OrchardSpendingKeyPtr* spending_key, + void* stream, + write_callback_t write_cb); + +/** + * Returns the full viewing key for the specified spending key. + */ +OrchardFullViewingKeyPtr* orchard_spending_key_to_full_viewing_key( + const OrchardSpendingKeyPtr* key); + +/** + * Implements equality testing between spending keys. + */ +bool orchard_spending_key_eq( + const OrchardSpendingKeyPtr* k0, + const OrchardSpendingKeyPtr* k1); + +#ifdef __cplusplus +} +#endif + +#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_KEYS_H diff --git a/src/rust/src/orchard_keys_ffi.rs b/src/rust/src/orchard_keys_ffi.rs new file mode 100644 index 00000000000..efd4e77d1d3 --- /dev/null +++ b/src/rust/src/orchard_keys_ffi.rs @@ -0,0 +1,304 @@ +use std::io::{Read, Write}; +use std::slice; +use tracing::error; + +use orchard::keys::{DiversifierIndex, FullViewingKey, IncomingViewingKey, SpendingKey}; +use orchard::Address; + +use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}; + +// +// Addresses +// + +#[no_mangle] +pub extern "C" fn orchard_address_clone(addr: *const Address) -> *mut Address { + unsafe { addr.as_ref() } + .map(|addr| Box::into_raw(Box::new(*addr))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_address_free(addr: *mut Address) { + if !addr.is_null() { + drop(unsafe { Box::from_raw(addr) }); + } +} + +// +// Incoming viewing keys +// + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_clone( + key: *const IncomingViewingKey, +) -> *mut IncomingViewingKey { + unsafe { key.as_ref() } + .map(|key| Box::into_raw(Box::new(key.clone()))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_free(key: *mut IncomingViewingKey) { + if !key.is_null() { + drop(unsafe { Box::from_raw(key) }); + } +} + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_parse( + stream: Option, + read_cb: Option, +) -> *mut IncomingViewingKey { + let mut reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); + + let mut buf = [0u8; 64]; + match reader.read_exact(&mut buf) { + Err(e) => { + error!( + "Stream failure reading bytes of Orchard incoming viewing key: {}", + e + ); + std::ptr::null_mut() + } + Ok(()) => { + let read = IncomingViewingKey::from_bytes(&buf); + if read.is_some().into() { + Box::into_raw(Box::new(read.unwrap())) + } else { + error!("Failed to parse Orchard incoming viewing key."); + std::ptr::null_mut() + } + } + } +} + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_to_address( + key: *const IncomingViewingKey, + diversifier_index: *const [u8; 11], +) -> *mut Address { + let key = unsafe { + key.as_ref() + .expect("Orchard incoming viewing key pointer may not be null.") + }; + + let diversifier_index = unsafe { DiversifierIndex::from(*diversifier_index) }; + Box::into_raw(Box::new(key.address_at(diversifier_index))) +} + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_serialize( + key: *const IncomingViewingKey, + stream: Option, + write_cb: Option, +) -> bool { + let key = unsafe { + key.as_ref() + .expect("Orchard incoming viewing key pointer may not be null.") + }; + + let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); + match writer.write_all(&key.to_bytes()) { + Ok(()) => true, + Err(e) => { + error!("Stream failure writing Orchard incoming viewing key: {}", e); + false + } + } +} + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_lt( + k0: *const IncomingViewingKey, + k1: *const IncomingViewingKey, +) -> bool { + let k0 = unsafe { k0.as_ref() }; + let k1 = unsafe { k1.as_ref() }; + k0 < k1 +} + +#[no_mangle] +pub extern "C" fn orchard_incoming_viewing_key_eq( + k0: *const IncomingViewingKey, + k1: *const IncomingViewingKey, +) -> bool { + let k0 = unsafe { k0.as_ref() }; + let k1 = unsafe { k1.as_ref() }; + k0 == k1 +} + +// +// Full viewing keys +// + +#[no_mangle] +pub extern "C" fn orchard_full_viewing_key_clone( + key: *const FullViewingKey, +) -> *mut FullViewingKey { + unsafe { key.as_ref() } + .map(|key| Box::into_raw(Box::new(key.clone()))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_full_viewing_key_free(key: *mut FullViewingKey) { + if !key.is_null() { + drop(unsafe { Box::from_raw(key) }); + } +} + +#[no_mangle] +pub extern "C" fn orchard_full_viewing_key_parse( + stream: Option, + read_cb: Option, +) -> *mut FullViewingKey { + let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); + + match FullViewingKey::read(reader) { + Err(e) => { + error!( + "Stream failure reading bytes of Orchard full viewing key: {}", + e + ); + std::ptr::null_mut() + } + Ok(fvk) => Box::into_raw(Box::new(fvk)), + } +} + +#[no_mangle] +pub extern "C" fn orchard_full_viewing_key_serialize( + key: *const FullViewingKey, + stream: Option, + write_cb: Option, +) -> bool { + let key = unsafe { + key.as_ref() + .expect("Orchard full viewing key pointer may not be null.") + }; + + let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); + match key.write(&mut writer) { + Ok(()) => true, + Err(e) => { + error!("Stream failure writing Orchard full viewing key: {}", e); + false + } + } +} + +#[no_mangle] +pub extern "C" fn orchard_full_viewing_key_to_incoming_viewing_key( + key: *const FullViewingKey, +) -> *mut IncomingViewingKey { + unsafe { key.as_ref() } + .map(|key| Box::into_raw(Box::new(IncomingViewingKey::from(key)))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_full_viewing_key_eq( + k0: *const FullViewingKey, + k1: *const FullViewingKey, +) -> bool { + let k0 = unsafe { k0.as_ref() }; + let k1 = unsafe { k1.as_ref() }; + k0 == k1 +} + +// +// Spending keys +// + +#[no_mangle] +pub extern "C" fn orchard_spending_key_for_account( + seed: *const u8, + seed_len: usize, + bip44_coin_type: u32, + account_id: u32, +) -> *mut SpendingKey { + let seed = unsafe { slice::from_raw_parts(seed, seed_len) }; + SpendingKey::from_zip32_seed(seed, bip44_coin_type, account_id) + .map(|key| Box::into_raw(Box::new(key))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_spending_key_clone(key: *const SpendingKey) -> *mut SpendingKey { + unsafe { key.as_ref() } + .map(|key| Box::into_raw(Box::new(*key))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_spending_key_free(key: *mut SpendingKey) { + if !key.is_null() { + drop(unsafe { Box::from_raw(key) }); + } +} + +#[no_mangle] +pub extern "C" fn orchard_spending_key_parse( + stream: Option, + read_cb: Option, +) -> *mut SpendingKey { + let mut reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); + + let mut buf = [0u8; 32]; + match reader.read_exact(&mut buf) { + Err(e) => { + error!( + "Stream failure reading bytes of Orchard spending key: {}", + e + ); + std::ptr::null_mut() + } + Ok(()) => { + let sk_opt = SpendingKey::from_bytes(buf); + if sk_opt.is_some().into() { + Box::into_raw(Box::new(sk_opt.unwrap())) + } else { + error!("Failed to parse Orchard spending key."); + std::ptr::null_mut() + } + } + } +} + +#[no_mangle] +pub extern "C" fn orchard_spending_key_serialize( + key: *const SpendingKey, + stream: Option, + write_cb: Option, +) -> bool { + let key = unsafe { + key.as_ref() + .expect("Orchard spending key pointer may not be null.") + }; + + let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); + match writer.write_all(key.to_bytes()) { + Ok(()) => true, + Err(e) => { + error!("Stream failure writing Orchard spending key: {}", e); + false + } + } +} + +#[no_mangle] +pub extern "C" fn orchard_spending_key_to_full_viewing_key( + key: *const SpendingKey, +) -> *mut FullViewingKey { + unsafe { key.as_ref() } + .map(|key| Box::into_raw(Box::new(FullViewingKey::from(key)))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn orchard_spending_key_eq(k0: *const SpendingKey, k1: *const SpendingKey) -> bool { + let k0 = unsafe { k0.as_ref() }; + let k1 = unsafe { k1.as_ref() }; + k0 == k1 +} diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index 2119abd3896..3520008897d 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -72,6 +72,7 @@ mod address_ffi; mod history_ffi; mod incremental_merkle_tree_ffi; mod orchard_ffi; +mod orchard_keys_ffi; mod transaction_ffi; mod zip339_ffi; diff --git a/src/wallet/gtest/test_orchard_zkeys.cpp b/src/wallet/gtest/test_orchard_zkeys.cpp new file mode 100644 index 00000000000..1ae284682ae --- /dev/null +++ b/src/wallet/gtest/test_orchard_zkeys.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include + +#include "zcash/address/orchard.hpp" + +TEST(OrchardZkeysTest, IVKSerializationRoundtrip) { + auto seed = MnemonicSeed::Random(1); //testnet coin type + auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0); + auto fvk = sk.ToFullViewingKey(); + auto ivk = fvk.ToIncomingViewingKey(); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << ivk; + + auto ivk0 = libzcash::OrchardIncomingViewingKey::Read(ss); + + ASSERT_EQ(ivk, ivk0); +} + +TEST(OrchardZkeysTest, FVKSerializationRoundtrip) { + auto seed = MnemonicSeed::Random(1); //testnet coin type + auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0); + auto fvk = sk.ToFullViewingKey(); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << fvk; + + auto fvk0 = libzcash::OrchardFullViewingKey::Read(ss); + + ASSERT_EQ(fvk, fvk0); +} + +TEST(OrchardZkeysTest, SKSerializationRoundtrip) { + auto seed = MnemonicSeed::Random(1); //testnet coin type + auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << sk; + + auto sk0 = libzcash::OrchardSpendingKey::Read(ss); + + ASSERT_EQ(sk, sk0); +} diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 38d416ee2e9..e41272a9076 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -2,6 +2,7 @@ #define ZC_ADDRESS_H_ #include "uint256.h" +#include "zcash/address/orchard.hpp" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" #include "zcash/address/zip32.h" diff --git a/src/zcash/address/orchard.cpp b/src/zcash/address/orchard.cpp new file mode 100644 index 00000000000..ada6103a2c7 --- /dev/null +++ b/src/zcash/address/orchard.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "zcash/address/orchard.hpp" + +namespace libzcash { + +OrchardRawAddress OrchardIncomingViewingKey::Address(const diversifier_index_t& j) const { + return OrchardRawAddress(orchard_incoming_viewing_key_to_address(inner.get(), j.begin())); +} + +OrchardIncomingViewingKey OrchardFullViewingKey::ToIncomingViewingKey() const { + return OrchardIncomingViewingKey(orchard_full_viewing_key_to_incoming_viewing_key(inner.get())); +} + +OrchardSpendingKey OrchardSpendingKey::ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + libzcash::AccountId accountId) { + + auto ptr = orchard_spending_key_for_account( + seed.RawSeed().data(), + seed.RawSeed().size(), + bip44CoinType, + accountId); + + if (ptr == nullptr) { + throw std::ios_base::failure("Unable to generate Orchard extended spending key."); + } + + return OrchardSpendingKey(ptr); +} + +OrchardFullViewingKey OrchardSpendingKey::ToFullViewingKey() const { + return OrchardFullViewingKey(orchard_spending_key_to_full_viewing_key(inner.get())); +} + +} //namespace libzcash + diff --git a/src/zcash/address/orchard.hpp b/src/zcash/address/orchard.hpp new file mode 100644 index 00000000000..9ba498d09a1 --- /dev/null +++ b/src/zcash/address/orchard.hpp @@ -0,0 +1,262 @@ +// Copyright (c) 2021 The Zcash Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ADDRESS_ORCHARD_H +#define ZCASH_ADDRESS_ORCHARD_H + +#include "streams.h" +#include "zcash/address/zip32.h" +#include + +namespace libzcash { + +class OrchardIncomingViewingKey; + +class OrchardRawAddress +{ +private: + std::unique_ptr inner; + + OrchardRawAddress() : inner(nullptr, orchard_address_free) {} + + OrchardRawAddress(OrchardRawAddressPtr* ptr) : inner(ptr, orchard_address_free) {} + + friend class OrchardIncomingViewingKey; +public: + OrchardRawAddress(OrchardRawAddress&& key) : inner(std::move(key.inner)) {} + + OrchardRawAddress(const OrchardRawAddress& key) : + inner(orchard_address_clone(key.inner.get()), orchard_address_free) {} + + OrchardRawAddress& operator=(OrchardRawAddress&& key) + { + if (this != &key) { + inner = std::move(key.inner); + } + return *this; + } + + OrchardRawAddress& operator=(const OrchardRawAddress& key) + { + if (this != &key) { + inner.reset(orchard_address_clone(key.inner.get())); + } + return *this; + } +}; + +class OrchardFullViewingKey; + +class OrchardIncomingViewingKey +{ +private: + std::unique_ptr inner; + + OrchardIncomingViewingKey() : inner(nullptr, orchard_incoming_viewing_key_free) {} + + OrchardIncomingViewingKey(OrchardIncomingViewingKeyPtr* key) : + inner(key, orchard_incoming_viewing_key_free) {} + + friend class OrchardFullViewingKey; +public: + + OrchardIncomingViewingKey(OrchardIncomingViewingKey&& key) : inner(std::move(key.inner)) {} + + OrchardIncomingViewingKey(const OrchardIncomingViewingKey& key) : + inner(orchard_incoming_viewing_key_clone(key.inner.get()), orchard_incoming_viewing_key_free) {} + + OrchardRawAddress Address(const diversifier_index_t& j) const; + + OrchardIncomingViewingKey& operator=(OrchardIncomingViewingKey&& key) + { + if (this != &key) { + inner = std::move(key.inner); + } + return *this; + } + + OrchardIncomingViewingKey& operator=(const OrchardIncomingViewingKey& key) + { + if (this != &key) { + inner.reset(orchard_incoming_viewing_key_clone(key.inner.get())); + } + return *this; + } + + friend bool operator==(const OrchardIncomingViewingKey& a, const OrchardIncomingViewingKey& b) + { + return orchard_incoming_viewing_key_eq(a.inner.get(), b.inner.get()); + } + + friend bool operator<(const OrchardIncomingViewingKey& c1, const OrchardIncomingViewingKey& c2) { + return orchard_incoming_viewing_key_lt(c1.inner.get(), c2.inner.get()); + } + + template + void Serialize(Stream& s) const { + RustStream rs(s); + if (!orchard_incoming_viewing_key_serialize(inner.get(), &rs, RustStream::write_callback)) { + throw std::ios_base::failure("Failed to serialize Orchard incoming viewing key"); + } + } + + template + void Unserialize(Stream& s) { + RustStream rs(s); + OrchardIncomingViewingKeyPtr* key = orchard_incoming_viewing_key_parse(&rs, RustStream::read_callback); + if (key == nullptr) { + throw std::ios_base::failure("Failed to parse Orchard incoming viewing key"); + } + inner.reset(key); + } + + template + static OrchardIncomingViewingKey Read(Stream& stream) { + OrchardIncomingViewingKey key; + stream >> key; + return key; + } +}; + +class OrchardSpendingKey; + +class OrchardFullViewingKey +{ +private: + std::unique_ptr inner; + + OrchardFullViewingKey() : inner(nullptr, orchard_full_viewing_key_free) {} + + OrchardFullViewingKey(OrchardFullViewingKeyPtr* ptr) : + inner(ptr, orchard_full_viewing_key_free) {} + + friend class OrchardSpendingKey; +public: + OrchardFullViewingKey(OrchardFullViewingKey&& key) : inner(std::move(key.inner)) {} + + OrchardFullViewingKey(const OrchardFullViewingKey& key) : + inner(orchard_full_viewing_key_clone(key.inner.get()), orchard_full_viewing_key_free) {} + + OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key) + { + if (this != &key) { + inner = std::move(key.inner); + } + return *this; + } + + OrchardFullViewingKey& operator=(const OrchardFullViewingKey& key) + { + if (this != &key) { + inner.reset(orchard_full_viewing_key_clone(key.inner.get())); + } + return *this; + } + + friend bool operator==(const OrchardFullViewingKey& a, const OrchardFullViewingKey& b) + { + return orchard_full_viewing_key_eq(a.inner.get(), b.inner.get()); + } + + template + void Serialize(Stream& s) const { + RustStream rs(s); + if (!orchard_full_viewing_key_serialize(inner.get(), &rs, RustStream::write_callback)) { + throw std::ios_base::failure("Failed to serialize Orchard full viewing key"); + } + } + + template + void Unserialize(Stream& s) { + RustStream rs(s); + OrchardFullViewingKeyPtr* key = orchard_full_viewing_key_parse(&rs, RustStream::read_callback); + if (key == nullptr) { + throw std::ios_base::failure("Failed to parse Orchard full viewing key"); + } + inner.reset(key); + } + + template + static OrchardFullViewingKey Read(Stream& stream) { + OrchardFullViewingKey key; + stream >> key; + return key; + } + + OrchardIncomingViewingKey ToIncomingViewingKey() const; +}; + +class OrchardSpendingKey +{ +private: + std::unique_ptr inner; + + OrchardSpendingKey() : inner(nullptr, orchard_spending_key_free) {} + + OrchardSpendingKey(OrchardSpendingKeyPtr* ptr) : + inner(ptr, orchard_spending_key_free) {} +public: + OrchardSpendingKey(OrchardSpendingKey&& key) : inner(std::move(key.inner)) {} + + OrchardSpendingKey(const OrchardSpendingKey& key) : + inner(orchard_spending_key_clone(key.inner.get()), orchard_spending_key_free) {} + + static OrchardSpendingKey ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + libzcash::AccountId accountId); + + OrchardSpendingKey& operator=(OrchardSpendingKey&& key) + { + if (this != &key) { + inner = std::move(key.inner); + } + return *this; + } + + OrchardSpendingKey& operator=(const OrchardSpendingKey& key) + { + if (this != &key) { + inner.reset(orchard_spending_key_clone(key.inner.get())); + } + return *this; + } + + friend bool operator==(const OrchardSpendingKey& a, const OrchardSpendingKey& b) + { + return orchard_spending_key_eq(a.inner.get(), b.inner.get()); + } + + template + void Serialize(Stream& s) const { + RustStream rs(s); + if (!orchard_spending_key_serialize(inner.get(), &rs, RustStream::write_callback)) { + throw std::ios_base::failure("Failed to serialize Orchard spending key"); + } + } + + template + void Unserialize(Stream& s) { + RustStream rs(s); + OrchardSpendingKeyPtr* key = orchard_spending_key_parse(&rs, RustStream::read_callback); + if (key == nullptr) { + throw std::ios_base::failure("Failed to parse Orchard spending key"); + } + inner.reset(key); + } + + template + static OrchardSpendingKey Read(Stream& stream) { + OrchardSpendingKey key; + stream >> key; + return key; + } + + OrchardFullViewingKey ToFullViewingKey() const; +}; + +} // namespace libzcash + +#endif // ZCASH_ADDRESS_ORCHARD_H + From 6ee0dea21888a77a252c1d7daecdaa752df68370 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 25 Nov 2021 08:28:53 -0700 Subject: [PATCH 145/514] Apply suggestions from code review Co-authored-by: Daira Hopwood --- src/rust/src/orchard_keys_ffi.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/rust/src/orchard_keys_ffi.rs b/src/rust/src/orchard_keys_ffi.rs index efd4e77d1d3..634c6258814 100644 --- a/src/rust/src/orchard_keys_ffi.rs +++ b/src/rust/src/orchard_keys_ffi.rs @@ -78,12 +78,10 @@ pub extern "C" fn orchard_incoming_viewing_key_to_address( key: *const IncomingViewingKey, diversifier_index: *const [u8; 11], ) -> *mut Address { - let key = unsafe { - key.as_ref() - .expect("Orchard incoming viewing key pointer may not be null.") - }; + let key = + unsafe { key.as_ref() }.expect("Orchard incoming viewing key pointer may not be null."); - let diversifier_index = unsafe { DiversifierIndex::from(*diversifier_index) }; + let diversifier_index = DiversifierIndex::from(unsafe { *diversifier_index }); Box::into_raw(Box::new(key.address_at(diversifier_index))) } @@ -93,10 +91,8 @@ pub extern "C" fn orchard_incoming_viewing_key_serialize( stream: Option, write_cb: Option, ) -> bool { - let key = unsafe { - key.as_ref() - .expect("Orchard incoming viewing key pointer may not be null.") - }; + let key = + unsafe { key.as_ref() }.expect("Orchard incoming viewing key pointer may not be null."); let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); match writer.write_all(&key.to_bytes()) { @@ -173,10 +169,7 @@ pub extern "C" fn orchard_full_viewing_key_serialize( stream: Option, write_cb: Option, ) -> bool { - let key = unsafe { - key.as_ref() - .expect("Orchard full viewing key pointer may not be null.") - }; + let key = unsafe { key.as_ref() }.expect("Orchard full viewing key pointer may not be null."); let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); match key.write(&mut writer) { @@ -272,10 +265,7 @@ pub extern "C" fn orchard_spending_key_serialize( stream: Option, write_cb: Option, ) -> bool { - let key = unsafe { - key.as_ref() - .expect("Orchard spending key pointer may not be null.") - }; + let key = unsafe { key.as_ref() }.expect("Orchard spending key pointer may not be null."); let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); match writer.write_all(key.to_bytes()) { From 5ae516f7b0770201894e6452451ad787cad02c97 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 29 Nov 2021 19:56:48 +0000 Subject: [PATCH 146/514] cargo update --- Cargo.lock | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce38c4237e3..9c99555b3e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,32 +574,31 @@ checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" [[package]] name = "futures-channel" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" [[package]] name = "futures-task" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" +checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" [[package]] name = "futures-util" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" dependencies = [ - "autocfg", "futures-core", "futures-task", "pin-project-lite", @@ -825,9 +824,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" [[package]] name = "libm" @@ -1548,9 +1547,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", @@ -1606,9 +1605,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99beeb0daeac2bd1e86ac2c21caddecb244b39a093594da1a661ec2060c7aedd" +checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" dependencies = [ "itoa", "libc", @@ -1616,9 +1615,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" dependencies = [ "tinyvec_macros", ] @@ -1631,9 +1630,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" +checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" dependencies = [ "autocfg", "libc", @@ -1645,9 +1644,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" +checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" dependencies = [ "proc-macro2", "quote", @@ -1705,9 +1704,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a4ddde70311d8da398062ecf6fc2c309337de6b0f77d6c27aff8d53f6fca52" +checksum = "7507ec620f809cdf07cccb5bc57b13069a88031b795efd4079b1c71b66c1613d" dependencies = [ "ansi_term", "lazy_static", From 6c0bd90ee67aa2721a25180a42937b680aaf84f1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 29 Nov 2021 19:58:59 +0000 Subject: [PATCH 147/514] ed25519-zebra 3 --- Cargo.lock | 5 +++-- Cargo.toml | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c99555b3e8..2337ac4af97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,8 +494,9 @@ dependencies = [ [[package]] name = "ed25519-zebra" -version = "2.2.0" -source = "git+https://github.com/ZcashFoundation/ed25519-zebra.git?rev=d3512400227a362d08367088ffaa9bd4142a69c7#d3512400227a362d08367088ffaa9bd4142a69c7" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" dependencies = [ "curve25519-dalek", "hex", diff --git a/Cargo.toml b/Cargo.toml index 31a0df3d618..c23a774650f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ zcash_history = "0.2" zcash_note_encryption = "0.0" zcash_primitives = "0.5" zcash_proofs = "0.5" -ed25519-zebra = "2.2.0" +ed25519-zebra = "3" # Metrics hyper = { version = "=0.14.2", default-features = false, features = ["server", "tcp", "http1"] } @@ -69,7 +69,6 @@ panic = 'abort' codegen-units = 1 [patch.crates-io] -ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } From e7c507cfb343eadd5d8696e6ac04e1f92e99d4a1 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Nov 2021 12:27:15 -0700 Subject: [PATCH 148/514] Update orchard & librustzcash dependency versions. --- Cargo.lock | 23 ++++++++++++----------- Cargo.toml | 12 ++++++------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b0bf6cf68f..ee014014009 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,7 +521,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "blake2b_simd", "byteorder", @@ -530,7 +530,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "blake2b_simd", ] @@ -1112,7 +1112,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.0.0" -source = "git+https://github.com/nuttycom/orchard.git?rev=14c4b40dfc2368b904ee157bc2aeaa7a2c90cb86#14c4b40dfc2368b904ee157bc2aeaa7a2c90cb86" +source = "git+https://github.com/zcash/orchard.git?rev=68b790c7dadade049f44ad4aafa0ff71a3a10e91#68b790c7dadade049f44ad4aafa0ff71a3a10e91" dependencies = [ "aes", "arrayvec 0.7.2", @@ -1404,8 +1404,9 @@ dependencies = [ [[package]] name = "reddsa" -version = "0.0.0" -source = "git+https://github.com/str4d/redjubjub.git?rev=416a6a8ebf8bd42c114c938883016c04f338de72#416a6a8ebf8bd42c114c938883016c04f338de72" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e2c94bca3445cae0d55dff7370e29c24885d2403a1665ce19c106c79455e6" dependencies = [ "blake2b_simd", "byteorder", @@ -1884,7 +1885,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "bech32", "blake2b_simd", @@ -1896,7 +1897,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "byteorder", "nonempty", @@ -1905,7 +1906,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "bigint", "blake2b_simd", @@ -1915,7 +1916,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "blake2b_simd", "byteorder", @@ -1930,7 +1931,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "aes", "bip0039", @@ -1964,7 +1965,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" +source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 4ddf0aeefc1..b104fd3df77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,9 +72,9 @@ codegen-units = 1 [patch.crates-io] ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -orchard = { git = "https://github.com/nuttycom/orchard.git", rev = "14c4b40dfc2368b904ee157bc2aeaa7a2c90cb86" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "68b790c7dadade049f44ad4aafa0ff71a3a10e91" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } From bde245d8bc49c69a316cb654c4f96bbf00a392c1 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Nov 2021 13:08:30 -0700 Subject: [PATCH 149/514] Remove incorrect FFI method documentation. Co-authored-by: str4d --- src/rust/include/rust/orchard/keys.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/rust/include/rust/orchard/keys.h b/src/rust/include/rust/orchard/keys.h index 07d91aca4b9..8ba5cc0296b 100644 --- a/src/rust/include/rust/orchard/keys.h +++ b/src/rust/include/rust/orchard/keys.h @@ -70,9 +70,7 @@ OrchardIncomingViewingKeyPtr* orchard_incoming_viewing_key_parse( read_callback_t read_cb); /** - * Serializes an Orchard incoming viewing key to the given stream - * - * If `incoming_viewing_key == nullptr`, this serializes `nActionsOrchard = 0`. + * Serializes an Orchard incoming viewing key to the given stream. */ bool orchard_incoming_viewing_key_serialize( const OrchardIncomingViewingKeyPtr* incoming_viewing_key, @@ -124,9 +122,7 @@ OrchardFullViewingKeyPtr* orchard_full_viewing_key_parse( read_callback_t read_cb); /** - * Serializes an Orchard full viewing key to the given stream - * - * If `full_viewing_key == nullptr`, this serializes `nActionsOrchard = 0`. + * Serializes an Orchard full viewing key to the given stream. */ bool orchard_full_viewing_key_serialize( const OrchardFullViewingKeyPtr* full_viewing_key, @@ -187,9 +183,7 @@ OrchardSpendingKeyPtr* orchard_spending_key_parse( read_callback_t read_cb); /** - * Serializes an Orchard spending key to the given stream - * - * If `spending_key == nullptr`, this will return `false` + * Serializes an Orchard spending key to the given stream. */ bool orchard_spending_key_serialize( const OrchardSpendingKeyPtr* spending_key, From 782c705cb1006cf2bfd0689a38d471ad9b77411e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Nov 2021 14:25:38 -0700 Subject: [PATCH 150/514] Remove Orchard spending key equality implementation. --- src/rust/include/rust/orchard/keys.h | 7 ------- src/rust/src/orchard_keys_ffi.rs | 7 ------- src/wallet/gtest/test_orchard_zkeys.cpp | 8 ++++++-- src/zcash/address/orchard.hpp | 5 ----- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/rust/include/rust/orchard/keys.h b/src/rust/include/rust/orchard/keys.h index 8ba5cc0296b..082462c1d3d 100644 --- a/src/rust/include/rust/orchard/keys.h +++ b/src/rust/include/rust/orchard/keys.h @@ -196,13 +196,6 @@ bool orchard_spending_key_serialize( OrchardFullViewingKeyPtr* orchard_spending_key_to_full_viewing_key( const OrchardSpendingKeyPtr* key); -/** - * Implements equality testing between spending keys. - */ -bool orchard_spending_key_eq( - const OrchardSpendingKeyPtr* k0, - const OrchardSpendingKeyPtr* k1); - #ifdef __cplusplus } #endif diff --git a/src/rust/src/orchard_keys_ffi.rs b/src/rust/src/orchard_keys_ffi.rs index 634c6258814..d068857d35c 100644 --- a/src/rust/src/orchard_keys_ffi.rs +++ b/src/rust/src/orchard_keys_ffi.rs @@ -285,10 +285,3 @@ pub extern "C" fn orchard_spending_key_to_full_viewing_key( .map(|key| Box::into_raw(Box::new(FullViewingKey::from(key)))) .unwrap_or(std::ptr::null_mut()) } - -#[no_mangle] -pub extern "C" fn orchard_spending_key_eq(k0: *const SpendingKey, k1: *const SpendingKey) -> bool { - let k0 = unsafe { k0.as_ref() }; - let k1 = unsafe { k1.as_ref() }; - k0 == k1 -} diff --git a/src/wallet/gtest/test_orchard_zkeys.cpp b/src/wallet/gtest/test_orchard_zkeys.cpp index 1ae284682ae..bdf84f66c70 100644 --- a/src/wallet/gtest/test_orchard_zkeys.cpp +++ b/src/wallet/gtest/test_orchard_zkeys.cpp @@ -35,12 +35,16 @@ TEST(OrchardZkeysTest, FVKSerializationRoundtrip) { TEST(OrchardZkeysTest, SKSerializationRoundtrip) { auto seed = MnemonicSeed::Random(1); //testnet coin type - auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0); + auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << sk; + std::string skStr = ss.str(); auto sk0 = libzcash::OrchardSpendingKey::Read(ss); + CDataStream ss0(SER_NETWORK, PROTOCOL_VERSION); + ss0 << sk0; + std::string sk0Str = ss0.str(); - ASSERT_EQ(sk, sk0); + ASSERT_EQ(skStr, sk0Str); } diff --git a/src/zcash/address/orchard.hpp b/src/zcash/address/orchard.hpp index 9ba498d09a1..60e668f5cf6 100644 --- a/src/zcash/address/orchard.hpp +++ b/src/zcash/address/orchard.hpp @@ -223,11 +223,6 @@ class OrchardSpendingKey return *this; } - friend bool operator==(const OrchardSpendingKey& a, const OrchardSpendingKey& b) - { - return orchard_spending_key_eq(a.inner.get(), b.inner.get()); - } - template void Serialize(Stream& s) const { RustStream rs(s); From d65200c8601c16c35f6207d8648f6771867182e9 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 19 Nov 2021 14:12:56 -0700 Subject: [PATCH 151/514] Refine structure of Zcash address generation. This commit does not include functional changes; it merely breaks up content previously included in `zcash/address/zip32.h` into smaller and more focused components. --- src/Makefile.am | 10 +- src/keystore.h | 1 + src/wallet/gtest/test_orchard_zkeys.cpp | 1 + src/wallet/wallet.h | 2 + src/zcash/Address.hpp | 1 + src/zcash/address/bip44.cpp | 70 ++++++++ src/zcash/address/bip44.h | 36 ++++ src/zcash/address/mnemonic.cpp | 37 ++++ src/zcash/address/mnemonic.h | 107 ++++++++++++ src/zcash/address/unified.cpp | 73 ++++++++ src/zcash/address/unified.h | 92 ++++++++++ src/zcash/address/zip32.cpp | 162 +----------------- src/zcash/address/zip32.h | 214 ++---------------------- 13 files changed, 442 insertions(+), 364 deletions(-) create mode 100644 src/zcash/address/bip44.cpp create mode 100644 src/zcash/address/bip44.h create mode 100644 src/zcash/address/mnemonic.cpp create mode 100644 src/zcash/address/mnemonic.h create mode 100644 src/zcash/address/unified.cpp create mode 100644 src/zcash/address/unified.h diff --git a/src/Makefile.am b/src/Makefile.am index aa3213ff6af..bca93e6cef7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -107,9 +107,12 @@ LIBZCASH_H = \ zcash/IncrementalMerkleTree.hpp \ zcash/NoteEncryption.hpp \ zcash/Address.hpp \ + zcash/address/bip44.h \ + zcash/address/mnemonic.h \ + zcash/address/orchard.h \ zcash/address/sapling.hpp \ zcash/address/sprout.hpp \ - zcash/address/orchard.h \ + zcash/address/unified.h \ zcash/address/zip32.h \ zcash/History.hpp \ zcash/JoinSplit.hpp \ @@ -536,9 +539,12 @@ libzcash_a_SOURCES = \ zcash/IncrementalMerkleTree.cpp \ zcash/NoteEncryption.cpp \ zcash/Address.cpp \ + zcash/address/bip44.cpp \ + zcash/address/mnemonic.cpp \ + zcash/address/orchard.cpp \ zcash/address/sapling.cpp \ zcash/address/sprout.cpp \ - zcash/address/orchard.cpp \ + zcash/address/unified.cpp \ zcash/address/zip32.cpp \ zcash/History.cpp \ zcash/JoinSplit.cpp \ diff --git a/src/keystore.h b/src/keystore.h index 78bf5c400e0..c2e40b399e4 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -11,6 +11,7 @@ #include "script/script.h" #include "script/standard.h" #include "sync.h" +#include "zcash/address/mnemonic.h" #include "zcash/Address.hpp" #include "zcash/NoteEncryption.hpp" diff --git a/src/wallet/gtest/test_orchard_zkeys.cpp b/src/wallet/gtest/test_orchard_zkeys.cpp index bdf84f66c70..3afd22f0a36 100644 --- a/src/wallet/gtest/test_orchard_zkeys.cpp +++ b/src/wallet/gtest/test_orchard_zkeys.cpp @@ -4,6 +4,7 @@ #include +#include "zcash/address/mnemonic.h" #include "zcash/address/orchard.hpp" TEST(OrchardZkeysTest, IVKSerializationRoundtrip) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3bb5c55d143..6bec30f9e70 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -23,6 +23,8 @@ #include "wallet/crypter.h" #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" +#include "zcash/address/bip44.h" +#include "zcash/address/mnemonic.h" #include "zcash/Address.hpp" #include "zcash/Note.hpp" #include "base58.h" diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index e41272a9076..3200e983327 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -5,6 +5,7 @@ #include "zcash/address/orchard.hpp" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" +#include "zcash/address/unified.h" #include "zcash/address/zip32.h" #include diff --git a/src/zcash/address/bip44.cpp b/src/zcash/address/bip44.cpp new file mode 100644 index 00000000000..f3e8b69e8f1 --- /dev/null +++ b/src/zcash/address/bip44.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "bip44.h" + +std::optional> libzcash::DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { + auto rawSeed = seed.RawSeed(); + auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); + + // We use a fixed keypath scheme of m/44'/coin_type'/account' + // Derive m/44' + auto m_44h = m.Derive(44 | HARDENED_KEY_LIMIT); + if (!m_44h.has_value()) return std::nullopt; + + // Derive m/44'/coin_type' + auto m_44h_cth = m_44h.value().Derive(bip44CoinType | HARDENED_KEY_LIMIT); + if (!m_44h_cth.has_value()) return std::nullopt; + + // Derive m/44'/coin_type'/account_id' + auto result = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT); + if (!result.has_value()) return std::nullopt; + + auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + + return std::make_pair(result.value(), hdKeypath); +} + +std::optional libzcash::Bip44AccountChains::ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + libzcash::AccountId accountId) { + auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); + if (!accountKeyOpt.has_value()) return std::nullopt; + + auto accountKey = accountKeyOpt.value(); + auto external = accountKey.first.Derive(0); + auto internal = accountKey.first.Derive(1); + + if (!(external.has_value() && internal.has_value())) return std::nullopt; + + return Bip44AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); +} + +std::optional> libzcash::Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { + auto childKey = external.Derive(addrIndex); + if (!childKey.has_value()) return std::nullopt; + + auto hdKeypath = "m/44'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(accountId) + "'/" + + "0/" + + std::to_string(addrIndex); + + return std::make_pair(childKey.value(), hdKeypath); +} + +std::optional> libzcash::Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { + auto childKey = internal.Derive(addrIndex); + if (!childKey.has_value()) return std::nullopt; + + auto hdKeypath = "m/44'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(accountId) + "'/" + + "1/" + + std::to_string(addrIndex); + + return std::make_pair(childKey.value(), hdKeypath); +} + diff --git a/src/zcash/address/bip44.h b/src/zcash/address/bip44.h new file mode 100644 index 00000000000..4a6bc15c930 --- /dev/null +++ b/src/zcash/address/bip44.h @@ -0,0 +1,36 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZCASH_ADDRESS_BIP44_H +#define ZCASH_ZCASH_ADDRESS_BIP44_H + +#include "zip32.h" + +namespace libzcash { + +std::optional> DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); + +class Bip44AccountChains { +private: + uint256 seedFp; + libzcash::AccountId accountId; + uint32_t bip44CoinType; + CExtKey external; + CExtKey internal; + + Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, libzcash::AccountId accountIdIn, CExtKey externalIn, CExtKey internalIn): + seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} +public: + static std::optional ForAccount( + const HDSeed& mnemonic, + uint32_t bip44CoinType, + libzcash::AccountId accountId); + + std::optional> DeriveExternal(uint32_t addrIndex); + std::optional> DeriveInternal(uint32_t addrIndex); +}; + +} //namespace libzcash + +#endif // ZCASH_ZCASH_ADDRESS_BIP44_H diff --git a/src/zcash/address/mnemonic.cpp b/src/zcash/address/mnemonic.cpp new file mode 100644 index 00000000000..97ac243897a --- /dev/null +++ b/src/zcash/address/mnemonic.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2018-2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "random.h" + +#include "mnemonic.h" + +#include "bip44.h" +#include "unified.h" + +using namespace libzcash; + +MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) +{ + assert(entropyLen >= 32); + while (true) { // loop until we find usable entropy + RawHDSeed entropy(entropyLen, 0); + GetRandBytes(entropy.data(), entropyLen); + const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); + SecureString mnemonic(phrase); + zip339_free_phrase(phrase); + MnemonicSeed seed(language, mnemonic); + + // Verify that the seed data is valid entropy for unified spending keys at + // account 0 and at both the public & private chain levels for account 0x7FFFFFFF. + // It is not necessary to check for a valid diversified Sapling address at + // account 0x7FFFFFFF because derivation via the legacy path can simply search + // for a valid diversifier; unlike in the unified spending key case, diversifier + // indices don't need to line up with anything. + if (ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && + Bip44AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { + return seed; + } + } +} + diff --git a/src/zcash/address/mnemonic.h b/src/zcash/address/mnemonic.h new file mode 100644 index 00000000000..db9f6a517de --- /dev/null +++ b/src/zcash/address/mnemonic.h @@ -0,0 +1,107 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZCASH_ADDRESS_MNEMONIC_H +#define ZCASH_ZCASH_ADDRESS_MNEMONIC_H + +#include "zip32.h" + +class MnemonicSeed: public HDSeed { +private: + Language language; + SecureString mnemonic; + + MnemonicSeed() {} + + void SetSeedFromMnemonic() { + seed.resize(64); + zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); + } + +public: + MnemonicSeed(Language languageIn, SecureString mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { + SetSeedFromMnemonic(); + } + + /** + * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible + * to check that the generated seed can produce valid transparent and unified addresses at account + * numbers 0x7FFFFFFF and 0x00 respectively. + */ + static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); + + static std::string LanguageName(Language language) { + switch (language) { + case English: + return "English"; + case SimplifiedChinese: + return "Simplified Chinese"; + case TraditionalChinese: + return "Traditional Chinese"; + case Czech: + return "Czech"; + case French: + return "French"; + case Italian: + return "Italian"; + case Japanese: + return "Japanese"; + case Korean: + return "Korean"; + case Portuguese: + return "Portuguese"; + case Spanish: + return "Spanish"; + default: + return "INVALID"; + } + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + if (ser_action.ForRead()) { + uint32_t language0; + + READWRITE(language0); + READWRITE(mnemonic); + language = (Language) language0; + SetSeedFromMnemonic(); + } else { + uint32_t language0 = (uint32_t) language; + + READWRITE(language0); + READWRITE(mnemonic); + } + } + + template + static MnemonicSeed Read(Stream& stream) { + MnemonicSeed seed; + stream >> seed; + return seed; + } + + const Language GetLanguage() const { + return language; + } + + const SecureString& GetMnemonic() const { + return mnemonic; + } + + friend bool operator==(const MnemonicSeed& a, const MnemonicSeed& b) + { + return a.seed == b.seed; + } + + friend bool operator!=(const MnemonicSeed& a, const MnemonicSeed& b) + { + return !(a == b); + } +}; + +#endif // ZCASH_ZCASH_ADDRESS_MNEMONIC_H + diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp new file mode 100644 index 00000000000..27067e6b1b7 --- /dev/null +++ b/src/zcash/address/unified.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "unified.h" +#include "bip44.h" + +using namespace libzcash; + +// +// Unified Keys +// + +std::optional> ZcashdUnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, AccountId accountId) { + ZcashdUnifiedSpendingKey usk; + usk.accountId = accountId; + + auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); + if (!transparentKey.has_value()) return std::nullopt; + usk.transparentKey = transparentKey.value().first; + + auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); + usk.saplingKey = saplingKey.first; + + return std::make_pair(usk, saplingKey.second); +} + +ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { + ZcashdUnifiedFullViewingKey ufvk; + + if (transparentKey.has_value()) { + ufvk.transparentKey = transparentKey.value().Neuter(); + } + + if (saplingKey.has_value()) { + ufvk.saplingKey = saplingKey.value().ToXFVK(); + } + + return ufvk; +} + +std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { + ZcashdUnifiedAddress ua; + + if (transparentKey.has_value()) { + auto childIndex = j.ToTransparentChildIndex(); + if (!childIndex.has_value()) return std::nullopt; + + CExtPubKey externalKey; + if (!transparentKey.value().Derive(externalKey, 0)) { + return std::nullopt; + } + + CExtPubKey childKey; + if (externalKey.Derive(childKey, childIndex.value())) { + ua.transparentAddress = childKey.pubkey.GetID(); + } else { + return std::nullopt; + } + } + + if (saplingKey.has_value()) { + auto saplingAddress = saplingKey.value().Address(j); + if (saplingAddress.has_value()) { + ua.saplingAddress = saplingAddress.value(); + } else { + return std::nullopt; + } + } + + return ua; +} + diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h new file mode 100644 index 00000000000..08269b7712f --- /dev/null +++ b/src/zcash/address/unified.h @@ -0,0 +1,92 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZCASH_ADDRESS_UNIFIED_H +#define ZCASH_ZCASH_ADDRESS_UNIFIED_H + +#include "zip32.h" +#include "bip44.h" + +namespace libzcash { + +class ZcashdUnifiedSpendingKey; +class ZcashdUnifiedFullViewingKey; + +class ZcashdUnifiedAddress { +private: + diversifier_index_t diversifier_index; + std::optional transparentAddress; + std::optional saplingAddress; + + friend class ZcashdUnifiedFullViewingKey; + + ZcashdUnifiedAddress() {} +public: + const std::optional& GetTransparentAddress() const { + return transparentAddress; + } + + const std::optional& GetSaplingPaymentAddress() const { + return saplingAddress; + } +}; + +class ZcashdUnifiedFullViewingKey { +private: + std::optional transparentKey; + std::optional saplingKey; + + ZcashdUnifiedFullViewingKey() {} + + friend class ZcashdUnifiedSpendingKey; +public: + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingExtendedFullViewingKey() const { + return saplingKey; + } + + std::optional Address(diversifier_index_t j) const; + + std::pair FindAddress(diversifier_index_t j) const { + auto addr = Address(j); + while (!addr.has_value()) { + if (!j.increment()) + throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; + addr = Address(j); + } + return std::make_pair(addr.value(), j); + } +}; + +class ZcashdUnifiedSpendingKey { +private: + libzcash::AccountId accountId; + std::optional transparentKey; + std::optional saplingKey; + + ZcashdUnifiedSpendingKey() {} +public: + static std::optional> ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + libzcash::AccountId accountId); + + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingExtendedSpendingKey() const { + return saplingKey; + } + + ZcashdUnifiedFullViewingKey ToFullViewingKey() const; +}; + +} //namespace libzcash + +#endif // ZCASH_ZCASH_ADDRESS_UNIFIED_H + diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index f88311fdecd..fc558edef85 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Zcash developers +// Copyright (c) 2018-2021 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -21,30 +21,6 @@ const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x7FFFFFFF); -MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) -{ - assert(entropyLen >= 32); - while (true) { // loop until we find usable entropy - RawHDSeed entropy(entropyLen, 0); - GetRandBytes(entropy.data(), entropyLen); - const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); - SecureString mnemonic(phrase); - zip339_free_phrase(phrase); - MnemonicSeed seed(language, mnemonic); - - // Verify that the seed data is valid entropy for unified spending keys at - // account 0 and at both the public & private chain levels for account 0x7FFFFFFF. - // It is not necessary to check for a valid diversified Sapling address at - // account 0x7FFFFFFF because derivation via the legacy path can simply search - // for a valid diversifier; unlike in the unified spending key case, diversifier - // indices don't need to line up with anything. - if (libzcash::ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && - libzcash::Bip44AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { - return seed; - } - } -} - uint256 HDSeed::Fingerprint() const { CBLAKE2bWriter h(SER_GETHASH, 0, ZCASH_HD_SEED_FP_PERSONAL); @@ -81,74 +57,6 @@ std::optional diversifier_index_t::ToTransparentChildIndex() const } } -// -// Transparent -// - -std::optional> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { - auto rawSeed = seed.RawSeed(); - auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); - - // We use a fixed keypath scheme of m/44'/coin_type'/account' - // Derive m/44' - auto m_44h = m.Derive(44 | HARDENED_KEY_LIMIT); - if (!m_44h.has_value()) return std::nullopt; - - // Derive m/44'/coin_type' - auto m_44h_cth = m_44h.value().Derive(bip44CoinType | HARDENED_KEY_LIMIT); - if (!m_44h_cth.has_value()) return std::nullopt; - - // Derive m/44'/coin_type'/account_id' - auto result = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT); - if (!result.has_value()) return std::nullopt; - - auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - - return std::make_pair(result.value(), hdKeypath); -} - -std::optional Bip44AccountChains::ForAccount( - const MnemonicSeed& seed, - uint32_t bip44CoinType, - libzcash::AccountId accountId) { - auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); - if (!accountKeyOpt.has_value()) return std::nullopt; - - auto accountKey = accountKeyOpt.value(); - auto external = accountKey.first.Derive(0); - auto internal = accountKey.first.Derive(1); - - if (!(external.has_value() && internal.has_value())) return std::nullopt; - - return Bip44AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); -} - -std::optional> Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { - auto childKey = external.Derive(addrIndex); - if (!childKey.has_value()) return std::nullopt; - - auto hdKeypath = "m/44'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(accountId) + "'/" - + "0/" - + std::to_string(addrIndex); - - return std::make_pair(childKey.value(), hdKeypath); -} - -std::optional> Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { - auto childKey = internal.Derive(addrIndex); - if (!childKey.has_value()) return std::nullopt; - - auto hdKeypath = "m/44'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(accountId) + "'/" - + "1/" - + std::to_string(addrIndex); - - return std::make_pair(childKey.value(), hdKeypath); -} - // // Sapling // @@ -251,7 +159,7 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const return xsk_i; } -std::pair SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { +std::pair SaplingExtendedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/account' @@ -269,7 +177,7 @@ std::pair SaplingExtendedSpendingKey::For return std::make_pair(xsk, hdKeypath); } -std::pair SaplingExtendedSpendingKey::Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { +std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/0x7FFFFFFF'/addressIndex' @@ -309,70 +217,6 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const return ret; } -// -// Unified -// - -std::optional> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { - ZcashdUnifiedSpendingKey usk; - usk.accountId = accountId; - - auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); - if (!transparentKey.has_value()) return std::nullopt; - usk.transparentKey = transparentKey.value().first; - - auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); - usk.saplingKey = saplingKey.first; - - return std::make_pair(usk, saplingKey.second); -} - -ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { - ZcashdUnifiedFullViewingKey ufvk; - - if (transparentKey.has_value()) { - ufvk.transparentKey = transparentKey.value().Neuter(); - } - - if (saplingKey.has_value()) { - ufvk.saplingKey = saplingKey.value().ToXFVK(); - } - - return ufvk; -} - -std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { - ZcashdUnifiedAddress ua; - - if (transparentKey.has_value()) { - auto childIndex = j.ToTransparentChildIndex(); - if (!childIndex.has_value()) return std::nullopt; - - CExtPubKey externalKey; - if (!transparentKey.value().Derive(externalKey, 0)) { - return std::nullopt; - } - - CExtPubKey childKey; - if (externalKey.Derive(childKey, childIndex.value())) { - ua.transparentAddress = childKey.pubkey.GetID(); - } else { - return std::nullopt; - } - } - - if (saplingKey.has_value()) { - auto saplingAddress = saplingKey.value().Address(j); - if (saplingAddress.has_value()) { - ua.saplingAddress = saplingAddress.value(); - } else { - return std::nullopt; - } - } - - return ua; -} - std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath) { std::regex pattern("m/" + std::to_string(purpose) + "'/" + std::to_string(coinType) + "'/([0-9]+)'.*"); std::smatch matches; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 9da16b71e9a..7f9cf4cc93d 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Zcash developers +// Copyright (c) 2018-2021 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -22,13 +22,6 @@ const uint32_t HARDENED_KEY_LIMIT = 0x80000000; const size_t ZIP32_XFVK_SIZE = 169; const size_t ZIP32_XSK_SIZE = 169; -/** - * The account identifier used for HD derivation of - * transparent and Sapling addresses via the legacy - * `getnewaddress` and `z_getnewaddress` code paths, - */ -const uint32_t ZCASH_LEGACY_ACCOUNT = HARDENED_KEY_LIMIT - 1; - typedef std::vector> RawHDSeed; typedef std::string HDKeyPath; @@ -64,102 +57,6 @@ class HDSeed { }; -class MnemonicSeed: public HDSeed { -private: - Language language; - SecureString mnemonic; - - MnemonicSeed() {} - - void SetSeedFromMnemonic() { - seed.resize(64); - zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); - } - -public: - MnemonicSeed(Language languageIn, SecureString mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { - SetSeedFromMnemonic(); - } - - /** - * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible - * to check that the generated seed can produce valid transparent and unified addresses at account - * numbers 0x7FFFFFFF and 0x00 respectively. - */ - static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); - - static std::string LanguageName(Language language) { - switch (language) { - case English: - return "English"; - case SimplifiedChinese: - return "Simplified Chinese"; - case TraditionalChinese: - return "Traditional Chinese"; - case Czech: - return "Czech"; - case French: - return "French"; - case Italian: - return "Italian"; - case Japanese: - return "Japanese"; - case Korean: - return "Korean"; - case Portuguese: - return "Portuguese"; - case Spanish: - return "Spanish"; - default: - return "INVALID"; - } - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - if (ser_action.ForRead()) { - uint32_t language0; - - READWRITE(language0); - READWRITE(mnemonic); - language = (Language) language0; - SetSeedFromMnemonic(); - } else { - uint32_t language0 = (uint32_t) language; - - READWRITE(language0); - READWRITE(mnemonic); - } - } - - template - static MnemonicSeed Read(Stream& stream) { - MnemonicSeed seed; - stream >> seed; - return seed; - } - - const Language GetLanguage() const { - return language; - } - - const SecureString& GetMnemonic() const { - return mnemonic; - } - - friend bool operator==(const MnemonicSeed& a, const MnemonicSeed& b) - { - return a.seed == b.seed; - } - - friend bool operator!=(const MnemonicSeed& a, const MnemonicSeed& b) - { - return !(a == b); - } -}; - // This is not part of ZIP 32, but is here because it's linked to the HD seed. uint256 ovkForShieldingFromTaddr(HDSeed& seed); @@ -167,6 +64,13 @@ namespace libzcash { typedef uint32_t AccountId; +/** + * The account identifier used for HD derivation of + * transparent and Sapling addresses via the legacy + * `getnewaddress` and `z_getnewaddress` code paths, + */ +const AccountId ZCASH_LEGACY_ACCOUNT = HARDENED_KEY_LIMIT - 1; + /** * 88-bit diversifier index. This would ideally derive from base_uint * but those values must have bit widths that are multiples of 32. @@ -292,8 +196,8 @@ struct SaplingExtendedSpendingKey { } static SaplingExtendedSpendingKey Master(const HDSeed& seed); - static std::pair ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); - static std::pair Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); + static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); + static std::pair Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); SaplingExtendedSpendingKey Derive(uint32_t i) const; @@ -311,104 +215,8 @@ struct SaplingExtendedSpendingKey { } }; -class ZcashdUnifiedSpendingKey; -class ZcashdUnifiedFullViewingKey; - -class ZcashdUnifiedAddress { -private: - diversifier_index_t diversifier_index; - std::optional transparentAddress; - std::optional saplingAddress; - - friend class ZcashdUnifiedFullViewingKey; - - ZcashdUnifiedAddress() {} -public: - const std::optional& GetTransparentAddress() const { - return transparentAddress; - } - - const std::optional& GetSaplingPaymentAddress() const { - return saplingAddress; - } -}; - -class ZcashdUnifiedFullViewingKey { -private: - std::optional transparentKey; - std::optional saplingKey; - - ZcashdUnifiedFullViewingKey() {} - - friend class ZcashdUnifiedSpendingKey; -public: - const std::optional& GetTransparentKey() const { - return transparentKey; - } - - const std::optional& GetSaplingExtendedFullViewingKey() const { - return saplingKey; - } - - std::optional Address(diversifier_index_t j) const; - - std::pair FindAddress(diversifier_index_t j) const { - auto addr = Address(j); - while (!addr.has_value()) { - if (!j.increment()) - throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; - addr = Address(j); - } - return std::make_pair(addr.value(), j); - } -}; - -class ZcashdUnifiedSpendingKey { -private: - libzcash::AccountId accountId; - std::optional transparentKey; - std::optional saplingKey; - - ZcashdUnifiedSpendingKey() {} -public: - static std::optional> ForAccount( - const MnemonicSeed& mnemonic, - uint32_t bip44CoinType, - libzcash::AccountId accountId); - - const std::optional& GetTransparentKey() const { - return transparentKey; - } - - const std::optional& GetSaplingExtendedSpendingKey() const { - return saplingKey; - } - - ZcashdUnifiedFullViewingKey ToFullViewingKey() const; -}; - std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath); -class Bip44AccountChains { -private: - uint256 seedFp; - libzcash::AccountId accountId; - uint32_t bip44CoinType; - CExtKey external; - CExtKey internal; - - Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, libzcash::AccountId accountIdIn, CExtKey externalIn, CExtKey internalIn): - seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} -public: - static std::optional ForAccount( - const MnemonicSeed& mnemonic, - uint32_t bip44CoinType, - libzcash::AccountId accountId); - - std::optional> DeriveExternal(uint32_t addrIndex); - std::optional> DeriveInternal(uint32_t addrIndex); -}; - -} +} //namespace libzcash #endif // ZCASH_ZCASH_ADDRESS_ZIP32_H From a2f8e3b56c1652fdb77091ff00bfe03804e3eb68 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 19 Nov 2021 16:21:36 -0700 Subject: [PATCH 152/514] Use CKeyID and CScriptID instead of new P2PKH/P2SHAddress classes. --- src/gtest/test_keys.cpp | 8 ++++---- src/key_io.cpp | 12 ++++++------ src/pubkey.h | 1 + src/script/script.h | 10 ++++++++++ src/script/standard.h | 9 --------- src/wallet/wallet.h | 1 + src/zcash/Address.cpp | 12 ++++++------ src/zcash/Address.hpp | 26 ++++++++------------------ 8 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index 4ff275128d7..d86c959f648 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -78,11 +78,11 @@ namespace libzcash { return tfm::format("Sapling(%s)", HexStr(ss.begin(), ss.end())); } - std::string operator()(const P2SHAddress &p2sh) const { + std::string operator()(const CScriptID &p2sh) const { return tfm::format("P2SH(%s)", p2sh.GetHex()); } - std::string operator()(const P2PKHAddress &p2pkh) const { + std::string operator()(const CKeyID &p2pkh) const { return tfm::format("P2PKH(%s)", p2pkh.GetHex()); } @@ -140,11 +140,11 @@ TEST(Keys, EncodeAndDecodeUnified) ua.AddReceiver(r); } if (!test[1].isNull()) { - libzcash::P2SHAddress r(ParseHex(test[1].get_str())); + CScriptID r(ParseHex(test[1].get_str())); ua.AddReceiver(r); } if (!test[0].isNull()) { - libzcash::P2PKHAddress r(ParseHex(test[0].get_str())); + CKeyID r(ParseHex(test[0].get_str())); ua.AddReceiver(r); } diff --git a/src/key_io.cpp b/src/key_io.cpp index db83bea04fd..e22243f9cab 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -56,8 +56,8 @@ class DataLenForReceiver { DataLenForReceiver() {} size_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const { return 43; } - size_t operator()(const libzcash::P2SHAddress &p2sh) const { return 20; } - size_t operator()(const libzcash::P2PKHAddress &p2pkh) const { return 20; } + size_t operator()(const CScriptID &p2sh) const { return 20; } + size_t operator()(const CKeyID &p2pkh) const { return 20; } size_t operator()(const libzcash::UnknownReceiver &unknown) const { return unknown.data.size(); } }; @@ -82,11 +82,11 @@ class CopyDataForReceiver { memcpy(data, ss.data(), ss.size()); } - void operator()(const libzcash::P2SHAddress &p2sh) const { + void operator()(const CScriptID &p2sh) const { memcpy(data, p2sh.begin(), p2sh.size()); } - void operator()(const libzcash::P2PKHAddress &p2pkh) const { + void operator()(const CKeyID &p2pkh) const { memcpy(data, p2pkh.begin(), p2pkh.size()); } @@ -421,7 +421,7 @@ static bool AddP2SHReceiver(void* ua, const unsigned char* raw) reinterpret_cast(raw + 20), SER_NETWORK, PROTOCOL_VERSION); - libzcash::P2SHAddress receiver; + CScriptID receiver; ss >> receiver; return reinterpret_cast(ua)->AddReceiver(receiver); } @@ -436,7 +436,7 @@ static bool AddP2PKHReceiver(void* ua, const unsigned char* raw) reinterpret_cast(raw + 20), SER_NETWORK, PROTOCOL_VERSION); - libzcash::P2PKHAddress receiver; + CKeyID receiver; ss >> receiver; return reinterpret_cast(ua)->AddReceiver(receiver); } diff --git a/src/pubkey.h b/src/pubkey.h index 4cdfbef3831..94ff547c4a6 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -22,6 +22,7 @@ class CKeyID : public uint160 public: CKeyID() : uint160() {} CKeyID(const uint160& in) : uint160(in) {} + explicit CKeyID(const std::vector& vch) : uint160(vch) {} }; typedef uint256 ChainCode; diff --git a/src/script/script.h b/src/script/script.h index e671191972d..b20227caf90 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -625,6 +625,16 @@ class CScript : public CScriptBase } }; +/** A reference to a CScript: the Hash160 of its serialization */ +class CScriptID : public uint160 +{ +public: + CScriptID() : uint160() {} + explicit CScriptID(const CScript& in); + CScriptID(const uint160& in) : uint160(in) {} + explicit CScriptID(const std::vector& vch) : uint160(vch) {} +}; + class CReserveScript { public: diff --git a/src/script/standard.h b/src/script/standard.h index 09a23368f47..920ac165d5e 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -18,15 +18,6 @@ static const bool DEFAULT_ACCEPT_DATACARRIER = true; class CKeyID; class CScript; -/** A reference to a CScript: the Hash160 of its serialization (see script.h) */ -class CScriptID : public uint160 -{ -public: - CScriptID() : uint160() {} - explicit CScriptID(const CScript& in); - CScriptID(const uint160& in) : uint160(in) {} -}; - /** * Default setting for nMaxDatacarrierBytes. 80 bytes of data, +1 for OP_RETURN, * +2 for the pushdata opcodes. diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6bec30f9e70..6e1ffa2fa8d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -24,6 +24,7 @@ #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" #include "zcash/address/bip44.h" +#include "zcash/address/unified.h" #include "zcash/address/mnemonic.h" #include "zcash/Address.hpp" #include "zcash/Note.hpp" diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 9b5340cb14b..568b3bbff6f 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -24,8 +24,8 @@ bool UnifiedAddress::AddReceiver(Receiver receiver) { auto t = std::visit(TypecodeForReceiver(), r); if ( (t == typecode) || - (std::holds_alternative(r) && std::holds_alternative(receiver)) || - (std::holds_alternative(r) && std::holds_alternative(receiver)) + (std::holds_alternative(r) && std::holds_alternative(receiver)) || + (std::holds_alternative(r) && std::holds_alternative(receiver)) ) { return false; } @@ -76,13 +76,13 @@ uint32_t TypecodeForReceiver::operator()( } uint32_t TypecodeForReceiver::operator()( - const libzcash::P2SHAddress &p2sh) const + const CScriptID &p2sh) const { return ZCASH_UA_TYPECODE_P2SH; } uint32_t TypecodeForReceiver::operator()( - const libzcash::P2PKHAddress &p2sh) const + const CKeyID &p2sh) const { return ZCASH_UA_TYPECODE_P2PKH; } @@ -100,13 +100,13 @@ std::optional ReceiverToRawAddress::operator()( } std::optional ReceiverToRawAddress::operator()( - const libzcash::P2SHAddress &p2sh) const + const CScriptID &p2sh) const { return std::nullopt; } std::optional ReceiverToRawAddress::operator()( - const libzcash::P2PKHAddress &p2sh) const + const CKeyID &p2sh) const { return std::nullopt; } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 3200e983327..09e6144a41c 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -2,26 +2,16 @@ #define ZC_ADDRESS_H_ #include "uint256.h" +#include "pubkey.h" +#include "script/script.h" #include "zcash/address/orchard.hpp" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" -#include "zcash/address/unified.h" #include "zcash/address/zip32.h" #include namespace libzcash { -// We use new classes here instead of CKeyID and CScriptID to prevent a cyclic dependency. -class P2PKHAddress: public uint160 { -public: - P2PKHAddress() {} - explicit P2PKHAddress(const std::vector& vch) : uint160(vch) {} -}; -class P2SHAddress: public uint160 { -public: - P2SHAddress() {} - explicit P2SHAddress(const std::vector& vch) : uint160(vch) {} -}; /** Protocol addresses that can receive funds in a transaction. */ typedef std::variant RawAddress; @@ -61,8 +51,8 @@ class UnknownReceiver { */ typedef std::variant< SaplingPaymentAddress, - P2SHAddress, - P2PKHAddress, + CScriptID, + CKeyID, UnknownReceiver> Receiver; struct ReceiverIterator { @@ -179,8 +169,8 @@ class TypecodeForReceiver { TypecodeForReceiver() {} uint32_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - uint32_t operator()(const libzcash::P2SHAddress &p2sh) const; - uint32_t operator()(const libzcash::P2PKHAddress &p2pkh) const; + uint32_t operator()(const CScriptID &p2sh) const; + uint32_t operator()(const CKeyID &p2pkh) const; uint32_t operator()(const libzcash::UnknownReceiver &p2pkh) const; }; @@ -192,8 +182,8 @@ class ReceiverToRawAddress { ReceiverToRawAddress() {} std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - std::optional operator()(const libzcash::P2SHAddress &p2sh) const; - std::optional operator()(const libzcash::P2PKHAddress &p2pkh) const; + std::optional operator()(const CScriptID &p2sh) const; + std::optional operator()(const CKeyID &p2pkh) const; std::optional operator()(const libzcash::UnknownReceiver &p2pkh) const; }; From 3bfb9d0c844dcb8ca89b371c4beb5365eb0b3d00 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 19 Nov 2021 16:29:24 -0700 Subject: [PATCH 153/514] Remove ZcashdUnifiedAddress in favor of UnifiedAddress --- src/zcash/address/unified.cpp | 24 ++++++++++++------------ src/zcash/address/unified.h | 24 +++--------------------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index 27067e6b1b7..d72a8686ece 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -39,8 +39,17 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { return ufvk; } -std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { - ZcashdUnifiedAddress ua; +std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { + UnifiedAddress ua; + + if (saplingKey.has_value()) { + auto saplingAddress = saplingKey.value().Address(j); + if (saplingAddress.has_value()) { + ua.AddReceiver(saplingAddress.value()); + } else { + return std::nullopt; + } + } if (transparentKey.has_value()) { auto childIndex = j.ToTransparentChildIndex(); @@ -53,16 +62,7 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversi CExtPubKey childKey; if (externalKey.Derive(childKey, childIndex.value())) { - ua.transparentAddress = childKey.pubkey.GetID(); - } else { - return std::nullopt; - } - } - - if (saplingKey.has_value()) { - auto saplingAddress = saplingKey.value().Address(j); - if (saplingAddress.has_value()) { - ua.saplingAddress = saplingAddress.value(); + ua.AddReceiver(childKey.pubkey.GetID()); } else { return std::nullopt; } diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 08269b7712f..be87a8d6b34 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -7,31 +7,13 @@ #include "zip32.h" #include "bip44.h" +#include "zcash/Address.hpp" namespace libzcash { class ZcashdUnifiedSpendingKey; class ZcashdUnifiedFullViewingKey; -class ZcashdUnifiedAddress { -private: - diversifier_index_t diversifier_index; - std::optional transparentAddress; - std::optional saplingAddress; - - friend class ZcashdUnifiedFullViewingKey; - - ZcashdUnifiedAddress() {} -public: - const std::optional& GetTransparentAddress() const { - return transparentAddress; - } - - const std::optional& GetSaplingPaymentAddress() const { - return saplingAddress; - } -}; - class ZcashdUnifiedFullViewingKey { private: std::optional transparentKey; @@ -49,9 +31,9 @@ class ZcashdUnifiedFullViewingKey { return saplingKey; } - std::optional Address(diversifier_index_t j) const; + std::optional Address(diversifier_index_t j) const; - std::pair FindAddress(diversifier_index_t j) const { + std::pair FindAddress(diversifier_index_t j) const { auto addr = Address(j); while (!addr.has_value()) { if (!j.increment()) From 7fa0e1a6a94e1fc5ef2b81b3cd661e2186a6fa97 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Fri, 3 Dec 2021 13:32:40 -0700 Subject: [PATCH 154/514] test: automatically add missing nuparams This test-only change allows python (rpc) tests to specify, for example, that NU5 should be activated at height X, without having to specify all the previous network upgrades. Previous upgrades can (and must) still be specified if they activate at different block heights (than, in this example, NU5). This makes tests easier to write (and read), especially as the number of network upgrades increases over time. Note that this change only affects zcashd behavior in regtest mode. For the other network modes (testnet and mainnet), the activation heights are hard-coded in chainparams.cpp. --- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/nuparams.py | 217 ++++++++++++++++++++++++++++++++++++ src/init.cpp | 23 ++++ 3 files changed, 241 insertions(+) create mode 100755 qa/rpc-tests/nuparams.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 9f36dbc1823..c7a1057e00d 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -122,6 +122,7 @@ 'feature_zip239.py', 'feature_zip244_blockcommitments.py', 'upgrade_golden.py', + 'nuparams.py', 'post_heartwood_rollback.py', 'feature_logging.py', 'feature_walletfile.py', diff --git a/qa/rpc-tests/nuparams.py b/qa/rpc-tests/nuparams.py new file mode 100755 index 00000000000..4482f116a4e --- /dev/null +++ b/qa/rpc-tests/nuparams.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + start_nodes, + nuparams, + HEARTWOOD_BRANCH_ID, + NU5_BRANCH_ID, +) + + +class NuparamsTest(BitcoinTestFramework): + ''' + Test that unspecified network upgrades are activated automatically; + this is really more of a test of the test framework. + ''' + + def __init__(self): + super().__init__() + self.num_nodes = 1 + self.setup_clean_chain = True + + def setup_network(self, split=False): + args = [[ + nuparams(HEARTWOOD_BRANCH_ID, 3), + nuparams(NU5_BRANCH_ID, 5), + ] * self.num_nodes] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, args) + self.is_network_split = False + self.sync_all() + + def run_test(self): + node = self.nodes[0] + # No blocks have been created, only the genesis block exists (height 0) + bci = node.getblockchaininfo() + assert_equal(bci['blocks'], 0) + upgrades = bci['upgrades'] + + overwinter = upgrades['5ba81b19'] + assert_equal(overwinter['name'], 'Overwinter') + assert_equal(overwinter['activationheight'], 1) + assert_equal(overwinter['status'], 'pending') + + sapling = upgrades['76b809bb'] + assert_equal(sapling['name'], 'Sapling') + assert_equal(sapling['activationheight'], 1) + assert_equal(sapling['status'], 'pending') + + blossom = upgrades['2bb40e60'] + assert_equal(blossom['name'], 'Blossom') + assert_equal(blossom['activationheight'], 3) + assert_equal(blossom['status'], 'pending') + + heartwood = upgrades['f5b9230b'] + assert_equal(heartwood['name'], 'Heartwood') + assert_equal(heartwood['activationheight'], 3) + assert_equal(heartwood['status'], 'pending') + + canopy = upgrades['e9ff75a6'] + assert_equal(canopy['name'], 'Canopy') + assert_equal(canopy['activationheight'], 5) + assert_equal(canopy['status'], 'pending') + + nu5 = upgrades['37519621'] + assert_equal(nu5['name'], 'NU5') + assert_equal(nu5['activationheight'], 5) + assert_equal(nu5['status'], 'pending') + + node.generate(1) + + # start_node() hardcodes Sapling and Overwinter to activate a height 1 + bci = node.getblockchaininfo() + assert_equal(bci['blocks'], 1) + upgrades = bci['upgrades'] + + overwinter = upgrades['5ba81b19'] + assert_equal(overwinter['name'], 'Overwinter') + assert_equal(overwinter['activationheight'], 1) + assert_equal(overwinter['status'], 'active') + + sapling = upgrades['76b809bb'] + assert_equal(sapling['name'], 'Sapling') + assert_equal(sapling['activationheight'], 1) + assert_equal(sapling['status'], 'active') + + blossom = upgrades['2bb40e60'] + assert_equal(blossom['name'], 'Blossom') + assert_equal(blossom['activationheight'], 3) + assert_equal(blossom['status'], 'pending') + + heartwood = upgrades['f5b9230b'] + assert_equal(heartwood['name'], 'Heartwood') + assert_equal(heartwood['activationheight'], 3) + assert_equal(heartwood['status'], 'pending') + + canopy = upgrades['e9ff75a6'] + assert_equal(canopy['name'], 'Canopy') + assert_equal(canopy['activationheight'], 5) + assert_equal(canopy['status'], 'pending') + + nu5 = upgrades['37519621'] + assert_equal(nu5['name'], 'NU5') + assert_equal(nu5['activationheight'], 5) + assert_equal(nu5['status'], 'pending') + + node.generate(1) + bci = node.getblockchaininfo() + assert_equal(bci['blocks'], 2) + upgrades = bci['upgrades'] + + overwinter = upgrades['5ba81b19'] + assert_equal(overwinter['name'], 'Overwinter') + assert_equal(overwinter['activationheight'], 1) + assert_equal(overwinter['status'], 'active') + + sapling = upgrades['76b809bb'] + assert_equal(sapling['name'], 'Sapling') + assert_equal(sapling['activationheight'], 1) + assert_equal(sapling['status'], 'active') + + blossom = upgrades['2bb40e60'] + assert_equal(blossom['name'], 'Blossom') + assert_equal(blossom['activationheight'], 3) + assert_equal(blossom['status'], 'pending') + + heartwood = upgrades['f5b9230b'] + assert_equal(heartwood['name'], 'Heartwood') + assert_equal(heartwood['activationheight'], 3) + assert_equal(heartwood['status'], 'pending') + + canopy = upgrades['e9ff75a6'] + assert_equal(canopy['name'], 'Canopy') + assert_equal(canopy['activationheight'], 5) + assert_equal(canopy['status'], 'pending') + + nu5 = upgrades['37519621'] + assert_equal(nu5['name'], 'NU5') + assert_equal(nu5['activationheight'], 5) + assert_equal(nu5['status'], 'pending') + + node.generate(2) + bci = node.getblockchaininfo() + assert_equal(bci['blocks'], 4) + upgrades = bci['upgrades'] + + overwinter = upgrades['5ba81b19'] + assert_equal(overwinter['name'], 'Overwinter') + assert_equal(overwinter['activationheight'], 1) + assert_equal(overwinter['status'], 'active') + + sapling = upgrades['76b809bb'] + assert_equal(sapling['name'], 'Sapling') + assert_equal(sapling['activationheight'], 1) + assert_equal(sapling['status'], 'active') + + blossom = upgrades['2bb40e60'] + assert_equal(blossom['name'], 'Blossom') + assert_equal(blossom['activationheight'], 3) + assert_equal(blossom['status'], 'active') + + heartwood = upgrades['f5b9230b'] + assert_equal(heartwood['name'], 'Heartwood') + assert_equal(heartwood['activationheight'], 3) + assert_equal(heartwood['status'], 'active') + + canopy = upgrades['e9ff75a6'] + assert_equal(canopy['name'], 'Canopy') + assert_equal(canopy['activationheight'], 5) + assert_equal(canopy['status'], 'pending') + + nu5 = upgrades['37519621'] + assert_equal(nu5['name'], 'NU5') + assert_equal(nu5['activationheight'], 5) + assert_equal(nu5['status'], 'pending') + + node.generate(1) + bci = node.getblockchaininfo() + assert_equal(bci['blocks'], 5) + upgrades = bci['upgrades'] + + overwinter = upgrades['5ba81b19'] + assert_equal(overwinter['name'], 'Overwinter') + assert_equal(overwinter['activationheight'], 1) + assert_equal(overwinter['status'], 'active') + + sapling = upgrades['76b809bb'] + assert_equal(sapling['name'], 'Sapling') + assert_equal(sapling['activationheight'], 1) + assert_equal(sapling['status'], 'active') + + blossom = upgrades['2bb40e60'] + assert_equal(blossom['name'], 'Blossom') + assert_equal(blossom['activationheight'], 3) + assert_equal(blossom['status'], 'active') + + heartwood = upgrades['f5b9230b'] + assert_equal(heartwood['name'], 'Heartwood') + assert_equal(heartwood['activationheight'], 3) + assert_equal(heartwood['status'], 'active') + + canopy = upgrades['e9ff75a6'] + assert_equal(canopy['name'], 'Canopy') + assert_equal(canopy['activationheight'], 5) + assert_equal(canopy['status'], 'active') + + nu5 = upgrades['37519621'] + assert_equal(nu5['name'], 'NU5') + assert_equal(nu5['activationheight'], 5) + assert_equal(nu5['status'], 'active') + + +if __name__ == '__main__': + NuparamsTest().main() diff --git a/src/init.cpp b/src/init.cpp index 365272ebf2d..b632d7e2b29 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1143,6 +1143,29 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) return InitError(strprintf("Invalid network upgrade (%s)", vDeploymentParams[0])); } } + + // To make testing easier (this code path is active only for regtest), activate missing network versions, + // so for example, if a Python (RPC) test does: + // extra_args = [ + // nuparams(BLOSSOM_BRANCH_ID, 205), + // nuparams(HEARTWOOD_BRANCH_ID, 205), + // nuparams(CANOPY_BRANCH_ID, 205), + // nuparams(NU5_BRANCH_ID, 210), + // ] + // + // This can be simplified to: + // extra_args = [ + // nuparams(CANOPY_BRANCH_ID, 205), + // nuparams(NU5_BRANCH_ID, 210), + // ] + const auto& consensus = chainparams.GetConsensus(); + int nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + for (auto i = Consensus::MAX_NETWORK_UPGRADES-1; i >= Consensus::BASE_SPROUT + 1; --i) { + if (consensus.vUpgrades[i].nActivationHeight == Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) { + UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex(i), nActivationHeight); + } + nActivationHeight = consensus.vUpgrades[i].nActivationHeight; + } } if (mapArgs.count("-nurejectoldversions")) { From df0534e35a10fad4f55051fb597b6756d58c7d8f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 13 Dec 2021 21:22:13 +0000 Subject: [PATCH 155/514] cargo update Thanks to metrics-rs/metrics#231 being merged, our dependency tree is now almost entirely de-duplicated! --- Cargo.lock | 187 ++++++++++++++--------------------------------------- 1 file changed, 47 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2337ac4af97..94e8f8d0c60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "opaque-debug", @@ -72,11 +72,11 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "atomic-shim" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20fdac7156779a1a30d970e838195558b4810dd06aa69e7c7461bdc518edf9b" +checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77" dependencies = [ - "crossbeam", + "crossbeam-utils", ] [[package]] @@ -106,7 +106,7 @@ dependencies = [ "bitvec", "blake2s_simd", "byteorder", - "crossbeam-channel 0.5.1", + "crossbeam-channel", "ff", "group", "lazy_static", @@ -247,12 +247,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -265,7 +259,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "zeroize", @@ -308,49 +302,14 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-channel 0.4.4", - "crossbeam-deque 0.7.4", - "crossbeam-epoch 0.8.2", - "crossbeam-queue", - "crossbeam-utils 0.7.2", -] - -[[package]] -name = "crossbeam-channel" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" -dependencies = [ - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - [[package]] name = "crossbeam-channel" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.5", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" -dependencies = [ - "crossbeam-epoch 0.8.2", - "crossbeam-utils 0.7.2", - "maybe-uninit", + "cfg-if", + "crossbeam-utils", ] [[package]] @@ -359,24 +318,9 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch 0.9.5", - "crossbeam-utils 0.8.5", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "lazy_static", - "maybe-uninit", - "memoffset 0.5.6", - "scopeguard", + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] @@ -385,42 +329,20 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.5", + "cfg-if", + "crossbeam-utils", "lazy_static", - "memoffset 0.6.4", + "memoffset", "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "lazy_static", -] - [[package]] name = "crossbeam-utils" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -459,7 +381,7 @@ version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "num_cpus", ] @@ -622,7 +544,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -633,7 +555,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -779,7 +701,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -825,9 +747,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" [[package]] name = "libm" @@ -886,7 +808,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -907,12 +829,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.4.1" @@ -921,18 +837,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memoffset" -version = "0.5.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] @@ -989,15 +896,15 @@ dependencies = [ [[package]] name = "metrics-util" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c9b6aee519e1461b678952d3671652bb341d0664b1188f895a436a4e2e6ffa" +checksum = "1174223789e331d9d47a4a953dac36e397db60fa8d2a111ac505388c6c7fe32e" dependencies = [ "ahash", "aho-corasick", "atomic-shim", - "crossbeam-epoch 0.9.5", - "crossbeam-utils 0.8.5", + "crossbeam-epoch", + "crossbeam-utils", "dashmap", "hashbrown", "indexmap", @@ -1169,7 +1076,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall", @@ -1270,9 +1177,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a" dependencies = [ "unicode-xid", ] @@ -1283,7 +1190,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" dependencies = [ - "crossbeam-utils 0.8.5", + "crossbeam-utils", "libc", "mach", "once_cell", @@ -1383,7 +1290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", - "crossbeam-deque 0.8.1", + "crossbeam-deque", "either", "rayon-core", ] @@ -1394,9 +1301,9 @@ version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ - "crossbeam-channel 0.5.1", - "crossbeam-deque 0.8.1", - "crossbeam-utils 0.8.5", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", "lazy_static", "num_cpus", ] @@ -1471,18 +1378,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" dependencies = [ "proc-macro2", "quote", @@ -1496,7 +1403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest", "opaque-debug", @@ -1529,7 +1436,7 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "winapi", ] @@ -1666,7 +1573,7 @@ version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1678,7 +1585,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94571df2eae3ed4353815ea5a90974a594a1792d8782ff2cbcc9392d1101f366" dependencies = [ - "crossbeam-channel 0.5.1", + "crossbeam-channel", "time", "tracing-subscriber", ] @@ -1705,9 +1612,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507ec620f809cdf07cccb5bc57b13069a88031b795efd4079b1c71b66c1613d" +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" dependencies = [ "ansi_term", "lazy_static", @@ -1791,7 +1698,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] From 640f31463fc3b37d076d0c8fabce4b7ec3697e40 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sat, 4 Dec 2021 11:00:47 -0700 Subject: [PATCH 156/514] Update to ufvk zcash_address build. --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 10 +++++----- src/rust/src/address_ffi.rs | 9 ++++++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee014014009..a2119f065b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,7 +521,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "blake2b_simd", "byteorder", @@ -530,7 +530,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "blake2b_simd", ] @@ -1885,7 +1885,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "bech32", "blake2b_simd", @@ -1897,7 +1897,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "byteorder", "nonempty", @@ -1906,7 +1906,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "bigint", "blake2b_simd", @@ -1916,7 +1916,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "blake2b_simd", "byteorder", @@ -1931,7 +1931,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "aes", "bip0039", @@ -1965,7 +1965,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d" +source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f" dependencies = [ "bellman", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index b104fd3df77..9e7113993a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,8 +73,8 @@ codegen-units = 1 ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "68b790c7dadade049f44ad4aafa0ff71a3a10e91" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" } diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index a77488743b3..bab50741ee6 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -5,7 +5,10 @@ use std::{ }; use libc::{c_char, c_void}; -use zcash_address::{unified, FromAddress, Network, ToAddress, ZcashAddress}; +use zcash_address::{ + unified::{self, Container, Encoding}, + FromAddress, Network, ToAddress, ZcashAddress, +}; use zcash_primitives::sapling; pub type UnifiedAddressObj = NonNull; @@ -69,7 +72,7 @@ impl UnifiedAddressHelper { } self.ua - .receivers() + .items() .into_iter() .map(|receiver| match receiver { unified::Receiver::Orchard(data) => { @@ -209,7 +212,7 @@ pub extern "C" fn zcash_address_serialize_unified( } }; - let ua: unified::Address = match receivers.try_into() { + let ua = match unified::Address::try_from_items(receivers) { Ok(ua) => ua, Err(e) => { tracing::error!("{}", e); From 4257abd32839aad9c771f679ca2f129c4a12699f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 30 Nov 2021 20:35:26 -0700 Subject: [PATCH 157/514] Adds SaplingDiversifiableFullViewingKey Adds SaplingDiversifiableFullViewingKey as a superclass of SaplingExtendedFullViewingKey, and reduces the Sapling component of UnifiedFullViewingKey to the new intermediate type. This permits deserialization from the data that will be present in the serialized form of the Sapling component of a UFVK. --- src/rust/include/librustzcash.h | 10 ++++--- src/rust/src/rustzcash.rs | 27 +++++++++-------- src/wallet/test/rpc_wallet_tests.cpp | 2 +- src/zcash/address/unified.h | 4 +-- src/zcash/address/zip32.cpp | 28 +++++++++--------- src/zcash/address/zip32.h | 43 ++++++++++++++++------------ 6 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index 94d49535fc1..048c70ef5e4 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -317,8 +317,9 @@ extern "C" { * - addr_ret: [c_uchar; 43] array to which the returned address will be written, * if the specified diversifier index `j` produces a valid address. */ - bool librustzcash_zip32_xfvk_address( - const unsigned char *xfvk, + bool librustzcash_zip32_sapling_address( + const unsigned char *fvk, + const unsigned char *dk, const unsigned char *j, unsigned char *addr_ret ); @@ -338,8 +339,9 @@ extern "C" { * returned address was found * - addr_ret: [c_uchar; 43] array to which the returned address will be written */ - bool librustzcash_zip32_find_xfvk_address( - const unsigned char *xfvk, + bool librustzcash_zip32_find_sapling_address( + const unsigned char *fvk, + const unsigned char *dk, const unsigned char *j, unsigned char *j_ret, unsigned char *addr_ret diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index 3520008897d..ed62203a5e5 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -46,14 +46,15 @@ use zcash_primitives::{ block::equihash, constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, merkle_tree::MerklePath, - sapling::{merkle_hash, spend_sig}, sapling::{ + keys::FullViewingKey, note_encryption::sapling_ka_agree, redjubjub::{self, Signature}, Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ViewingKey, }, + sapling::{merkle_hash, spend_sig}, transaction::components::Amount, - zip32, + zip32::{self, sapling_address, sapling_find_address}, }; use zcash_proofs::{ circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, @@ -1080,16 +1081,17 @@ pub extern "C" fn librustzcash_zip32_xfvk_derive( /// Derive a PaymentAddress from an ExtendedFullViewingKey. #[no_mangle] -pub extern "C" fn librustzcash_zip32_xfvk_address( - xfvk: *const [c_uchar; 169], +pub extern "C" fn librustzcash_zip32_sapling_address( + fvk: *const [c_uchar; 96], + dk: *const [c_uchar; 32], j: *const [c_uchar; 11], addr_ret: *mut [c_uchar; 43], ) -> bool { - let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) - .expect("valid ExtendedFullViewingKey"); + let fvk = FullViewingKey::read(&unsafe { *fvk }[..]).expect("valid Sapling FullViewingKey"); + let dk = zip32::DiversifierKey(unsafe { *dk }); let j = zip32::DiversifierIndex(unsafe { *j }); - match xfvk.address(j) { + match sapling_address(&fvk, &dk, j) { Some(addr) => { let addr_ret = unsafe { &mut *addr_ret }; addr_ret.copy_from_slice(&addr.to_bytes()); @@ -1102,17 +1104,18 @@ pub extern "C" fn librustzcash_zip32_xfvk_address( /// Derive a PaymentAddress from an ExtendedFullViewingKey. #[no_mangle] -pub extern "C" fn librustzcash_zip32_find_xfvk_address( - xfvk: *const [c_uchar; 169], +pub extern "C" fn librustzcash_zip32_find_sapling_address( + fvk: *const [c_uchar; 96], + dk: *const [c_uchar; 32], j: *const [c_uchar; 11], j_ret: *mut [c_uchar; 11], addr_ret: *mut [c_uchar; 43], ) -> bool { - let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) - .expect("valid ExtendedFullViewingKey"); + let fvk = FullViewingKey::read(&unsafe { *fvk }[..]).expect("valid Sapling FullViewingKey"); + let dk = zip32::DiversifierKey(unsafe { *dk }); let j = zip32::DiversifierIndex(unsafe { *j }); - match xfvk.find_address(j) { + match sapling_find_address(&fvk, &dk, j) { Some((j, addr)) => { let j_ret = unsafe { &mut *j_ret }; let addr_ret = unsafe { &mut *addr_ret }; diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 7b91c216752..0528ab0e654 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -80,7 +80,7 @@ static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) { return usk.value() .ToFullViewingKey() - .GetSaplingExtendedFullViewingKey().value() + .GetSaplingKey().value() .FindAddress(libzcash::diversifier_index_t(0)).first; } diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index be87a8d6b34..68de2792002 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -17,7 +17,7 @@ class ZcashdUnifiedFullViewingKey; class ZcashdUnifiedFullViewingKey { private: std::optional transparentKey; - std::optional saplingKey; + std::optional saplingKey; ZcashdUnifiedFullViewingKey() {} @@ -27,7 +27,7 @@ class ZcashdUnifiedFullViewingKey { return transparentKey; } - const std::optional& GetSaplingExtendedFullViewingKey() const { + const std::optional& GetSaplingKey() const { return saplingKey; } diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index fc558edef85..c0512a43da9 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -83,15 +83,16 @@ std::optional SaplingExtendedFullViewingKey::Deri } std::optional - SaplingExtendedFullViewingKey::Address(diversifier_index_t j) const + SaplingDiversifiableFullViewingKey::Address(diversifier_index_t j) const { - CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION); - ss_xfvk << *this; - CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end()); + CDataStream ss_fvk(SER_NETWORK, PROTOCOL_VERSION); + ss_fvk << fvk; + CSerializeData fvk_bytes(ss_fvk.begin(), ss_fvk.end()); CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize); - if (librustzcash_zip32_xfvk_address( - reinterpret_cast(xfvk_bytes.data()), + if (librustzcash_zip32_sapling_address( + reinterpret_cast(fvk_bytes.data()), + dk.begin(), j.begin(), reinterpret_cast(addr_bytes.data()))) { CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION); @@ -103,17 +104,18 @@ std::optional } } -libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() const +libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::DefaultAddress() const { - CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION); - ss_xfvk << *this; - CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end()); + CDataStream ss_fvk(SER_NETWORK, PROTOCOL_VERSION); + ss_fvk << fvk; + CSerializeData fvk_bytes(ss_fvk.begin(), ss_fvk.end()); diversifier_index_t j_default; diversifier_index_t j_ret; CSerializeData addr_bytes_ret(libzcash::SerializedSaplingPaymentAddressSize); - if (librustzcash_zip32_find_xfvk_address( - reinterpret_cast(xfvk_bytes.data()), + if (librustzcash_zip32_find_sapling_address( + reinterpret_cast(fvk_bytes.data()), + dk.begin(), j_default.begin(), j_ret.begin(), reinterpret_cast(addr_bytes_ret.data()))) { CDataStream ss_addr(addr_bytes_ret, SER_NETWORK, PROTOCOL_VERSION); @@ -122,7 +124,7 @@ libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() return addr; } else { // If we can't obtain a default address, we are *very* unlucky... - throw std::runtime_error("SaplingExtendedFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!"); + throw std::runtime_error("SaplingDiversifiableFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!"); } } diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 7f9cf4cc93d..608918e685b 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -117,28 +117,11 @@ class diversifier_index_t : public base_blob<88> { } }; -struct SaplingExtendedFullViewingKey { - uint8_t depth; - uint32_t parentFVKTag; - uint32_t childIndex; - uint256 chaincode; +class SaplingDiversifiableFullViewingKey { +public: libzcash::SaplingFullViewingKey fvk; uint256 dk; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(depth); - READWRITE(parentFVKTag); - READWRITE(childIndex); - READWRITE(chaincode); - READWRITE(fvk); - READWRITE(dk); - } - - std::optional Derive(uint32_t i) const; - // Attempts to construct a valid payment address with diversifier index // `j`; returns std::nullopt if `j` does not result in a valid diversifier // given this xfvk. @@ -158,6 +141,28 @@ struct SaplingExtendedFullViewingKey { } libzcash::SaplingPaymentAddress DefaultAddress() const; +}; + +class SaplingExtendedFullViewingKey: public SaplingDiversifiableFullViewingKey { +public: + uint8_t depth; + uint32_t parentFVKTag; + uint32_t childIndex; + uint256 chaincode; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(depth); + READWRITE(parentFVKTag); + READWRITE(childIndex); + READWRITE(chaincode); + READWRITE(fvk); + READWRITE(dk); + } + + std::optional Derive(uint32_t i) const; friend inline bool operator==(const SaplingExtendedFullViewingKey& a, const SaplingExtendedFullViewingKey& b) { return ( From c78887a14870ef4a66bc10ae994931e6b0095dba Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 1 Dec 2021 19:09:16 -0700 Subject: [PATCH 158/514] Add Rust FFI components for unified full viewing keys. --- src/rust/include/rust/unified_keys.h | 102 +++++++++++++++++++++ src/rust/src/address_ffi.rs | 2 +- src/rust/src/rustzcash.rs | 1 + src/rust/src/unified_keys_ffi.rs | 132 +++++++++++++++++++++++++++ 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 src/rust/include/rust/unified_keys.h create mode 100644 src/rust/src/unified_keys_ffi.rs diff --git a/src/rust/include/rust/unified_keys.h b/src/rust/include/rust/unified_keys.h new file mode 100644 index 00000000000..10ea8ea01d9 --- /dev/null +++ b/src/rust/include/rust/unified_keys.h @@ -0,0 +1,102 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_RUST_INCLUDE_RUST_UNIFIED_KEYS_H +#define ZCASH_RUST_INCLUDE_RUST_UNIFIED_KEYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Unified full viewing keys +// + +/** + * Void pointer type representing a reference to a Rust-allocated + * unified::Ufvk value + */ +struct UnifiedFullViewingKeyPtr; +typedef struct UnifiedFullViewingKeyPtr UnifiedFullViewingKeyPtr; + +/** + * Free the memory allocated for the given address::unified::Ufvk; + */ +void unified_full_viewing_key_free(UnifiedFullViewingKeyPtr* ptr); + +/** + * Clones the given Unified full viewing key and returns + * a pointer to the newly created value. Both the original + * one's memory and the newly allocated one need to be freed + * independently. + */ +UnifiedFullViewingKeyPtr* unified_full_viewing_key_clone( + const UnifiedFullViewingKeyPtr* ptr); + +/** + * Parses a unified full viewing key from the given string. + * + * - If the key does not parse correctly, or the network for which the + * key was encoded does not match the specified network the returned + * pointer will be null. + */ +UnifiedFullViewingKeyPtr* unified_full_viewing_key_parse( + const char* network, + const char* str); + +/** + * Serializes a unified full viewing key and returns its string representation. + */ +char* unified_full_viewing_key_serialize( + const char* network, + const UnifiedFullViewingKeyPtr* full_viewing_key); + +/** + * Reads the transparent component of a unified viewing key. + * + * `tkeyout` must be of length 65. + * + * Returns `true` if the UFVK contained a transparent component, + * `false` otherwise. The bytes of the transparent key will be + * copied to tkeyout + */ +bool unified_full_viewing_key_read_transparent( + const UnifiedFullViewingKeyPtr* full_viewing_key, + unsigned char *tkeyout); + +/** + * Reads the Sapling component of a unified viewing key. + * + * `skeyout` must be of length 128. + * + * Returns `true` if the UFVK contained a Sapling component, + * false otherwise. + */ +bool unified_full_viewing_key_read_sapling( + const UnifiedFullViewingKeyPtr* full_viewing_key, + unsigned char *skeyout); + +/** + * Sets the Sapling component of a unified viewing key. + * + * `t_key` must be of length 65 and must be the concatenated + * bytes of the serialized `(ChainCode, CPubKey)` pair. + * + * `sapling_key` must be of length 128 and must be the concatenated + * bytes of the serialized `(SaplingFullViewingKey, DiversifierKey)` + * pair. + * + * Returns the newly allocated UFVK if the operation succeeds, + * or the null pointer otherwise. + */ +UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components( + const unsigned char *t_key, + const unsigned char *sapling_key); + +#ifdef __cplusplus +} +#endif + +#endif // ZCASH_RUST_INCLUDE_RUST_UNIFIED_KEYS_H + diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index bab50741ee6..808af7f54a8 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -26,7 +26,7 @@ pub type GetReceiverLenCb = pub type GetReceiverDataCb = unsafe extern "C" fn(ua: Option, index: usize, data: *mut u8, length: usize); -fn network_from_cstr(network: *const c_char) -> Option { +pub(crate) fn network_from_cstr(network: *const c_char) -> Option { match unsafe { CStr::from_ptr(network) }.to_str().unwrap() { "main" => Some(Network::Main), "test" => Some(Network::Test), diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index ed62203a5e5..86f11f4e5c6 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -75,6 +75,7 @@ mod incremental_merkle_tree_ffi; mod orchard_ffi; mod orchard_keys_ffi; mod transaction_ffi; +mod unified_keys_ffi; mod zip339_ffi; mod test_harness_ffi; diff --git a/src/rust/src/unified_keys_ffi.rs b/src/rust/src/unified_keys_ffi.rs new file mode 100644 index 00000000000..eeca01c1855 --- /dev/null +++ b/src/rust/src/unified_keys_ffi.rs @@ -0,0 +1,132 @@ +use libc::c_char; +use std::ffi::{CStr, CString}; +use tracing::error; + +use zcash_address::unified::{Container, Encoding, Fvk, Ufvk}; + +use crate::address_ffi::network_from_cstr; + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_free(key: *mut Ufvk) { + if !key.is_null() { + drop(unsafe { Box::from_raw(key) }); + } +} + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_clone(key: *const Ufvk) -> *mut Ufvk { + unsafe { key.as_ref() } + .map(|key| Box::into_raw(Box::new(key.clone()))) + .unwrap_or(std::ptr::null_mut()) +} + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_parse( + network: *const c_char, + encoded: *const c_char, +) -> *mut Ufvk { + let network = match network_from_cstr(network) { + Some(n) => n, + None => return std::ptr::null_mut(), + }; + + match unsafe { CStr::from_ptr(encoded) }.to_str() { + Ok(encoded) => match Ufvk::decode(encoded) { + Ok((parsed_network, fvk)) => { + if parsed_network == network { + Box::into_raw(Box::new(fvk)) + } else { + error!( + "Key was encoded for a different network than what was requested: {:?}", + parsed_network + ); + std::ptr::null_mut() + } + } + Err(e) => { + error!("Failure decoding unified full viewing key: {}", e); + std::ptr::null_mut() + } + }, + Err(e) => { + error!("Failure reading bytes of unified full viewing key: {}", e); + std::ptr::null_mut() + } + } +} + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_serialize( + network: *const c_char, + key: *const Ufvk, +) -> *mut c_char { + let key = unsafe { key.as_ref() }.expect("Unified full viewing key pointer may not be null."); + match network_from_cstr(network) { + Some(n) => CString::new(key.encode(&n)).unwrap().into_raw(), + None => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_read_transparent( + key: *const Ufvk, + out: *mut [u8; 65], +) -> bool { + let key = unsafe { key.as_ref() }.expect("Unified full viewing key pointer may not be null."); + let out = unsafe { &mut *out }; + + for r in &key.items() { + if let Fvk::P2pkh(data) = r { + *out = *data; + return true; + } + } + + false +} + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_read_sapling( + key: *const Ufvk, + out: *mut [u8; 128], +) -> bool { + let key = unsafe { key.as_ref() }.expect("Unified full viewing key pointer may not be null."); + let out = unsafe { &mut *out }; + + for r in &key.items() { + if let Fvk::Sapling(data) = r { + *out = *data; + return true; + } + } + + false +} + +#[no_mangle] +pub extern "C" fn unified_full_viewing_key_from_components( + t_key: *const [u8; 65], + sapling_key: *const [u8; 128], +) -> *mut Ufvk { + let t_key = unsafe { t_key.as_ref() }; + let sapling_key = unsafe { sapling_key.as_ref() }; + + let mut items = vec![]; + if let Some(t_bytes) = t_key { + items.push(Fvk::P2pkh(*t_bytes)); + } + if let Some(sapling_bytes) = sapling_key { + items.push(Fvk::Sapling(*sapling_bytes)); + } + + match Ufvk::try_from_items(items) { + Ok(ufvk) => Box::into_raw(Box::new(ufvk)), + Err(e) => { + error!( + "An error occurred constructing the unified full viewing key: {:?}", + e + ); + std::ptr::null_mut() + } + } +} From 217c56811d1f7c4e38caa2cd7fe46bac66215d98 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Nov 2021 16:40:29 -0700 Subject: [PATCH 159/514] Add UnifiedFullViewingKey type. This type is backed by the `zcash_address` implementaion of unified full viewing keys. It is intended for serialization and parsing of UFVKs, and provides conversion functions that allow for construction to and from ZcashdUnifiedFullViewingKey values. --- src/key_constants.h | 2 + src/key_io.cpp | 11 +++ src/pubkey.cpp | 8 +++ src/pubkey.h | 49 +++++++++++++ src/rust/include/rust/unified_keys.h | 8 +-- src/rust/src/address_ffi.rs | 2 +- src/rust/src/unified_keys_ffi.rs | 9 ++- src/wallet/wallet.cpp | 4 ++ src/wallet/wallet.h | 1 + src/zcash/Address.cpp | 104 +++++++++++++++++++++++++++ src/zcash/Address.hpp | 83 +++++++++++++++++++-- src/zcash/address/unified.cpp | 47 ++++++++++-- src/zcash/address/unified.h | 21 +++--- src/zcash/address/zip32.h | 20 ++++++ 14 files changed, 339 insertions(+), 30 deletions(-) diff --git a/src/key_constants.h b/src/key_constants.h index aa999dd5810..70b8f87e9f9 100644 --- a/src/key_constants.h +++ b/src/key_constants.h @@ -5,6 +5,8 @@ #ifndef ZCASH_KEY_CONSTANTS_H #define ZCASH_KEY_CONSTANTS_H +#include + class KeyConstants { public: diff --git a/src/key_io.cpp b/src/key_io.cpp index e22243f9cab..8d3a2b8d5be 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -190,6 +190,10 @@ class ViewingKeyEncoder return ret; } + std::string operator()(const libzcash::UnifiedFullViewingKey& ufvk) const { + return ufvk.Encode(keyConstants); + } + std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; @@ -485,6 +489,13 @@ std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) libzcash::ViewingKey KeyIO::DecodeViewingKey(const std::string& str) { + // Try parsing as a Unified full viewing key + auto ufvk = libzcash::UnifiedFullViewingKey::Decode(str, keyConstants); + if (ufvk.has_value()) { + return ufvk.value(); + } + + // Fall back on trying Sprout or Sapling. return DecodeAny( diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 32808a999b2..58a23873b46 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -134,6 +134,14 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, NULL, &sig)); } +/* static */ std::optional CChainablePubKey::FromParts(ChainCode chaincode, CPubKey pubkey) { + if (pubkey.IsCompressed()) { + return CChainablePubKey(chaincode, pubkey); + } else { + return std::nullopt; + } +} + /* static */ int ECCVerifyHandle::refcount = 0; ECCVerifyHandle::ECCVerifyHandle() diff --git a/src/pubkey.h b/src/pubkey.h index 94ff547c4a6..04739eaea33 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -203,6 +203,55 @@ class CPubKey bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const; }; +class CChainablePubKey { +private: + ChainCode chaincode; + CPubKey pubkey; + + CChainablePubKey() {} + CChainablePubKey(ChainCode chaincode, CPubKey pubkey): chaincode(chaincode), pubkey(pubkey) {} +public: + static std::optional FromParts(ChainCode chaincode, CPubKey pubkey); + + const ChainCode& GetChainCode() const { + return chaincode; + } + + const CPubKey& GetPubKey() const { + return pubkey; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(chaincode); + if (ser_action.ForRead()) { + std::array pubkeyBytes; + READWRITE(pubkeyBytes); + pubkey = CPubKey(pubkeyBytes.begin(), pubkeyBytes.end()); + assert(pubkey.IsCompressed()); + } else { + assert(pubkey.size() == 33); + std::array pubkeyBytes; + std::copy(pubkey.begin(), pubkey.end(), pubkeyBytes.begin()); + READWRITE(pubkeyBytes); + } + } + + template + static CChainablePubKey Read(Stream& stream) { + CChainablePubKey key; + stream >> key; + return key; + } + + friend bool operator==(const CChainablePubKey &a, const CChainablePubKey &b) + { + return a.chaincode == b.chaincode && a.pubkey == b.pubkey; + } +}; + struct CExtPubKey { unsigned char nDepth; unsigned char vchFingerprint[4]; diff --git a/src/rust/include/rust/unified_keys.h b/src/rust/include/rust/unified_keys.h index 10ea8ea01d9..6d993bd4767 100644 --- a/src/rust/include/rust/unified_keys.h +++ b/src/rust/include/rust/unified_keys.h @@ -63,7 +63,7 @@ char* unified_full_viewing_key_serialize( */ bool unified_full_viewing_key_read_transparent( const UnifiedFullViewingKeyPtr* full_viewing_key, - unsigned char *tkeyout); + unsigned char* tkeyout); /** * Reads the Sapling component of a unified viewing key. @@ -75,7 +75,7 @@ bool unified_full_viewing_key_read_transparent( */ bool unified_full_viewing_key_read_sapling( const UnifiedFullViewingKeyPtr* full_viewing_key, - unsigned char *skeyout); + unsigned char* skeyout); /** * Sets the Sapling component of a unified viewing key. @@ -91,8 +91,8 @@ bool unified_full_viewing_key_read_sapling( * or the null pointer otherwise. */ UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components( - const unsigned char *t_key, - const unsigned char *sapling_key); + const unsigned char* t_key, + const unsigned char* sapling_key); #ifdef __cplusplus } diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index 808af7f54a8..a670d181523 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -212,7 +212,7 @@ pub extern "C" fn zcash_address_serialize_unified( } }; - let ua = match unified::Address::try_from_items(receivers) { + let ua = match unified::Address::try_from_items_preserving_order(receivers) { Ok(ua) => ua, Err(e) => { tracing::error!("{}", e); diff --git a/src/rust/src/unified_keys_ffi.rs b/src/rust/src/unified_keys_ffi.rs index eeca01c1855..b7ecfc38373 100644 --- a/src/rust/src/unified_keys_ffi.rs +++ b/src/rust/src/unified_keys_ffi.rs @@ -27,11 +27,14 @@ pub extern "C" fn unified_full_viewing_key_parse( ) -> *mut Ufvk { let network = match network_from_cstr(network) { Some(n) => n, - None => return std::ptr::null_mut(), + None => { + return std::ptr::null_mut(); + } }; match unsafe { CStr::from_ptr(encoded) }.to_str() { - Ok(encoded) => match Ufvk::decode(encoded) { + Ok(encoded) => { + match Ufvk::decode(encoded) { Ok((parsed_network, fvk)) => { if parsed_network == network { Box::into_raw(Box::new(fvk)) @@ -47,7 +50,7 @@ pub extern "C" fn unified_full_viewing_key_parse( error!("Failure decoding unified full viewing key: {}", e); std::ptr::null_mut() } - }, + }}, Err(e) => { error!("Failure reading bytes of unified full viewing key: {}", e); std::ptr::null_mut() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 91cd9ed9b3b..f1238644d06 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5506,6 +5506,10 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu } } +KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::UnifiedFullViewingKey& no) const { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unified full viewing key import is not yet supported."); +} + KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6e1ffa2fa8d..2beb7d18c33 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1481,6 +1481,7 @@ class AddViewingKeyToWallet KeyAddResult operator()(const libzcash::SproutViewingKey &sk) const; KeyAddResult operator()(const libzcash::SaplingExtendedFullViewingKey &sk) const; + KeyAddResult operator()(const libzcash::UnifiedFullViewingKey &sk) const; KeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 568b3bbff6f..3481ae08db4 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -1,6 +1,10 @@ #include "Address.hpp" +#include "zcash/address/unified.h" +#include "utilstrencodings.h" #include +#include +#include const uint8_t ZCASH_UA_TYPECODE_P2PKH = 0x00; const uint8_t ZCASH_UA_TYPECODE_P2SH = 0x01; @@ -51,6 +55,14 @@ std::pair AddressInfoFromViewingKey::operator()(con std::pair AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &xfvk) const { return std::make_pair("sapling", xfvk.DefaultAddress()); } +std::pair AddressInfoFromViewingKey::operator()(const UnifiedFullViewingKey &ufvk) const { + return std::make_pair( + "unified", + ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(ufvk) + .FindAddress(diversifier_index_t(0)) + .first + ); +} std::pair AddressInfoFromViewingKey::operator()(const InvalidEncoding&) const { throw std::invalid_argument("Cannot derive default address from invalid viewing key"); } @@ -176,3 +188,95 @@ std::set GetRawAddresses::operator()( } return ret; } + +std::optional libzcash::UnifiedFullViewingKey::Decode( + const std::string& str, + const KeyConstants& keyConstants) { + UnifiedFullViewingKeyPtr* ptr = unified_full_viewing_key_parse( + keyConstants.NetworkIDString().c_str(), + str.c_str()); + if (ptr == nullptr) { + return std::nullopt; + } else { + return UnifiedFullViewingKey(ptr); + } +} + +std::string libzcash::UnifiedFullViewingKey::Encode(const KeyConstants& keyConstants) const { + auto encoded = unified_full_viewing_key_serialize( + keyConstants.NetworkIDString().c_str(), + inner.get()); + // Copy the C-string into C++. + std::string res(encoded); + // Free the C-string. + zcash_address_string_free(encoded); + return res; +} + +std::optional libzcash::UnifiedFullViewingKey::GetSaplingKey() const { + std::vector buffer(128); + if (unified_full_viewing_key_read_sapling(inner.get(), buffer.data())) { + CDataStream ss(buffer, SER_NETWORK, PROTOCOL_VERSION); + return SaplingDiversifiableFullViewingKey::Read(ss); + } else { + return std::nullopt; + } +} + +std::optional libzcash::UnifiedFullViewingKey::GetTransparentKey() const { + std::vector buffer(65); + if (unified_full_viewing_key_read_transparent(inner.get(), buffer.data())) { + CDataStream ss(buffer, SER_NETWORK, PROTOCOL_VERSION); + return CChainablePubKey::Read(ss); + } else { + return std::nullopt; + } +} + +bool libzcash::UnifiedFullViewingKeyBuilder::AddTransparentKey(const CChainablePubKey& key) { + if (t_bytes.has_value()) return false; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << key; + assert(ss.size() == 65); + std::vector ss_bytes(ss.begin(), ss.end()); + t_bytes = ss_bytes; + return true; +} + +bool libzcash::UnifiedFullViewingKeyBuilder::AddSaplingKey(const SaplingDiversifiableFullViewingKey& key) { + if (sapling_bytes.has_value()) return false; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << key; + assert(ss.size() == 128); + std::vector ss_bytes(ss.begin(), ss.end()); + sapling_bytes = ss_bytes; + return true; +} + +std::optional libzcash::UnifiedFullViewingKeyBuilder::build() const { + UnifiedFullViewingKeyPtr* ptr = unified_full_viewing_key_from_components( + t_bytes.has_value() ? t_bytes.value().data() : nullptr, + sapling_bytes.has_value() ? sapling_bytes.value().data() : nullptr); + + if (ptr == nullptr) { + return std::nullopt; + } else { + return UnifiedFullViewingKey(ptr); + } +} + +libzcash::UnifiedFullViewingKey libzcash::UnifiedFullViewingKey::FromZcashdUFVK(const ZcashdUnifiedFullViewingKey& key) { + UnifiedFullViewingKeyBuilder builder; + if (key.GetTransparentKey().has_value()) { + builder.AddTransparentKey(key.GetTransparentKey().value()); + } + if (key.GetSaplingKey().has_value()) { + builder.AddSaplingKey(key.GetSaplingKey().value()); + } + + auto result = builder.build(); + if (!result.has_value()) { + throw std::invalid_argument("Cannot convert from invalid viewing key."); + } + return result.value(); +} diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 09e6144a41c..8db8b03d25e 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -1,15 +1,18 @@ #ifndef ZC_ADDRESS_H_ #define ZC_ADDRESS_H_ -#include "uint256.h" +#include "key_constants.h" #include "pubkey.h" #include "script/script.h" +#include "uint256.h" #include "zcash/address/orchard.hpp" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" +#include "zcash/address/unified.h" #include "zcash/address/zip32.h" #include +#include namespace libzcash { @@ -127,14 +130,85 @@ class UnifiedAddress { } }; +class UnifiedFullViewingKeyBuilder; + +/** + * Wrapper for a zcash_address::unified::Ufvk. + */ +class UnifiedFullViewingKey { +private: + std::unique_ptr inner; + + UnifiedFullViewingKey() : + inner(nullptr, unified_full_viewing_key_free) {} + + UnifiedFullViewingKey(UnifiedFullViewingKeyPtr* ptr) : + inner(ptr, unified_full_viewing_key_free) {} + + friend class UnifiedFullViewingKeyBuilder; +public: + static std::optional Decode( + const std::string& str, + const KeyConstants& keyConstants); + + static UnifiedFullViewingKey FromZcashdUFVK(const ZcashdUnifiedFullViewingKey&); + + std::string Encode(const KeyConstants& keyConstants) const; + + std::optional GetSaplingKey() const; + + std::optional GetTransparentKey() const; + + UnifiedFullViewingKey(const UnifiedFullViewingKey& key) : + inner(unified_full_viewing_key_clone(key.inner.get()), unified_full_viewing_key_free) {} + + UnifiedFullViewingKey& operator=(UnifiedFullViewingKey&& key) + { + if (this != &key) { + inner = std::move(key.inner); + } + return *this; + } + + UnifiedFullViewingKey& operator=(const UnifiedFullViewingKey& key) + { + if (this != &key) { + inner.reset(unified_full_viewing_key_clone(key.inner.get())); + } + return *this; + } +}; + +class UnifiedFullViewingKeyBuilder { +private: + std::optional> t_bytes; + std::optional> sapling_bytes; +public: + UnifiedFullViewingKeyBuilder(): t_bytes(std::nullopt), sapling_bytes(std::nullopt) {} + + bool AddTransparentKey(const CChainablePubKey&); + bool AddSaplingKey(const SaplingDiversifiableFullViewingKey&); + + std::optional build() const; +}; + /** Addresses that can appear in a string encoding. */ typedef std::variant< InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress, UnifiedAddress> PaymentAddress; -typedef std::variant ViewingKey; -typedef std::variant SpendingKey; +/** Viewing keys that can have a string encoding. */ +typedef std::variant< + InvalidEncoding, + SproutViewingKey, + SaplingExtendedFullViewingKey, + UnifiedFullViewingKey> ViewingKey; +/** Spending keys that can have a string encoding. */ +typedef std::variant< + InvalidEncoding, + SproutSpendingKey, + SaplingExtendedSpendingKey> SpendingKey; class AddressInfoFromSpendingKey { public: @@ -147,10 +221,11 @@ class AddressInfoFromViewingKey { public: std::pair operator()(const SproutViewingKey&) const; std::pair operator()(const struct SaplingExtendedFullViewingKey&) const; + std::pair operator()(const UnifiedFullViewingKey&) const; std::pair operator()(const InvalidEncoding&) const; }; -} +} //namespace libzcash /** Check whether a PaymentAddress is not an InvalidEncoding. */ bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr); diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index d72a8686ece..56d2c1fc879 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . +#include "zcash/Address.hpp" #include "unified.h" #include "bip44.h" @@ -29,7 +30,11 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { ZcashdUnifiedFullViewingKey ufvk; if (transparentKey.has_value()) { - ufvk.transparentKey = transparentKey.value().Neuter(); + auto extPubKey = transparentKey.value().Neuter(); + + // TODO: how to ensure that the pubkey is in its compressed representation? + // Is that already guaranteed? + ufvk.transparentKey = CChainablePubKey::FromParts(extPubKey.chaincode, extPubKey.pubkey).value(); } if (saplingKey.has_value()) { @@ -39,6 +44,23 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { return ufvk; } +ZcashdUnifiedFullViewingKey ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey( + const UnifiedFullViewingKey& ufvk) { + ZcashdUnifiedFullViewingKey result; + + auto transparentKey = ufvk.GetTransparentKey(); + if (transparentKey.has_value()) { + result.transparentKey = transparentKey.value(); + } + + auto saplingKey = ufvk.GetSaplingKey(); + if (saplingKey.has_value()) { + result.saplingKey = saplingKey.value(); + } + + return result; +} + std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { UnifiedAddress ua; @@ -52,17 +74,20 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_i } if (transparentKey.has_value()) { + const auto& tkey = transparentKey.value(); auto childIndex = j.ToTransparentChildIndex(); if (!childIndex.has_value()) return std::nullopt; - CExtPubKey externalKey; - if (!transparentKey.value().Derive(externalKey, 0)) { + CPubKey externalKey; + ChainCode externalChainCode; + if (!tkey.GetPubKey().Derive(externalKey, externalChainCode, 0, tkey.GetChainCode())) { return std::nullopt; } - CExtPubKey childKey; - if (externalKey.Derive(childKey, childIndex.value())) { - ua.AddReceiver(childKey.pubkey.GetID()); + CPubKey childKey; + ChainCode childChainCode; + if (externalKey.Derive(childKey, childChainCode, childIndex.value(), externalChainCode)) { + ua.AddReceiver(childKey.GetID()); } else { return std::nullopt; } @@ -71,3 +96,13 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_i return ua; } +std::pair ZcashdUnifiedFullViewingKey::FindAddress(diversifier_index_t j) const { + auto addr = Address(j); + while (!addr.has_value()) { + if (!j.increment()) + throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; + addr = Address(j); + } + return std::make_pair(addr.value(), j); +} + diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 68de2792002..f65b4b13d45 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -7,23 +7,28 @@ #include "zip32.h" #include "bip44.h" -#include "zcash/Address.hpp" namespace libzcash { class ZcashdUnifiedSpendingKey; class ZcashdUnifiedFullViewingKey; +// prototypes for the classes handling ZIP-316 encoding (in Address.hpp) +class UnifiedAddress; +class UnifiedFullViewingKey; + class ZcashdUnifiedFullViewingKey { private: - std::optional transparentKey; + std::optional transparentKey; std::optional saplingKey; ZcashdUnifiedFullViewingKey() {} friend class ZcashdUnifiedSpendingKey; public: - const std::optional& GetTransparentKey() const { + static ZcashdUnifiedFullViewingKey FromUnifiedFullViewingKey(const UnifiedFullViewingKey& ufvk); + + const std::optional& GetTransparentKey() const { return transparentKey; } @@ -33,15 +38,7 @@ class ZcashdUnifiedFullViewingKey { std::optional Address(diversifier_index_t j) const; - std::pair FindAddress(diversifier_index_t j) const { - auto addr = Address(j); - while (!addr.has_value()) { - if (!j.increment()) - throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; - addr = Address(j); - } - return std::make_pair(addr.value(), j); - } + std::pair FindAddress(diversifier_index_t j) const; }; class ZcashdUnifiedSpendingKey { diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 608918e685b..2a18d1af418 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -141,6 +141,26 @@ class SaplingDiversifiableFullViewingKey { } libzcash::SaplingPaymentAddress DefaultAddress() const; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(fvk); + READWRITE(dk); + } + + template + static SaplingDiversifiableFullViewingKey Read(Stream& stream) { + SaplingDiversifiableFullViewingKey key; + stream >> key; + return key; + } + + friend inline bool operator==(const SaplingDiversifiableFullViewingKey& a, const SaplingDiversifiableFullViewingKey& b) { + return (a.fvk == b.fvk && a.dk == b.dk); + } + }; class SaplingExtendedFullViewingKey: public SaplingDiversifiableFullViewingKey { From 22cd218e831c508108f7155cd3d716dfdc592481 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 6 Dec 2021 11:56:32 -0700 Subject: [PATCH 160/514] Apply suggestions from code review Co-authored-by: Daira Hopwood --- src/rust/include/librustzcash.h | 6 ++++-- src/rust/include/rust/unified_keys.h | 24 ++++++++++++++++-------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index 048c70ef5e4..7b538bcb34b 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -311,7 +311,8 @@ extern "C" { * if no valid address can be derived for the specified diversifier index. * * Arguments: - * - xfvk: [c_uchar; 169] the serialized form of a Sapling extended full viewing key + * - xfvk: [c_uchar; 96] the serialized form of a Sapling full viewing key + * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key * - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching, * encoded in little-endian order * - addr_ret: [c_uchar; 43] array to which the returned address will be written, @@ -332,7 +333,8 @@ extern "C" { * in which case this function will return `false`. * * Arguments: - * - xfvk: [c_uchar; 169] the serialized form of a Sapling extended full viewing key + * - xfvk: [c_uchar; 96] the serialized form of a Sapling full viewing key + * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key * - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching, * encoded in little-endian order * - j_ret: [c_uchar; 11] array that will store the diversifier index at which the diff --git a/src/rust/include/rust/unified_keys.h b/src/rust/include/rust/unified_keys.h index 6d993bd4767..56360b55d0c 100644 --- a/src/rust/include/rust/unified_keys.h +++ b/src/rust/include/rust/unified_keys.h @@ -47,6 +47,9 @@ UnifiedFullViewingKeyPtr* unified_full_viewing_key_parse( /** * Serializes a unified full viewing key and returns its string representation. + * + * The returned string's memory must be freed by the caller using + * `zcash_address_string_free`. */ char* unified_full_viewing_key_serialize( const char* network, @@ -57,9 +60,9 @@ char* unified_full_viewing_key_serialize( * * `tkeyout` must be of length 65. * - * Returns `true` if the UFVK contained a transparent component, - * `false` otherwise. The bytes of the transparent key will be - * copied to tkeyout + * Returns `true` if the UFVK contained a transparent component, `false` + * otherwise. If this returns `true`, the transparent key will be copied to + * `tkeyout` as the byte representation of the `(ChainCode, CPubKey)` pair */ bool unified_full_viewing_key_read_transparent( const UnifiedFullViewingKeyPtr* full_viewing_key, @@ -71,24 +74,29 @@ bool unified_full_viewing_key_read_transparent( * `skeyout` must be of length 128. * * Returns `true` if the UFVK contained a Sapling component, - * false otherwise. + * `false` otherwise. The bytes of the `(ak, nk, ovk, dk)` fields + * of the viewing key, in the encoding given by `EncodeExtFVKParts` + * defined in ZIP 32, will be copied to `skeyout` if `true` is returned. */ bool unified_full_viewing_key_read_sapling( const UnifiedFullViewingKeyPtr* full_viewing_key, unsigned char* skeyout); /** - * Sets the Sapling component of a unified viewing key. + * Constructs a unified full viewing key from the binary encodings + * of its constituent parts * * `t_key` must be of length 65 and must be the concatenated * bytes of the serialized `(ChainCode, CPubKey)` pair. * * `sapling_key` must be of length 128 and must be the concatenated * bytes of the serialized `(SaplingFullViewingKey, DiversifierKey)` - * pair. + * pair in the encoding given by `EncodeExtFVKParts` defined in + * ZIP 32. * - * Returns the newly allocated UFVK if the operation succeeds, - * or the null pointer otherwise. + * Returns a pointer to newly allocated UFVK if the operation succeeds, + * or the null pointer otherwise. The pointer returned by this function + * must be freed by the caller with `unified_full_viewing_key_free`. */ UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components( const unsigned char* t_key, From 8ae8ddd7d10dcd8e179e7e6124ac35f488efaaed Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 8 Dec 2021 12:05:33 -0700 Subject: [PATCH 161/514] Add tests for ufvk roundtrip serialization. --- src/Makefile.test.include | 1 + src/gtest/test_keys.cpp | 80 +++++++++++++++++++- src/rust/src/unified_keys_ffi.rs | 9 ++- src/test/data/unified_full_viewing_keys.json | 14 ++++ 4 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 src/test/data/unified_full_viewing_keys.json diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 8f86ec3bae1..a21b7e403ab 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -44,6 +44,7 @@ JSON_TEST_FILES = \ test/data/merkle_commitments_sapling.json \ test/data/sapling_key_components.json \ test/data/unified_addrs.json \ + test/data/unified_full_viewing_keys.json \ test/data/zip0244.json RAW_TEST_FILES = test/data/alertTests.raw diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index d86c959f648..7927973a82c 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -10,6 +10,7 @@ #include "json_test_vectors.h" #include "test/data/unified_addrs.json.h" +#include "test/data/unified_full_viewing_keys.json.h" TEST(Keys, EncodeAndDecodeSapling) { @@ -102,7 +103,7 @@ namespace libzcash { } } -TEST(Keys, EncodeAndDecodeUnified) +TEST(Keys, EncodeAndDecodeUnifiedAddresses) { SelectParams(CBaseChainParams::MAIN); KeyIO keyIO(Params()); @@ -131,8 +132,7 @@ TEST(Keys, EncodeAndDecodeUnified) if (!test[2].isNull()) { auto data = ParseHex(test[2].get_str()); CDataStream ss( - reinterpret_cast(data.data()), - reinterpret_cast(data.data() + data.size()), + data, SER_NETWORK, PROTOCOL_VERSION); libzcash::SaplingPaymentAddress r; @@ -164,3 +164,77 @@ TEST(Keys, EncodeAndDecodeUnified) } } } + +TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys) +{ + SelectParams(CBaseChainParams::MAIN); + KeyIO keyIO(Params()); + + UniValue ua_tests = read_json(MAKE_STRING(json_tests::unified_full_viewing_keys)); + + for (size_t idx = 0; idx < ua_tests.size(); idx++) { + UniValue test = ua_tests[idx]; + std::string strTest = test.write(); + if (test.size() < 1) // Allow for extra stuff (useful for comments) + { + FAIL() << "Bad test: " << strTest; + continue; + } + if (test.size() == 1) continue; // comment + + //try { + libzcash::UnifiedFullViewingKeyBuilder builder; + // ["p2pkh_key_bytes, sapling_key_bytes, orchard_key_bytes, unknown_key_bytes, unified_key"] + // These were added to the UA in preference order by the Python test vectors. + if (!test[0].isNull()) { + auto data = ParseHex(test[0].get_str()); + ASSERT_EQ(data.size(), 65); + CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); + auto decoded = CChainablePubKey::Read(ss); + ASSERT_TRUE(builder.AddTransparentKey(decoded)); + } + if (!test[1].isNull()) { + auto data = ParseHex(test[1].get_str()); + ASSERT_EQ(data.size(), 128); + CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); + auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss); + ASSERT_TRUE(builder.AddSaplingKey(key)); + } + + // Orchard keys and unknown items are not yet supported; instead, + // we just test that we're able to parse the unified key string + // and that the constituent items match the elements; if no Sapling + // key is present then UFVK construction would fail because it might + // presume the UFVK to be transparent-only. + if (test[1].isNull()) + continue; + + // if (!test[2].isNull()) { + // auto data = ParseHex(test[3].get_str()); + // libzcash::UnknownReceiver r(0x03, data); + // ASSERT_TRUE(builder.AddUnknownItem(r)); + // } + // if (!test[4].isNull()) { + // auto data = ParseHex(test[4].get_str()); + // libzcash::UnknownReceiver r(test[3].get_int(), data); + // ASSERT_TRUE(builder.AddUnknownItem(r)); + // } + + auto built = builder.build(); + ASSERT_TRUE(built.has_value()); + + auto keystrBytes = ParseHex(test[5].get_str()); + std::string keystr(keystrBytes.begin(), keystrBytes.end()); + + auto decoded = libzcash::UnifiedFullViewingKey::Decode(keystr, Params()); + ASSERT_TRUE(decoded.has_value()); + + EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey()); + EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey()); + //} catch (const std::exception& ex) { + // FAIL() << "Bad test, couldn't deserialize data: " << strTest << ": " << ex.what(); + //} catch (...) { + // FAIL() << "Bad test, couldn't deserialize data: " << strTest; + //} + } +} diff --git a/src/rust/src/unified_keys_ffi.rs b/src/rust/src/unified_keys_ffi.rs index b7ecfc38373..8037fc2ea75 100644 --- a/src/rust/src/unified_keys_ffi.rs +++ b/src/rust/src/unified_keys_ffi.rs @@ -33,8 +33,7 @@ pub extern "C" fn unified_full_viewing_key_parse( }; match unsafe { CStr::from_ptr(encoded) }.to_str() { - Ok(encoded) => { - match Ufvk::decode(encoded) { + Ok(encoded) => match Ufvk::decode(encoded) { Ok((parsed_network, fvk)) => { if parsed_network == network { Box::into_raw(Box::new(fvk)) @@ -50,7 +49,7 @@ pub extern "C" fn unified_full_viewing_key_parse( error!("Failure decoding unified full viewing key: {}", e); std::ptr::null_mut() } - }}, + }, Err(e) => { error!("Failure reading bytes of unified full viewing key: {}", e); std::ptr::null_mut() @@ -125,6 +124,10 @@ pub extern "C" fn unified_full_viewing_key_from_components( match Ufvk::try_from_items(items) { Ok(ufvk) => Box::into_raw(Box::new(ufvk)), Err(e) => { + println!( + "An error occurred constructing the unified full viewing key: {:?}", + e + ); error!( "An error occurred constructing the unified full viewing key: {:?}", e diff --git a/src/test/data/unified_full_viewing_keys.json b/src/test/data/unified_full_viewing_keys.json new file mode 100644 index 00000000000..99c6990a0c4 --- /dev/null +++ b/src/test/data/unified_full_viewing_keys.json @@ -0,0 +1,14 @@ +[ + ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_full_viewing_keys.py"], + ["t_key_bytes, sapling_fvk_bytes, orchard_fvk_bytes, unknown_fvk_typecode, unknown_fvk_bytes, unified_fvk"], + [null, "cfb835e7c05c80c2a15a58702bc529a44e1a815ef79124f23709214cf0167ac4e6340b493dca8e4bee114259dc35edc4c296ffd53869885531d1bdb27008bbcd6fec092ad5c4d1f68819f41ae447db96df4a5f110018f47060916ec54884f1cc27a0d4c0bca90984cdf39fb4cc61ceee78ddaa2a45af871f49f04e98b02fb16b", null, 65535, null, "757669657731747878783339707833736a676478796c6d6636666876706e6878667966717376756e3863737330723678717830726b3974767a3076727a74756a74683474716e7534367877657035367279396a643537687972726c36757467657a356a717232716466737a79787265686b64686774757964376d78756e6e6133327732356d396b7771387163687377673476686632796d736b376c6e7637786a3864356b347a7272343370756e746b6d666b396e346a636b66763237677063376e747765726c6c756439307a346c356c71786a68366333356b376135786c6d663563726467677537366c307572657475787333386839"], + [null, "04da0d94cb0a6397067a81a88ef422e56678e0ba232eb4dd6b05b98bc5e3461cd4a52b366a7df1f3a871854bfe1492711dc5130b35441748caa2742959279ce31e2b3604995d4ccedc4618ad16cdd2c0d42a6d36fb3a0610054cedef30beca20d187f32ce02f8ca357b575e705cda7ef8e1c68b9110381bd3958e0659a084205", "9bf5e479a60cfa3893a2a7693488b3fa016402851263e63c48540b3daff13814cd71a2ab1f78c561c768a3641dcf7a7d6b473393a629841c81f93afa87689035a119c9f7dac6fb9e7cf8fd6aa40f5f7f9e75b161d5f7269d28a4211ab84a8408", 65532, null, "75766965773135797678377477766a376a726a67766d6e6c797474683632396e7861746c7837686e79727a707a3468337a74776a79786c7733793770657a737671397276643470726779326474726a6c736d6c326c76366c6a7575306a7274766b6d6d366c6a32793768706c70686b383978656b66636565687a637a6777773237756538763272376e3077727339636b78376134756c6365766b777830363077667139613379357932746d337132777163756c796d3232396668387474386a6d376c683863367639707a67613274396e6c78673630377478353673303775757177337673336d78663676716b68353071636a33677678347664367a367379356e3039387565636c396a707968737178333934677337323539786b33796c376764326e333370767371707979377064393734347271786e73343073327a323963796b3435766a763765756b713976777a3879306163383539707a7166386b6d6c6b767678723930716b326e30376c6e7264393061363467376b6472336366356a637665726c7173667134366e6e71777137386b68"], + ["546c1fe01f7e9c8e36d6a5e29d4e30a73594bf5098421c69378af1e40f64e12503f4e4887b87079d7b7647f01a93a73c7a7d517840575c3246710adbdd752ed2d6", "fc02759ff2853b5e9e15842bef22c76023d43db7c265f120ed175713195f1240f63aee02ec23f146e9e25d25605fbae5472cdedc3b4c31c66b76fe9e6e47eeca79d5fb6a84d152820daaf89e99551d068d99cdf9be065007bc25f245ea62631d17b3bc77f62f35bd4205e6f682b1f9e824ecea53e271b80ff6bc79ef68a20ab5", "c36559ed79d1fecbe1de93d57698e915802f0ccd097adb4e448caef8c48d6c1e439482a1e5636131cba024f891b3bb1dfe333773415eeecf7a42e37dbd934e3a97d92dc90afa0ec3e96eeed9238dfcb6da23799cff14d477368d9ddc330afe39", 65532, null, "757669657731796e727935646c356c676b6733656a61326b386d777779646661357a363465726674657936383365776561326e3068333436636134326e6d7561347170636b63787565707073366e326d656a6c6b64363930333772766d7a61343677306c6d3035676a7070686566756a7a6d6734663876366d7130746b676766376667727138307a6d6d6b756467737739397465687363396d6a65773966367238366b6463646a7a666e66773065326b6d713567646b6866617175797167676b64616d68727a726e6a756663777739386e6577647a7479726d353775757037673338383730703932763776787a727377336c6366797a37616e326473616637757733663672716b32747a6a396c66356371376378346a77767667776a3078787064723433786a6d63646b366b6c6668773379663672617168383676676c61706a636d6b6879776a67366b687161777561667077716a6b776a75756c6a646e756b68646c6b6e726c3878307674727075727335636b7a357734683030397674657165646478786e63666e736c6e6830687074723665766566366e7a393268723838766c616a79366b6378366d306a67613267706b7773736b347277773373766d6e61683437656b64687430713964647467666e7a6736736d6b656c66336a723834707473706c376e37646575397a306d65616b756b336b3867707768703532"], + [null, null, "13a3e9649004641d3fc4da1cdefc4c5c6c457b0aee6e668d1d57a58bc48b0e1501a9e2572d16a3cb72c7b539582a46d8f67823da02235d41d49c12788bceeb334500fef8adfc1bcbaa0eede5b48ea7d3e9ec2a774986aac43050c50aa24f1d0f", 65533, null, "757669657731686d3672366e6434747463707137786e326a307367796a336d63726e6633336b796b3574797271666c70663774327266336a6c6530713839616a6870327764746164397533646e76356b6c746171767576647a6c7770397679726378666535676b763078646a6578326e68707839336e65367876307770386130676c6e36746a6b76796b7174686166726e686e6b6633676138656a766676717636386735633974386a75656e7a6833396766776a64637238786d7476677937656d7465"], + [null, null, "33ca395730a107b148032a999349f6dc9983daab9d01ffb1180b56ba7038221d1bf174c534ae1adb7a61a30ad0e3b36c52cf90957491ed84f90b5bd42c76833ef0cf897fe579461bc8db0b37e2da35766f2ee00712416c87f0a679fc55d4791f", 65531, null, "7576696577316478386a3774756b7473686678737930336167706a68633878326d716137343338353861343274736c327335786b78647a706534617664677363647978366163676c35736c656a3978616e33307479616735707333706a6a7375677075307276343237656b6c336c78616c6738637378726568667670717635306672617772687868716b38766a387975353270336875333872786c3772726e6c766a663374776b7467336b67323667656765636d30786873726d673671667036327563"], + ["9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f540215761b6d5155ba66df44f224e914e4a8bb6f2fee6820a865ac9fbfb327c11507", null, "a71cccc7d3f4fd389ba45bc4dc4e9b3d91d1fdc70eb15ab92aced578409e272f15181f6ae4b8412caf1044dab7749111cdba8c43a4ffad1de39bb22d2dd150077e9343b886179115512836623a664e3125d2faf44a1308e3dbd525da73e46523", 65534, null, "75766965773166326d6d6a783867786e376a716a796a397872776c6b32656d61617770393561326e716a3366373574766b37737a766d6430686e6d7a6d79637732336773343870646a37676d326c36637573763464733935666d656d723972723966787838333370776a63336c7a353330376c787a7632366630757132756b70776167717730786c7a6a746838386476776536616a3339686d6e7661307561716b6a656c73373268386563757539753273636c30756c373239687a397437793537656b617978777375326638366164306c3536727237653374353378736333787333347075766167667564787734766a377039336c346377303232747238706b746567716d386e32737835687061747a3776357130636d3865767032356d77776b6767657568677366737a357078"], + ["1ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671021c3e1aa400191c4609ba93df32612f46dfa68aebcfb80778b7fb59bb563e0a7a", null, "3d301cbf5f38ad466992b6466c3377d9f314e6e0e456cd8879f9f9a2db838232c3c801b93141a1ddc29f1079a9b3b46126ed23aeabc4007912798cadf9cfc00e800540cf11186e253036ae5a53f875c50a4a7940021523a92d8b359c140acb31", 65534, null, "7576696577317237386b6434393237373270396371646165336a396a6a647572637a76357a75633632706b366b3371756674776339396735666c7a6366356470737a676b6d6574307a6533726d38746c33356d63647a686564616a74386e7a397877377676656c673275397137756a373876736372666d383466703937633770753867326e78376a3476346c3966633530637671756b396d37673536306d386e3468707972726e6c6a39726b377537726d657578303265723876727a727167326136767535766b786d6b66753368326834666b30777a336c337735657173676c393374777037616d6d3936726b6b30356d6e326b7970677a3863636e6c3778786e3973616b35787a6c65617072736c3630653573323870346e796875356b6a32776b35726c6676766e3434396d35"], + ["26c1039586a7afcf4a0d9c731e985d99589c8bb838e8aaf745533ed9e8ae3a1c02ca6a53e13c134c1fb70013b1c3c7f59788b28df32fac5c220f565bd926dca633", null, "ffdffc7112cf550135fa5476272b24289fcabc5401b16db739d1df8f4493f1143c3cc90b837152144de475182209b0c169559f4a91fb788403b3b7642cc99c0d519abc658bfae98e12c9878d9e16c22937cc8182ebac54f15e307a2e639da239", 65535, null, "75766965773139706d666b617678647639777a6867766670653070753678727366373967616e613832396c3277667537713833336e63667a71657732656c637579643674757139683770656472397871763068713461637768396e647332776436677474357633776530777964336374736463336866676b7968637779776d753733323076666b30636b6e70326e65706a64686d756b3375746365613267796d78306630327a6d6c61386a706137656c6d7768796b7434637371333739717a356133386579346b79353077373064353330797a6a6d686c7473637a32673832766a7479616b65737366323776397177736b79713734786c6a63333530663767793036306a686b6c673871727671746535656c6d637374336e6b30766b6d6c6e6d6d7437367337657363656a707263"], + [null, "deb352b3dfeb09d42a96e77ce38e8a6bd00a1052e565215e1325e0712703368c8dbc0cd5eb82701bbd5903e60d488ef8f8944940ca7d87efbbd6c1eacf9ede8019a3b8fde82c151e11321e54a49aaf6de8b9d6515816a5104fb6ea54ed33873dc4a6ba5a80c1ee0f78378db0052388372aecdde7f53fb35320bec33763ddaa51", "37e5d0367502f509f94f077449cbd07473b9d2b630a5b4f8cefa689543d27028b01e4b736cc5620dc82093b4077b12fab582f225f3a5e58a76921cdad17dd70d458aee1ef78af37e9aadfffbd95db1046ad5b9f29ea4297050cadf9825721632", 65533, "857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a", "757669657731756d6b78787175716e7377307a73717a6175327235376d3863706a75363576787865636d6437686c726d677367326c63653932396c6a3563776c636e65707973636577306371353577646d657072767476337a707665356465727976716d726c7035796770346c733264716d796764636738636d6132763878656e6e7a637575326132736c6c7938646c646479757a717573677064736e7a61717933747332736c70746b6d386a737567766a333938766e336b7472643975757167336b3376737a33687a7a6361736c3765646e676868366c78707477746e6468377961616665796c6372353870686b66636635307a6b64666d6b36736133366e36646a687536636e68347278663530766b79396a767663663075707a346479713876367465687732673661616b737730747873336530646b3973763336613371797874356d68636373366768387265336736757633327a3370737879767133617930776e7332616b756666376a333833677130757036706d6e3467366c763937666a646d73397a336735777261383338786668657a776361363574796174333078637933326a76357279356e37613363306d3036713763616e71736e72366133673330716b387265616b717a716d64366b656338667663336d7a6a6e6d39653564376564686a6c36357a67636c743272756e6a34337a6d6d73327665636b7176366e3338716477303863736d7378746b643068796666366a6e3266377732376434726a7168326c7a637638386d30383763393839"], + ["d715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f803ff3f19851764670df1660c9cb56918abc2a0691eb8b89e705eb8e65b0f4e0dcb", "068d407022db8e5dd0730882bd54851ca0797a00dff60e358d28a04df20793838ba1618a6871da256cdbf7d8ad2ce81d9ce01df64765bc7c14ac74e7ed60129090fcb061b3e672a742aa0f5db728265947355b6375f3d2226b25129dced2e0991d02f739d2d822df5d41edc122b2330916ba36ca09e80cf07f99be4a45fce8e7", "d0e5967a78a4ba305dc8c47ad6ea668d2e88e11575d2ec1f082c740b5fdc050ddb57fa080007c5d868dd8670904b76a82959e8482795ab3ba4cbdc7bb37b1822d2f3ccda83c005fe5c571dbec5074f159f345ad2f60edf8364e4ddf983f91235", 65533, "ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e95", "757669657731666b6e393734747468686363306b71796d726a7467637370646c7170613768346e356e7439386b76773761326a676a686174706a713478656a76673879646e747074706e327a6c64707471386371716e646a6c67336c6b786b66776a7635726e39767638636a3666783567307a387a66746e6470726b7073387937656d34737a79327679386a70336832653678386b64717170656e356865387272326466797072767763713563756e7639396c67306a6b397233796c777064617a723779757a357938756a773071366e333939756d643236366a37396a7371776d6c7967767533776e666b797a3335647964687861706c63327578713572613237396e77396e73776379326837676873673930376d70733279686578666876323576776c343461383833376a63777336766365776a7434347a357333336e71756c396d35737a6d6e6179346476763364777568377a796871687732376d753777376a736b30756e6b73787363617a3372616d7973386579667a7a6475636e6765657764357738707433687478666a66786b657a65757130326d6c6333646b636163716b736c716761356b33656c3064746775796771686a3976793572646b70746d7274633432776c3336667676346b6d6b66793735677377776b72796e3835656e6e32396d75396a6c35326e7730717a7967747a366879347a653535756e707071647133346371767237766374777132657a676c716432666b79676161657072746c66376b65707277763871307571383865657771746a756b6c6a3973713933323675717a76786b727837773772716e7a68633361777138367938796a3371386868633472356a6e3864637a7766613934763367307572716173706334727132636878767a3772776e6872683332386168326c6168707776656134747771"] +] From 6cccc4ad3c3d8952c00e9a25d4aa0e45c00a25f6 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 10 Dec 2021 12:29:33 -0700 Subject: [PATCH 162/514] Apply suggestions from code review Co-authored-by: Daira Hopwood --- src/gtest/test_keys.cpp | 89 +++++++++++----------------- src/rust/include/rust/unified_keys.h | 6 +- src/rust/src/unified_keys_ffi.rs | 5 +- 3 files changed, 43 insertions(+), 57 deletions(-) diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index 7927973a82c..d2144e18c79 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -182,59 +182,42 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys) } if (test.size() == 1) continue; // comment - //try { - libzcash::UnifiedFullViewingKeyBuilder builder; - // ["p2pkh_key_bytes, sapling_key_bytes, orchard_key_bytes, unknown_key_bytes, unified_key"] - // These were added to the UA in preference order by the Python test vectors. - if (!test[0].isNull()) { - auto data = ParseHex(test[0].get_str()); - ASSERT_EQ(data.size(), 65); - CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); - auto decoded = CChainablePubKey::Read(ss); - ASSERT_TRUE(builder.AddTransparentKey(decoded)); - } - if (!test[1].isNull()) { - auto data = ParseHex(test[1].get_str()); - ASSERT_EQ(data.size(), 128); - CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); - auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss); - ASSERT_TRUE(builder.AddSaplingKey(key)); - } + libzcash::UnifiedFullViewingKeyBuilder builder; + // ["p2pkh_key_bytes, sapling_key_bytes, orchard_key_bytes, unknown_key_bytes, unified_key"] + // These were added to the UA in preference order by the Python test vectors. + if (!test[0].isNull()) { + auto data = ParseHex(test[0].get_str()); + ASSERT_EQ(data.size(), 65); + CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); + auto decoded = CChainablePubKey::Read(ss); + ASSERT_TRUE(builder.AddTransparentKey(decoded)); + } + if (!test[1].isNull()) { + auto data = ParseHex(test[1].get_str()); + ASSERT_EQ(data.size(), 128); + CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); + auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss); + ASSERT_TRUE(builder.AddSaplingKey(key)); + } + + // Orchard keys and unknown items are not yet supported; instead, + // we just test that we're able to parse the unified key string + // and that the constituent items match the elements; if no Sapling + // key is present then UFVK construction would fail because it might + // presume the UFVK to be transparent-only. + if (test[1].isNull()) + continue; + + auto built = builder.build(); + ASSERT_TRUE(built.has_value()); + + auto keystrBytes = ParseHex(test[5].get_str()); + std::string keystr(keystrBytes.begin(), keystrBytes.end()); + + auto decoded = libzcash::UnifiedFullViewingKey::Decode(keystr, Params()); + ASSERT_TRUE(decoded.has_value()); - // Orchard keys and unknown items are not yet supported; instead, - // we just test that we're able to parse the unified key string - // and that the constituent items match the elements; if no Sapling - // key is present then UFVK construction would fail because it might - // presume the UFVK to be transparent-only. - if (test[1].isNull()) - continue; - - // if (!test[2].isNull()) { - // auto data = ParseHex(test[3].get_str()); - // libzcash::UnknownReceiver r(0x03, data); - // ASSERT_TRUE(builder.AddUnknownItem(r)); - // } - // if (!test[4].isNull()) { - // auto data = ParseHex(test[4].get_str()); - // libzcash::UnknownReceiver r(test[3].get_int(), data); - // ASSERT_TRUE(builder.AddUnknownItem(r)); - // } - - auto built = builder.build(); - ASSERT_TRUE(built.has_value()); - - auto keystrBytes = ParseHex(test[5].get_str()); - std::string keystr(keystrBytes.begin(), keystrBytes.end()); - - auto decoded = libzcash::UnifiedFullViewingKey::Decode(keystr, Params()); - ASSERT_TRUE(decoded.has_value()); - - EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey()); - EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey()); - //} catch (const std::exception& ex) { - // FAIL() << "Bad test, couldn't deserialize data: " << strTest << ": " << ex.what(); - //} catch (...) { - // FAIL() << "Bad test, couldn't deserialize data: " << strTest; - //} + EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey()); + EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey()); } } diff --git a/src/rust/include/rust/unified_keys.h b/src/rust/include/rust/unified_keys.h index 56360b55d0c..f9c837bbf00 100644 --- a/src/rust/include/rust/unified_keys.h +++ b/src/rust/include/rust/unified_keys.h @@ -62,7 +62,8 @@ char* unified_full_viewing_key_serialize( * * Returns `true` if the UFVK contained a transparent component, `false` * otherwise. If this returns `true`, the transparent key will be copied to - * `tkeyout` as the byte representation of the `(ChainCode, CPubKey)` pair + * `tkeyout` as the byte representation of the `(ChainCode, CPubKey)` pair. + * If it returns `false` then `tkeyout` will be unchanged. */ bool unified_full_viewing_key_read_transparent( const UnifiedFullViewingKeyPtr* full_viewing_key, @@ -77,6 +78,7 @@ bool unified_full_viewing_key_read_transparent( * `false` otherwise. The bytes of the `(ak, nk, ovk, dk)` fields * of the viewing key, in the encoding given by `EncodeExtFVKParts` * defined in ZIP 32, will be copied to `skeyout` if `true` is returned. + * If `false` is returned then `skeyout` will be unchanged. */ bool unified_full_viewing_key_read_sapling( const UnifiedFullViewingKeyPtr* full_viewing_key, @@ -94,7 +96,7 @@ bool unified_full_viewing_key_read_sapling( * pair in the encoding given by `EncodeExtFVKParts` defined in * ZIP 32. * - * Returns a pointer to newly allocated UFVK if the operation succeeds, + * Returns a pointer to newly allocated UFVK if the operation succeeds, * or the null pointer otherwise. The pointer returned by this function * must be freed by the caller with `unified_full_viewing_key_free`. */ diff --git a/src/rust/src/unified_keys_ffi.rs b/src/rust/src/unified_keys_ffi.rs index 8037fc2ea75..9857e6b3c65 100644 --- a/src/rust/src/unified_keys_ffi.rs +++ b/src/rust/src/unified_keys_ffi.rs @@ -39,8 +39,9 @@ pub extern "C" fn unified_full_viewing_key_parse( Box::into_raw(Box::new(fvk)) } else { error!( - "Key was encoded for a different network than what was requested: {:?}", - parsed_network + "Key was encoded for a different network ({:?}) than what was requested ({:?})", + parsed_network, + network, ); std::ptr::null_mut() } From d1890ebd241ffadd808bacf521a4e55ccc2bb348 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 13 Dec 2021 21:00:05 -0700 Subject: [PATCH 163/514] Apply suggestions from code review Co-authored-by: str4d --- src/pubkey.h | 6 ++--- src/rust/include/librustzcash.h | 40 ++++++++++++++++++--------------- src/zcash/Address.hpp | 10 +++++++++ src/zcash/address/unified.h | 12 ++++++++++ 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/pubkey.h b/src/pubkey.h index 04739eaea33..1d66e091a5c 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -227,13 +227,13 @@ class CChainablePubKey { inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(chaincode); if (ser_action.ForRead()) { - std::array pubkeyBytes; + std::array pubkeyBytes; READWRITE(pubkeyBytes); pubkey = CPubKey(pubkeyBytes.begin(), pubkeyBytes.end()); assert(pubkey.IsCompressed()); } else { - assert(pubkey.size() == 33); - std::array pubkeyBytes; + assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); + std::array pubkeyBytes; std::copy(pubkey.begin(), pubkey.end(), pubkeyBytes.begin()); READWRITE(pubkeyBytes); } diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index 7b538bcb34b..c55073cff98 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -307,16 +307,18 @@ extern "C" { ); /** - * Derive a PaymentAddress from an ExtendedFullViewingKey. Returns 'false' - * if no valid address can be derived for the specified diversifier index. + * Derive a PaymentAddress from a (SaplingFullViewingKey, DiversifierKey) + * pair. Returns 'false' if no valid address can be derived for the + * specified diversifier index. * * Arguments: - * - xfvk: [c_uchar; 96] the serialized form of a Sapling full viewing key + * - fvk: [c_uchar; 96] the serialized form of a Sapling full viewing key * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key - * - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching, - * encoded in little-endian order - * - addr_ret: [c_uchar; 43] array to which the returned address will be written, - * if the specified diversifier index `j` produces a valid address. + * - j: [c_uchar; 11] the 88-bit diversifier address at which to start + * searching, encoded in little-endian order + * - addr_ret: [c_uchar; 43] array to which the returned address will be + * written, if the specified diversifier index `j` produces a valid + * address. */ bool librustzcash_zip32_sapling_address( const unsigned char *fvk, @@ -326,20 +328,22 @@ extern "C" { ); /** - * Derive a PaymentAddress from an ExtendedFullViewingKey by searching the - * space of valid diversifiers starting at diversifier index `j`. This will - * always return a valid address along with the diversifier index that produced - * the address unless no addresses can be derived at any diversifier index >= `j`, - * in which case this function will return `false`. + * Derive a PaymentAddress from a (SaplingFullViewingKey, DiversifierKey) + * pair by searching the space of valid diversifiers starting at + * diversifier index `j`. This will always return a valid address along + * with the diversifier index that produced the address unless no addresses + * can be derived at any diversifier index >= `j`, in which case this + * function will return `false`. * * Arguments: - * - xfvk: [c_uchar; 96] the serialized form of a Sapling full viewing key + * - fvk: [c_uchar; 96] the serialized form of a Sapling full viewing key * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key - * - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching, - * encoded in little-endian order - * - j_ret: [c_uchar; 11] array that will store the diversifier index at which the - * returned address was found - * - addr_ret: [c_uchar; 43] array to which the returned address will be written + * - j: [c_uchar; 11] the 88-bit diversifier address at which to start + * searching, encoded in little-endian order + * - j_ret: [c_uchar; 11] array that will store the diversifier index at + * which the returned address was found + * - addr_ret: [c_uchar; 43] array to which the returned address will be + * written */ bool librustzcash_zip32_find_sapling_address( const unsigned char *fvk, diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 8db8b03d25e..b6a63c8db57 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -151,6 +151,14 @@ class UnifiedFullViewingKey { const std::string& str, const KeyConstants& keyConstants); + /** + * This method should only be used for serialization of unified full + * viewing keys that have been generated internally from unified spending + * keys by Zcashd. It is not suitable for use in any case where the + * ZcashdUnifiedFullViewingKey value may have been produced by a + * potentially-lossy conversion from a UnifiedFullViewingKey value that + * originated outside of zcashd. + */ static UnifiedFullViewingKey FromZcashdUFVK(const ZcashdUnifiedFullViewingKey&); std::string Encode(const KeyConstants& keyConstants) const; @@ -159,6 +167,8 @@ class UnifiedFullViewingKey { std::optional GetTransparentKey() const; + UnifiedFullViewingKey(UnifiedFullViewingKey&& key) : inner(std::move(key.inner)) {} + UnifiedFullViewingKey(const UnifiedFullViewingKey& key) : inner(unified_full_viewing_key_clone(key.inner.get()), unified_full_viewing_key_free) {} diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index f65b4b13d45..9742eb2fa02 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -17,6 +17,12 @@ class ZcashdUnifiedFullViewingKey; class UnifiedAddress; class UnifiedFullViewingKey; +/** + * An internal-only type for unified full viewing keys that represents only the + * set of receiver types that are supported by zcashd. This type does not + * support round-trip serialization to and from the UnifiedFullViewingKey type, + * which should be used in most cases. + */ class ZcashdUnifiedFullViewingKey { private: std::optional transparentKey; @@ -26,6 +32,9 @@ class ZcashdUnifiedFullViewingKey { friend class ZcashdUnifiedSpendingKey; public: + /** + * This constructor is lossy, and does not support round-trip transformations. + */ static ZcashdUnifiedFullViewingKey FromUnifiedFullViewingKey(const UnifiedFullViewingKey& ufvk); const std::optional& GetTransparentKey() const { @@ -41,6 +50,9 @@ class ZcashdUnifiedFullViewingKey { std::pair FindAddress(diversifier_index_t j) const; }; +/** + * The type of unified spending keys supported by zcashd. + */ class ZcashdUnifiedSpendingKey { private: libzcash::AccountId accountId; From 1e18410b5549f58555a992275127ba432e11c97d Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 14 Dec 2021 13:02:27 -0700 Subject: [PATCH 164/514] Apply suggestions from code review Co-authored-by: str4d Co-authored-by: Daira Hopwood --- src/gtest/test_keys.cpp | 2 +- src/rust/include/librustzcash.h | 6 +++--- src/rust/include/rust/unified_keys.h | 15 +++++++-------- src/rust/src/unified_keys_ffi.rs | 22 +++++++--------------- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index d2144e18c79..a9736930a99 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -183,7 +183,7 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys) if (test.size() == 1) continue; // comment libzcash::UnifiedFullViewingKeyBuilder builder; - // ["p2pkh_key_bytes, sapling_key_bytes, orchard_key_bytes, unknown_key_bytes, unified_key"] + // ["t_key_bytes, sapling_fvk_bytes, orchard_fvk_bytes, unknown_fvk_typecode, unknown_fvk_bytes, unified_fvk"] // These were added to the UA in preference order by the Python test vectors. if (!test[0].isNull()) { auto data = ParseHex(test[0].get_str()); diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index c55073cff98..ae5dc4b84f9 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -314,8 +314,8 @@ extern "C" { * Arguments: * - fvk: [c_uchar; 96] the serialized form of a Sapling full viewing key * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key - * - j: [c_uchar; 11] the 88-bit diversifier address at which to start - * searching, encoded in little-endian order + * - j: [c_uchar; 11] the 88-bit diversifier index, encoded in little-endian + * order * - addr_ret: [c_uchar; 43] array to which the returned address will be * written, if the specified diversifier index `j` produces a valid * address. @@ -338,7 +338,7 @@ extern "C" { * Arguments: * - fvk: [c_uchar; 96] the serialized form of a Sapling full viewing key * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key - * - j: [c_uchar; 11] the 88-bit diversifier address at which to start + * - j: [c_uchar; 11] the 88-bit diversifier index at which to start * searching, encoded in little-endian order * - j_ret: [c_uchar; 11] array that will store the diversifier index at * which the returned address was found diff --git a/src/rust/include/rust/unified_keys.h b/src/rust/include/rust/unified_keys.h index f9c837bbf00..f3929c5738c 100644 --- a/src/rust/include/rust/unified_keys.h +++ b/src/rust/include/rust/unified_keys.h @@ -56,7 +56,7 @@ char* unified_full_viewing_key_serialize( const UnifiedFullViewingKeyPtr* full_viewing_key); /** - * Reads the transparent component of a unified viewing key. + * Reads the transparent component of a unified full viewing key. * * `tkeyout` must be of length 65. * @@ -70,7 +70,7 @@ bool unified_full_viewing_key_read_transparent( unsigned char* tkeyout); /** - * Reads the Sapling component of a unified viewing key. + * Reads the Sapling component of a unified full viewing key. * * `skeyout` must be of length 128. * @@ -86,15 +86,14 @@ bool unified_full_viewing_key_read_sapling( /** * Constructs a unified full viewing key from the binary encodings - * of its constituent parts + * of its constituent parts. * - * `t_key` must be of length 65 and must be the concatenated + * If `t_key` is not `null`, it must be of length 65 and must be the concatenated * bytes of the serialized `(ChainCode, CPubKey)` pair. * - * `sapling_key` must be of length 128 and must be the concatenated - * bytes of the serialized `(SaplingFullViewingKey, DiversifierKey)` - * pair in the encoding given by `EncodeExtFVKParts` defined in - * ZIP 32. + * If `sapling_key` is not `null`, it must be of length 128 and must be the concatenated + * bytes of the `(ak, nk, ovk, dk)` fields in the encoding given by + * `EncodeExtFVKParts` defined in ZIP 32. * * Returns a pointer to newly allocated UFVK if the operation succeeds, * or the null pointer otherwise. The pointer returned by this function diff --git a/src/rust/src/unified_keys_ffi.rs b/src/rust/src/unified_keys_ffi.rs index 9857e6b3c65..a2adabd8221 100644 --- a/src/rust/src/unified_keys_ffi.rs +++ b/src/rust/src/unified_keys_ffi.rs @@ -34,17 +34,13 @@ pub extern "C" fn unified_full_viewing_key_parse( match unsafe { CStr::from_ptr(encoded) }.to_str() { Ok(encoded) => match Ufvk::decode(encoded) { - Ok((parsed_network, fvk)) => { - if parsed_network == network { - Box::into_raw(Box::new(fvk)) - } else { - error!( - "Key was encoded for a different network ({:?}) than what was requested ({:?})", - parsed_network, - network, - ); - std::ptr::null_mut() - } + Ok((parsed_network, fvk)) if parsed_network == network => Box::into_raw(Box::new(fvk)), + Ok((parsed_network, _)) => { + error!( + "Key was encoded for a different network ({:?}) than what was requested ({:?})", + parsed_network, network, + ); + std::ptr::null_mut() } Err(e) => { error!("Failure decoding unified full viewing key: {}", e); @@ -125,10 +121,6 @@ pub extern "C" fn unified_full_viewing_key_from_components( match Ufvk::try_from_items(items) { Ok(ufvk) => Box::into_raw(Box::new(ufvk)), Err(e) => { - println!( - "An error occurred constructing the unified full viewing key: {:?}", - e - ); error!( "An error occurred constructing the unified full viewing key: {:?}", e From d0b85b69f36ffae7dcf687300bff1e7dee0fbb26 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Wed, 15 Sep 2021 01:10:51 -0600 Subject: [PATCH 165/514] add ParseArbitraryInt() for diversifier index --- src/test/util_tests.cpp | 103 +++++++++++++++++++++++++++++++++++++++ src/utilstrencodings.cpp | 22 +++++++++ src/utilstrencodings.h | 5 ++ 3 files changed, 130 insertions(+) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 0e75189db7d..1b0b0d3602f 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -492,4 +492,107 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount)); } +BOOST_AUTO_TEST_CASE(test_ParseArbitraryInt) +{ + // Negation not allowed. + BOOST_CHECK(!ParseArbitraryInt("-1")); + + // no legal digits + BOOST_CHECK(!ParseArbitraryInt("")); + BOOST_CHECK(!ParseArbitraryInt(" ")); + + // Hex not allowed (only decimal). + BOOST_CHECK(!ParseArbitraryInt("ab")); + BOOST_CHECK(!ParseArbitraryInt("0xab")); + + // Decimal point not allowed. + BOOST_CHECK(!ParseArbitraryInt("1.")); + + std::optional> v; + + // simple success case + v = ParseArbitraryInt("1"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + + // Leading and trailing whitespace (spaces and tabs) is allowed. + v = ParseArbitraryInt(" 1"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + v = ParseArbitraryInt(" 1"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + v = ParseArbitraryInt(" \t1 "); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + + // Leading zeros have no effect, does not mean octal + v = ParseArbitraryInt("010\t"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 10); + + v = ParseArbitraryInt(" 255\t"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 255); + + v = ParseArbitraryInt("\t 256"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 2); + BOOST_CHECK_EQUAL((*v)[0], 0); + BOOST_CHECK_EQUAL((*v)[1], 1); + + v = ParseArbitraryInt("257 \t"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 2); + BOOST_CHECK_EQUAL((*v)[0], 1); + BOOST_CHECK_EQUAL((*v)[1], 1); + + v = ParseArbitraryInt("65535"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 2); + BOOST_CHECK_EQUAL((*v)[0], 255); + BOOST_CHECK_EQUAL((*v)[1], 255); + + v = ParseArbitraryInt("65536"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 3); + BOOST_CHECK_EQUAL((*v)[0], 0); + BOOST_CHECK_EQUAL((*v)[1], 0); + BOOST_CHECK_EQUAL((*v)[2], 1); + + // This decimal string came from: + // $ echo 16i 102030405060708090A0B0C0D0E0F0F1F2F3F4F5F6F7 p | dc + v = ParseArbitraryInt("6033354224708459019450009057293028077350189222196983"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 22); + BOOST_CHECK_EQUAL((*v)[0], 0xf7); + BOOST_CHECK_EQUAL((*v)[1], 0xf6); + BOOST_CHECK_EQUAL((*v)[2], 0xf5); + BOOST_CHECK_EQUAL((*v)[3], 0xf4); + BOOST_CHECK_EQUAL((*v)[4], 0xf3); + BOOST_CHECK_EQUAL((*v)[5], 0xf2); + BOOST_CHECK_EQUAL((*v)[6], 0xf1); + BOOST_CHECK_EQUAL((*v)[7], 0xf0); + BOOST_CHECK_EQUAL((*v)[8], 0xe0); + BOOST_CHECK_EQUAL((*v)[9], 0xd0); + BOOST_CHECK_EQUAL((*v)[10], 0xc0); + BOOST_CHECK_EQUAL((*v)[11], 0xb0); + BOOST_CHECK_EQUAL((*v)[12], 0xa0); + BOOST_CHECK_EQUAL((*v)[13], 0x90); + BOOST_CHECK_EQUAL((*v)[14], 0x80); + BOOST_CHECK_EQUAL((*v)[15], 0x70); + BOOST_CHECK_EQUAL((*v)[16], 0x60); + BOOST_CHECK_EQUAL((*v)[17], 0x50); + BOOST_CHECK_EQUAL((*v)[18], 0x40); + BOOST_CHECK_EQUAL((*v)[19], 0x30); + BOOST_CHECK_EQUAL((*v)[20], 0x20); + BOOST_CHECK_EQUAL((*v)[21], 0x10); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 0b6332a6350..5665d4c9d2d 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -506,3 +506,25 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) return true; } +/// Parse the decimal string into a little-endian byte vector. +std::optional> ParseArbitraryInt(const std::string& num_string) +{ + std::vector result; + const size_t start = num_string.find_first_not_of(WHITESPACE); + if (start == std::string::npos) return std::nullopt; + const size_t end = num_string.find_last_not_of(WHITESPACE); + assert(end != std::string::npos); + for (char c : num_string.substr(start, end-start+1)) { + if (c < '0' || c > '9') { + return std::nullopt; + } + uint16_t v = c - '0'; + for (auto& r : result) { + v += r * 10; + r = v & 0xFF; // store low byte of the value + v >>= 8; // carry to the next result position + } + if (v > 0) result.push_back(v); + } + return result; +} diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index d756db70c3a..04073db6f5f 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -12,6 +12,9 @@ #include #include #include +#include + +constexpr char WHITESPACE[] = " \t"; #define BEGIN(a) ((char*)&(a)) #define END(a) ((char*)&((&(a))[1])) @@ -164,4 +167,6 @@ bool ConvertBits(const O& outfn, I it, I end) { return true; } +std::optional> ParseArbitraryInt(const std::string& s); + #endif // BITCOIN_UTILSTRENCODINGS_H From cc70cd2c464345fe2781b3de864448c4d6dd8866 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 21 Sep 2021 11:45:59 -0600 Subject: [PATCH 166/514] add -orchardwallet experimental feature flag Also, temporarily don't allow -orchardwallet if running mainnet. --- src/experimental_features.cpp | 6 ++++++ src/experimental_features.h | 1 + src/init.cpp | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/src/experimental_features.cpp b/src/experimental_features.cpp index 67afbf7fcc2..bd1f3c3a5dc 100644 --- a/src/experimental_features.cpp +++ b/src/experimental_features.cpp @@ -11,6 +11,7 @@ bool fExperimentalDeveloperSetPoolSizeZero = false; bool fExperimentalPaymentDisclosure = false; bool fExperimentalInsightExplorer = false; bool fExperimentalLightWalletd = false; +bool fExperimentalOrchardWallet = false; std::optional InitExperimentalMode() { @@ -20,6 +21,7 @@ std::optional InitExperimentalMode() fExperimentalPaymentDisclosure = GetBoolArg("-paymentdisclosure", false); fExperimentalInsightExplorer = GetBoolArg("-insightexplorer", false); fExperimentalLightWalletd = GetBoolArg("-lightwalletd", false); + fExperimentalOrchardWallet = GetBoolArg("-orchardwallet", false); // Fail if user has set experimental options without the global flag if (!fExperimentalMode) { @@ -33,6 +35,8 @@ std::optional InitExperimentalMode() return _("Insight explorer requires -experimentalfeatures."); } else if (fExperimentalLightWalletd) { return _("Light Walletd requires -experimentalfeatures."); + } else if (fExperimentalOrchardWallet) { + return _("Orchard-enabled wallet requires -experimentalfeatures."); } } return std::nullopt; @@ -51,6 +55,8 @@ std::vector GetExperimentalFeatures() experimentalfeatures.push_back("insightexplorer"); if (fExperimentalLightWalletd) experimentalfeatures.push_back("lightwalletd"); + if (fExperimentalOrchardWallet) + experimentalfeatures.push_back("orchardwallet"); return experimentalfeatures; } diff --git a/src/experimental_features.h b/src/experimental_features.h index d607f1d5313..d2197be4871 100644 --- a/src/experimental_features.h +++ b/src/experimental_features.h @@ -14,6 +14,7 @@ extern bool fExperimentalDeveloperSetPoolSizeZero; extern bool fExperimentalPaymentDisclosure; extern bool fExperimentalInsightExplorer; extern bool fExperimentalLightWalletd; +extern bool fExperimentalOrchardWallet; std::optional InitExperimentalMode(); std::vector GetExperimentalFeatures(); diff --git a/src/init.cpp b/src/init.cpp index 158c58bd956..fedf8563be0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -947,6 +947,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) return InitError(err.value()); } + // Just temporarily (until fully functional), don't allow the Orchard wallet + // extensions if we're on mainnet + if (fExperimentalOrchardWallet && chainparams.NetworkIDString() == "main") { + return InitError(_("The -orchardwallet setting is not yet available on mainnet.")); + } + // if using block pruning, then disable txindex if (GetArg("-prune", 0)) { if (GetBoolArg("-txindex", DEFAULT_TXINDEX)) From 48446b5e39102cd831a482a3ee6c593b6229437a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 22 Nov 2021 11:56:49 -0700 Subject: [PATCH 167/514] Add functions for generating BIP-44 and ZIP-32 keypaths --- src/zcash/address/bip44.cpp | 6 +++++- src/zcash/address/bip44.h | 2 ++ src/zcash/address/zip32.cpp | 24 +++++++++++++----------- src/zcash/address/zip32.h | 10 +++++++++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/zcash/address/bip44.cpp b/src/zcash/address/bip44.cpp index f3e8b69e8f1..3456cc3c1dd 100644 --- a/src/zcash/address/bip44.cpp +++ b/src/zcash/address/bip44.cpp @@ -4,6 +4,10 @@ #include "bip44.h" +HDKeyPath libzcash::Bip44TransparentAccountKeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId) { + return "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; +} + std::optional> libzcash::DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { auto rawSeed = seed.RawSeed(); auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); @@ -21,7 +25,7 @@ std::optional> libzcash::DeriveBip44TransparentAcc auto result = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT); if (!result.has_value()) return std::nullopt; - auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + auto hdKeypath = libzcash::Bip44TransparentAccountKeyPath(bip44CoinType, accountId); return std::make_pair(result.value(), hdKeypath); } diff --git a/src/zcash/address/bip44.h b/src/zcash/address/bip44.h index 4a6bc15c930..cea73531245 100644 --- a/src/zcash/address/bip44.h +++ b/src/zcash/address/bip44.h @@ -9,6 +9,8 @@ namespace libzcash { +HDKeyPath Bip44TransparentAccountKeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId); + std::optional> DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); class Bip44AccountChains { diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index c0512a43da9..f5a753b3185 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -173,10 +173,7 @@ std::pair SaplingExtendedSpendingKey::For // Derive account key at the given account index auto xsk = m_32h_cth.Derive(accountId | HARDENED_KEY_LIMIT); - // Create new metadata - auto hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - - return std::make_pair(xsk, hdKeypath); + return std::make_pair(xsk, libzcash::Zip32AccountKeyPath(bip44CoinType, accountId)); } std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { @@ -198,13 +195,7 @@ std::pair SaplingExtendedSpendingKey::Leg // Derive key at the specified address index auto xsk = m_32h_cth_l.Derive(addressIndex | HARDENED_KEY_LIMIT); - // Create new metadata - auto hdKeypath = "m/32'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(ZCASH_LEGACY_ACCOUNT) + "'/" - + std::to_string(addressIndex) + "'"; - - return std::make_pair(xsk, hdKeypath); + return std::make_pair(xsk, libzcash::Zip32AccountKeyPath(bip44CoinType, ZCASH_LEGACY_ACCOUNT, addressIndex)); } SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const @@ -219,6 +210,17 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const return ret; } +HDKeyPath Zip32AccountKeyPath( + uint32_t bip44CoinType, + libzcash::AccountId accountId, + std::optional legacyAddressIndex) { + HDKeyPath addrSuffix = ""; + if (legacyAddressIndex.has_value()) { + addrSuffix = "/" + std::to_string(legacyAddressIndex.value()) + "'"; + } + return "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'" + addrSuffix; +} + std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath) { std::regex pattern("m/" + std::to_string(purpose) + "'/" + std::to_string(coinType) + "'/([0-9]+)'.*"); std::smatch matches; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 2a18d1af418..51d21e529cd 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -240,7 +240,15 @@ struct SaplingExtendedSpendingKey { } }; -std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath); +HDKeyPath Zip32AccountKeyPath( + uint32_t bip44CoinType, + libzcash::AccountId accountId, + std::optional legacyAddressIndex = std::nullopt); + +std::optional ParseHDKeypathAccount( + uint32_t purpose, + uint32_t coinType, + const std::string& keyPath); } //namespace libzcash From d0e836a15e085fe7b469db141ec1337f2a7be5a0 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 14 Dec 2021 18:07:11 -0700 Subject: [PATCH 168/514] Don't log 'ERROR: spent index not enabled' --- doc/release-notes.md | 6 +++++ src/main.cpp | 56 +++++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index a29094b5174..1d68f1c351c 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,3 +4,9 @@ release-notes at release time) Notable changes =============== +RPC changes +----------- + +- Fixed an issue where `ERROR: spent index not enabled` would be logged + unnecessarily on nodes that have either insightexplorer or lightwalletd + configuration options enabled. diff --git a/src/main.cpp b/src/main.cpp index 91863a38f6e..341e3eb4134 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2019,27 +2019,31 @@ bool AcceptToMemoryPool( bool GetTimestampIndex(unsigned int high, unsigned int low, bool fActiveOnly, std::vector > &hashes) { - if (!fTimestampIndex) - return error("Timestamp index not enabled"); - - if (!pblocktree->ReadTimestampIndex(high, low, fActiveOnly, hashes)) - return error("Unable to get hashes for timestamps"); - + if (!fTimestampIndex) { + LogPrint("rpc", "Timestamp index not enabled"); + return false; + } + if (!pblocktree->ReadTimestampIndex(high, low, fActiveOnly, hashes)) { + LogPrint("rpc", "Unable to get hashes for timestamps"); + return false; + } return true; } bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) { AssertLockHeld(cs_main); - if (!fSpentIndex) - return error("Spent index not enabled"); - + if (!fSpentIndex) { + LogPrint("rpc", "Spent index not enabled"); + return false; + } if (mempool.getSpentIndex(key, value)) return true; - if (!pblocktree->ReadSpentIndex(key, value)) - return error("Unable to get spent index information"); - + if (!pblocktree->ReadSpentIndex(key, value)) { + LogPrint("rpc", "Unable to get spent index information"); + return false; + } return true; } @@ -2047,24 +2051,28 @@ bool GetAddressIndex(const uint160& addressHash, int type, std::vector& addressIndex, int start, int end) { - if (!fAddressIndex) - return error("address index not enabled"); - - if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex, start, end)) - return error("unable to get txids for address"); - + if (!fAddressIndex) { + LogPrint("rpc", "address index not enabled"); + return false; + } + if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex, start, end)) { + LogPrint("rpc", "unable to get txids for address"); + return false; + } return true; } bool GetAddressUnspent(const uint160& addressHash, int type, std::vector& unspentOutputs) { - if (!fAddressIndex) - return error("address index not enabled"); - - if (!pblocktree->ReadAddressUnspentIndex(addressHash, type, unspentOutputs)) - return error("unable to get txids for address"); - + if (!fAddressIndex) { + LogPrint("rpc", "address index not enabled"); + return false; + } + if (!pblocktree->ReadAddressUnspentIndex(addressHash, type, unspentOutputs)) { + LogPrint("rpc", "unable to get txids for address"); + return false; + } return true; } From 00bda351f814f391be540a47f43d08394a493b61 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 14 Dec 2021 14:54:02 -0700 Subject: [PATCH 169/514] Check the output of zip339_phrase_to_seed in MnemonicSeed initialization. zip339_phrase_to_seed will return `false` if the string it is being used to convert to a seed is not valid UTF-8, but this result was previously unchecked. Fixes #5399 --- src/gtest/test_keystore.cpp | 17 +++++++++++++++++ src/utiltest.cpp | 2 +- src/zcash/address/mnemonic.cpp | 6 +++++- src/zcash/address/mnemonic.h | 22 +++++++++++++++------- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index c9b230b5934..3f78ef88bce 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -45,6 +45,23 @@ TEST(KeystoreTests, StoreAndRetrieveMnemonicSeed) { EXPECT_EQ(seed, seedOut.value()); } +TEST(KeystoreTests, DecodeInvalidMnemonic) { + SecureString mnemonic("\xff"); + EXPECT_FALSE(MnemonicSeed::ForPhrase(Language::English, mnemonic).has_value()); +} + +TEST(KeystoreTests, DeserializeMnemonic) { + CDataStream ss0(SER_NETWORK, CLIENT_VERSION); + ss0 << (uint32_t)English; + ss0 << SecureString("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); + EXPECT_NO_THROW(MnemonicSeed::Read(ss0)); + + CDataStream ss(SER_NETWORK, CLIENT_VERSION); + ss << (uint32_t)English; + ss << SecureString("\xff"); + EXPECT_THROW(MnemonicSeed::Read(ss), std::ios_base::failure); +} + TEST(KeystoreTests, StoreAndRetrieveLegacyHDSeed) { CBasicKeyStore keyStore; diff --git a/src/utiltest.cpp b/src/utiltest.cpp index afd60ae35bf..189c7daeb6f 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -317,7 +317,7 @@ void RegtestDeactivateNU5() { libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() { SecureString mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"); - MnemonicSeed seed(English, mnemonic); + auto seed{MnemonicSeed::ForPhrase(English, mnemonic).value()}; return libzcash::SaplingExtendedSpendingKey::Master(seed); } diff --git a/src/zcash/address/mnemonic.cpp b/src/zcash/address/mnemonic.cpp index 97ac243897a..db79be14aef 100644 --- a/src/zcash/address/mnemonic.cpp +++ b/src/zcash/address/mnemonic.cpp @@ -20,7 +20,11 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); SecureString mnemonic(phrase); zip339_free_phrase(phrase); - MnemonicSeed seed(language, mnemonic); + + // The phrase returned from zip339_entropy_to_phrase should always be a + // valid UTF-8 string; this `.value()` unwrap will correctly throw a + // `std::bad_optional_access` exception if that invariant does not hold. + auto seed = MnemonicSeed::ForPhrase(language, mnemonic).value(); // Verify that the seed data is valid entropy for unified spending keys at // account 0 and at both the public & private chain levels for account 0x7FFFFFFF. diff --git a/src/zcash/address/mnemonic.h b/src/zcash/address/mnemonic.h index db9f6a517de..57980f68d20 100644 --- a/src/zcash/address/mnemonic.h +++ b/src/zcash/address/mnemonic.h @@ -12,16 +12,22 @@ class MnemonicSeed: public HDSeed { Language language; SecureString mnemonic; - MnemonicSeed() {} - - void SetSeedFromMnemonic() { + bool SetSeedFromMnemonic() { seed.resize(64); - zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); + return zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); } + MnemonicSeed() {} public: - MnemonicSeed(Language languageIn, SecureString mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { - SetSeedFromMnemonic(); + static std::optional ForPhrase(Language languageIn, SecureString mnemonicIn) { + MnemonicSeed seed; + seed.language = languageIn; + seed.mnemonic = mnemonicIn; + if (seed.SetSeedFromMnemonic()) { + return seed; + } else { + return std::nullopt; + } } /** @@ -68,7 +74,9 @@ class MnemonicSeed: public HDSeed { READWRITE(language0); READWRITE(mnemonic); language = (Language) language0; - SetSeedFromMnemonic(); + if (!SetSeedFromMnemonic()) { + throw std::ios_base::failure("Could not interpret the mnemonic phrase as a valid UTF-8 string."); + } } else { uint32_t language0 = (uint32_t) language; From 4966bf315a1d433628cf7ecdabdcdf04e7499ef5 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Nov 2021 20:04:37 -0700 Subject: [PATCH 170/514] Compute key id for UFVKs. --- src/chainparams.cpp | 6 +++--- src/chainparams.h | 23 ++++++++--------------- src/key_constants.h | 15 +++++++++++++++ src/zcash/Address.cpp | 16 ++++++++++++++++ src/zcash/Address.hpp | 23 ++++++++++++++++------- src/zcash/address/unified.cpp | 7 ++++--- src/zcash/address/unified.h | 4 +++- 7 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 4df45f935cb..a87dc4d515a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -86,7 +86,7 @@ class CMainParams : public CChainParams { CMainParams() { keyConstants.strNetworkID = "main"; strCurrencyUnits = "ZEC"; - bip44CoinType = 133; // As registered in https://github.com/satoshilabs/slips/blob/master/slip-0044.md + keyConstants.bip44CoinType = 133; // As registered in https://github.com/satoshilabs/slips/blob/master/slip-0044.md consensus.fCoinbaseMustBeShielded = true; consensus.nSubsidySlowStartInterval = 20000; consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_HALVING_INTERVAL; @@ -370,7 +370,7 @@ class CTestNetParams : public CChainParams { CTestNetParams() { keyConstants.strNetworkID = "test"; strCurrencyUnits = "TAZ"; - bip44CoinType = 1; + keyConstants.bip44CoinType = 1; consensus.fCoinbaseMustBeShielded = true; consensus.nSubsidySlowStartInterval = 20000; consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_HALVING_INTERVAL; @@ -621,7 +621,7 @@ class CRegTestParams : public CChainParams { CRegTestParams() { keyConstants.strNetworkID = "regtest"; strCurrencyUnits = "REG"; - bip44CoinType = 1; + keyConstants.bip44CoinType = 1; consensus.fCoinbaseMustBeShielded = false; consensus.nSubsidySlowStartInterval = 0; consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_REGTEST_HALVING_INTERVAL; diff --git a/src/chainparams.h b/src/chainparams.h index 8ff3632f645..4b04ac967dc 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -32,17 +32,6 @@ struct CCheckpointData { double fTransactionsPerDay; }; -class CBaseKeyConstants : public KeyConstants { -public: - std::string NetworkIDString() const { return strNetworkID; } - const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } - const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; } - - std::string strNetworkID; - std::vector base58Prefixes[KeyConstants::MAX_BASE58_TYPES]; - std::string bech32HRPs[KeyConstants::MAX_BECH32_TYPES]; -}; - /** * CChainParams defines various tweakable parameters of a given instance of the * Bitcoin system. There are three: the main network on which people trade goods @@ -73,14 +62,19 @@ class CChainParams: public KeyConstants bool RequireStandard() const { return fRequireStandard; } int64_t PruneAfterHeight() const { return nPruneAfterHeight; } std::string CurrencyUnits() const { return strCurrencyUnits; } - uint32_t BIP44CoinType() const { return bip44CoinType; } /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } /** In the future use NetworkIDString() for RPC fields */ bool TestnetToBeDeprecatedFieldRPC() const { return fTestnetToBeDeprecatedFieldRPC; } - /** Return the BIP70 network string (main, test or regtest) */ - std::string NetworkIDString() const { return keyConstants.NetworkIDString(); } const std::vector& DNSSeeds() const { return vSeeds; } + /** Return the BIP70 network string (main, test or regtest) */ + std::string NetworkIDString() const { + return keyConstants.NetworkIDString(); + } + /** Return the BIP44 coin type for addresses created by the zcashd embedded wallet. */ + uint32_t BIP44CoinType() const { + return keyConstants.BIP44CoinType(); + } const std::vector& Base58Prefix(Base58Type type) const { return keyConstants.Base58Prefix(type); } @@ -107,7 +101,6 @@ class CChainParams: public KeyConstants std::vector vSeeds; CBaseKeyConstants keyConstants; std::string strCurrencyUnits; - uint32_t bip44CoinType; CBlock genesis; std::vector vFixedSeeds; bool fMiningRequiresPeers = false; diff --git a/src/key_constants.h b/src/key_constants.h index 70b8f87e9f9..62a795c24f1 100644 --- a/src/key_constants.h +++ b/src/key_constants.h @@ -6,6 +6,7 @@ #define ZCASH_KEY_CONSTANTS_H #include +#include class KeyConstants { @@ -35,8 +36,22 @@ class KeyConstants }; virtual std::string NetworkIDString() const =0; + virtual uint32_t BIP44CoinType() const =0; virtual const std::vector& Base58Prefix(Base58Type type) const =0; virtual const std::string& Bech32HRP(Bech32Type type) const =0; }; +class CBaseKeyConstants : public KeyConstants { +public: + std::string strNetworkID; + uint32_t bip44CoinType; + std::vector base58Prefixes[KeyConstants::MAX_BASE58_TYPES]; + std::string bech32HRPs[KeyConstants::MAX_BECH32_TYPES]; + + std::string NetworkIDString() const { return strNetworkID; } + uint32_t BIP44CoinType() const { return bip44CoinType; } + const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; } +}; + #endif // ZCASH_KEY_CONSTANTS_H diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 3481ae08db4..af2a1d873f5 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -12,6 +12,10 @@ const uint8_t ZCASH_UA_TYPECODE_SAPLING = 0x02; namespace libzcash { +// +// Unified Addresses +// + std::vector UnifiedAddress::GetSorted() const { std::vector sorted; for (const auto& receiver : receivers) { @@ -189,6 +193,10 @@ std::set GetRawAddresses::operator()( return ret; } +// +// Unified full viewing keys +// + std::optional libzcash::UnifiedFullViewingKey::Decode( const std::string& str, const KeyConstants& keyConstants) { @@ -280,3 +288,11 @@ libzcash::UnifiedFullViewingKey libzcash::UnifiedFullViewingKey::FromZcashdUFVK( } return result.value(); } + +libzcash::UFVKId libzcash::UnifiedFullViewingKey::GetKeyID(const KeyConstants& keyConstants) const { + // The ID of a ufvk is the blake2b hash of the serialized form of the + // ufvk with the receivers sorted in order of descending receiver type. + CBLAKE2bWriter h(SER_GETHASH, 0, ZCASH_UFVK_ID_PERSONAL); + h << Encode(keyConstants); + return libzcash::UFVKId(h.GetHash()); +} diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index b6a63c8db57..99b9e4a436f 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -3,6 +3,7 @@ #include "key_constants.h" #include "pubkey.h" +#include "key_constants.h" #include "script/script.h" #include "uint256.h" #include "zcash/address/orchard.hpp" @@ -132,6 +133,12 @@ class UnifiedAddress { class UnifiedFullViewingKeyBuilder; +class UFVKId: public uint256 { +public: + UFVKId() : uint256() {} + UFVKId(const uint256& in) : uint256(in) {} +}; + /** * Wrapper for a zcash_address::unified::Ufvk. */ @@ -147,9 +154,10 @@ class UnifiedFullViewingKey { friend class UnifiedFullViewingKeyBuilder; public: - static std::optional Decode( - const std::string& str, - const KeyConstants& keyConstants); + UnifiedFullViewingKey(UnifiedFullViewingKey&& key) : inner(std::move(key.inner)) {} + + UnifiedFullViewingKey(const UnifiedFullViewingKey& key) : + inner(unified_full_viewing_key_clone(key.inner.get()), unified_full_viewing_key_free) {} /** * This method should only be used for serialization of unified full @@ -161,16 +169,17 @@ class UnifiedFullViewingKey { */ static UnifiedFullViewingKey FromZcashdUFVK(const ZcashdUnifiedFullViewingKey&); + static std::optional Decode( + const std::string& str, + const KeyConstants& keyConstants); + std::string Encode(const KeyConstants& keyConstants) const; std::optional GetSaplingKey() const; std::optional GetTransparentKey() const; - UnifiedFullViewingKey(UnifiedFullViewingKey&& key) : inner(std::move(key.inner)) {} - - UnifiedFullViewingKey(const UnifiedFullViewingKey& key) : - inner(unified_full_viewing_key_clone(key.inner.get()), unified_full_viewing_key_free) {} + UFVKId GetKeyID(const KeyConstants& keyConstants) const; UnifiedFullViewingKey& operator=(UnifiedFullViewingKey&& key) { diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index 56d2c1fc879..fece68927ea 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -12,9 +12,11 @@ using namespace libzcash; // Unified Keys // -std::optional> ZcashdUnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, AccountId accountId) { +std::optional> ZcashdUnifiedSpendingKey::ForAccount( + const HDSeed& seed, + const uint32_t bip44CoinType, + AccountId accountId) { ZcashdUnifiedSpendingKey usk; - usk.accountId = accountId; auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); if (!transparentKey.has_value()) return std::nullopt; @@ -105,4 +107,3 @@ std::pair ZcashdUnifiedFullViewingKey::Find } return std::make_pair(addr.value(), j); } - diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 9742eb2fa02..cda543f9ebb 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -8,6 +8,9 @@ #include "zip32.h" #include "bip44.h" +const unsigned char ZCASH_UFVK_ID_PERSONAL[BLAKE2bPersonalBytes] = + {'Z', 'c', 'a', 's', 'h', '_', 'U', 'F', 'V', 'K', '_', 'I', 'd', '_', 'F', 'P'}; + namespace libzcash { class ZcashdUnifiedSpendingKey; @@ -55,7 +58,6 @@ class ZcashdUnifiedFullViewingKey { */ class ZcashdUnifiedSpendingKey { private: - libzcash::AccountId accountId; std::optional transparentKey; std::optional saplingKey; From 24ff7b36ecbe35beea2e11d51093743726883199 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 22 Nov 2021 11:56:49 -0700 Subject: [PATCH 171/514] Add ZcashdUnifiedKeyMetadata and libzcash::ReceiverType --- src/zcash/address/unified.cpp | 27 ++++++++++++++++++++++++--- src/zcash/address/unified.h | 31 ++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index fece68927ea..ed6bc2445af 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -12,9 +12,25 @@ using namespace libzcash; // Unified Keys // -std::optional> ZcashdUnifiedSpendingKey::ForAccount( +std::optional ZcashdUnifiedKeyMetadata::TransparentKeyPath() const { + if(std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::P2PKH) != receiverTypes.end()) { + return libzcash::Bip44TransparentAccountKeyPath(bip44CoinType, accountId); + } else { + return std::nullopt; + } +} + +std::optional ZcashdUnifiedKeyMetadata::SaplingKeyPath() const { + if(std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::Sapling) != receiverTypes.end()) { + return libzcash::Bip44TransparentAccountKeyPath(bip44CoinType, accountId); + } else { + return std::nullopt; + } +} + +std::optional> ZcashdUnifiedSpendingKey::ForAccount( const HDSeed& seed, - const uint32_t bip44CoinType, + uint32_t bip44CoinType, AccountId accountId) { ZcashdUnifiedSpendingKey usk; @@ -25,7 +41,12 @@ std::optional> ZcashdUnifiedSpend auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); usk.saplingKey = saplingKey.first; - return std::make_pair(usk, saplingKey.second); + ZcashdUnifiedKeyMetadata keyMetadata( + seed.Fingerprint(), + bip44CoinType, accountId, + { ReceiverType::P2PKH, ReceiverType::Sapling }); + + return std::make_pair(usk, keyMetadata); } ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index cda543f9ebb..58393834d26 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -13,13 +13,42 @@ const unsigned char ZCASH_UFVK_ID_PERSONAL[BLAKE2bPersonalBytes] = namespace libzcash { +enum class ReceiverType: uint32_t { + P2PKH = 0x00, + P2SH = 0x01, + Sapling = 0x02, + Orchard = 0x03 +}; + class ZcashdUnifiedSpendingKey; class ZcashdUnifiedFullViewingKey; + // prototypes for the classes handling ZIP-316 encoding (in Address.hpp) class UnifiedAddress; class UnifiedFullViewingKey; +class ZcashdUnifiedKeyMetadata { +private: + uint256 seedFp; + uint32_t bip44CoinType; + libzcash::AccountId accountId; + std::vector receiverTypes; +public: + ZcashdUnifiedKeyMetadata( + uint256 seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, std::vector receiverTypes): + seedFp(seedFp), bip44CoinType(bip44CoinType), accountId(accountId), receiverTypes(receiverTypes) {} + + const uint256& GetSeedFingerprint() const { + return seedFp; + } + const std::vector& GetReceiverTypes() const { + return receiverTypes; + } + std::optional TransparentKeyPath() const; + std::optional SaplingKeyPath() const; +}; + /** * An internal-only type for unified full viewing keys that represents only the * set of receiver types that are supported by zcashd. This type does not @@ -63,7 +92,7 @@ class ZcashdUnifiedSpendingKey { ZcashdUnifiedSpendingKey() {} public: - static std::optional> ForAccount( + static std::optional> ForAccount( const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); From 6d36921b94f30f97f2a09491b127c2adb1260266 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 22 Nov 2021 16:47:59 -0700 Subject: [PATCH 172/514] Add unified key components to the transparent & Sapling wallet parts. --- src/wallet/test/rpc_wallet_tests.cpp | 1 + src/wallet/wallet.cpp | 83 +++++++++++++++++++++++----- src/wallet/wallet.h | 30 ++++++++-- src/wallet/walletdb.cpp | 1 + src/zcash/address/unified.h | 65 ++++++++++++++++++++-- src/zcash/address/zip32.h | 1 + 6 files changed, 157 insertions(+), 24 deletions(-) diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 0528ab0e654..307f5cc3167 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -79,6 +79,7 @@ static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) { auto usk = pwallet->GenerateUnifiedSpendingKeyForAccount(0); return usk.value() + .first .ToFullViewingKey() .GetSaplingKey().value() .FindAddress(libzcash::diversifier_index_t(0)).first; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f1238644d06..11012f41352 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -172,7 +172,6 @@ std::pair CWallet::GenerateLegacySaplingZKey(u } else { return std::make_pair(xsk.first, false); } - } // Add spending key to keystore @@ -288,14 +287,26 @@ CPubKey CWallet::GenerateNewKey() // if we did not successfully generate a key, try again. } while (!extKey.has_value()); - CKey secret = extKey.value().first.key; + auto pubkey = AddKey(seed.Fingerprint(), extKey.value()); + + // Update the persisted chain information + if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { + throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); + } + + return pubkey; +} + +CPubKey CWallet::AddKey(const uint256& seedFingerprint, const std::pair& extSecret) +{ + CKey secret = extSecret.first.key; CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); // Create new metadata CKeyMetadata keyMeta(GetTime()); - keyMeta.hdKeypath = extKey.value().second; - keyMeta.seedFp = seed.Fingerprint(); + keyMeta.hdKeypath = extSecret.second; + keyMeta.seedFp = seedFingerprint; mapKeyMetadata[pubkey.GetID()] = keyMeta; if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) nTimeFirstKey = keyMeta.nCreateTime; @@ -303,11 +314,6 @@ CPubKey CWallet::GenerateNewKey() if (!AddKeyPubKey(secret, pubkey)) throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); - // Update the persisted chain information - if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { - throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); - } - return pubkey; } @@ -407,7 +413,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi return false; } -ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { +std::pair CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); if (!mnemonicHDChain.has_value()) { @@ -431,7 +437,8 @@ ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() { } } -std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { +std::optional> + CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { auto seed = GetMnemonicSeed(); if (!seed.has_value()) { throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed."); @@ -439,14 +446,60 @@ std::optional CWallet::GenerateUnifiedSpendi auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId); if (usk.has_value()) { - // TODO: Save the unified full viewing key & metadata to the wallet + if (!AddUnifiedSpendingKey(usk.value().first, usk.value().second)) { + throw std::runtime_error("CWallet::GenerateUnifiedSpendingKeyForAccount(): AddUnifiedSpendingKey failed."); + } - return usk.value().first; + return usk.value(); } else { return std::nullopt; } } +// Add spending key to keystore +bool CWallet::AddUnifiedSpendingKey( + const libzcash::ZcashdUnifiedSpendingKey& sk, + const libzcash::ZcashdUnifiedKeyMetadata& metadata) { + AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata + + auto ufvk = sk.ToFullViewingKey(); + auto metaKey = std::make_pair(metadata.GetSeedFingerprint(), metadata.GetAccountId()); + mapUnifiedKeyMetadata.insert({metaKey, metadata}); + + // Add Transparent component to the wallet + if (sk.GetTransparentKey().has_value()) { + AddKey(metadata.GetSeedFingerprint(), + std::make_pair(sk.GetTransparentKey().value(), metadata.TransparentKeyPath().value())); + } + + // Add Sapling component to the wallet + auto addSpendingKey = AddSpendingKeyToWallet( + this, Params().GetConsensus(), GetTime(), + metadata.SaplingKeyPath(), metadata.GetSeedFingerprint().GetHex(), true); + if (sk.GetSaplingExtendedSpendingKey().has_value() && + addSpendingKey(sk.GetSaplingExtendedSpendingKey().value()) == KeyNotAdded) { + // If adding the Sapling key to the wallet failed, abort the process. + return false; + } + + if (!fFileBacked) { + return true; + } + + if (!IsCrypted()) { + //return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(ufvk, metadata); + } + + return true; +} + +void CWallet::LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta) +{ + AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata + auto key = std::make_pair(meta.GetSeedFingerprint(), meta.GetAccountId()); + mapUnifiedKeyMetadata.insert({key, meta}); +} + void CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) { AssertLockHeld(cs_wallet); // mapKeyMetadata @@ -5542,7 +5595,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS if (m_wallet->HaveSaplingSpendingKey(extfvk)) { return KeyAlreadyExists; } else { - if (!m_wallet-> AddSaplingZKey(sk)) { + if (!m_wallet->AddSaplingZKey(sk)) { return KeyNotAdded; } @@ -5556,7 +5609,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS if (hdKeypath.has_value()) { m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.value(); } - if (seedFpStr) { + if (seedFpStr.has_value()) { uint256 seedFp; seedFp.SetHex(seedFpStr.value()); m_wallet->mapSaplingZKeyMetadata[ivk].seedFp = seedFp; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 2beb7d18c33..957aef8614e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -803,6 +803,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SyncMetaData(std::pair::iterator, typename TxSpendMap::iterator>); void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree); + /* Add an extended secret key to the wallet. Internal use only. */ + CPubKey AddKey(const uint256& seedFingerprint, const std::pair& extSecret); + protected: bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); void MarkAffectedTransactionsDirty(const CTransaction& tx); @@ -830,6 +833,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapKeyMetadata; std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; + std::map, libzcash::ZcashdUnifiedKeyMetadata> mapUnifiedKeyMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1096,11 +1100,27 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool LoadCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, const std::vector &vchCryptedSecret); - /** - * Unified keys & addresses - */ - libzcash::ZcashdUnifiedSpendingKey GenerateNewUnifiedSpendingKey(); - std::optional GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); + // + // Unified keys & addresses + // + + //! Generate the unified spending key from the wallet's mnemonic seed + //! for the next unused account identifier. + std::pair + GenerateNewUnifiedSpendingKey(); + + //! Generate the next available unified spending key from the wallet's + //! mnemonic seed. + std::optional> + GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); + + //! Add the specified unified spending key to the wallet with the provided key + //! metadata. + bool AddUnifiedSpendingKey( + const libzcash::ZcashdUnifiedSpendingKey& sk, + const libzcash::ZcashdUnifiedKeyMetadata& metadata); + + void LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta); /** * Increment the next transaction order id diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index dfe8ebf691a..8d32d9c34c4 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -169,6 +169,7 @@ bool CWalletDB::WriteZKey(const libzcash::SproutPaymentAddress& addr, const libz // pair is: tuple_key("zkey", paymentaddress) --> secretkey return Write(std::make_pair(std::string("zkey"), addr), key, false); } + bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingExtendedSpendingKey &key, const CKeyMetadata &keyMeta) diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 58393834d26..c86f83cf9fe 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -20,6 +20,30 @@ enum class ReceiverType: uint32_t { Orchard = 0x03 }; +class ZcashdUnifiedKeyMetadata; + +// Serialization wrapper for reading and writing ReceiverType +// in CompactSize format. +class ReceiverTypeSer { +private: + ReceiverType t; + + friend class ZcashdUnifiedKeyMetadata; +public: + ReceiverTypeSer() {} // for serialization only + ReceiverTypeSer(ReceiverType t): t(t) {} + + template + void Serialize(Stream &s) const { + WriteCompactSize(s, (uint64_t) t); + } + + template + void Unserialize(Stream& s) { + t = (ReceiverType) ReadCompactSize(s); + } +}; + class ZcashdUnifiedSpendingKey; class ZcashdUnifiedFullViewingKey; @@ -30,23 +54,56 @@ class UnifiedFullViewingKey; class ZcashdUnifiedKeyMetadata { private: - uint256 seedFp; + SeedFingerprint seedFp; uint32_t bip44CoinType; libzcash::AccountId accountId; - std::vector receiverTypes; + std::vector receiverTypes; + + ZcashdUnifiedKeyMetadata() {} public: ZcashdUnifiedKeyMetadata( - uint256 seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, std::vector receiverTypes): + SeedFingerprint seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, std::vector receiverTypes): seedFp(seedFp), bip44CoinType(bip44CoinType), accountId(accountId), receiverTypes(receiverTypes) {} - const uint256& GetSeedFingerprint() const { + const SeedFingerprint& GetSeedFingerprint() const { return seedFp; } + libzcash::AccountId GetAccountId() const { + return accountId; + } const std::vector& GetReceiverTypes() const { return receiverTypes; } std::optional TransparentKeyPath() const; std::optional SaplingKeyPath() const; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(seedFp); + READWRITE(bip44CoinType); + READWRITE(accountId); + if (ser_action.ForRead()) { + std::vector serReceiverTypes; + READWRITE(serReceiverTypes); + receiverTypes.clear(); + for (ReceiverTypeSer r : serReceiverTypes) + receiverTypes.push_back(r.t); + } else { + std::vector serReceiverTypes; + for (ReceiverType r : receiverTypes) + serReceiverTypes.push_back(ReceiverTypeSer(r)); + READWRITE(serReceiverTypes); + } + } + + template + static ZcashdUnifiedKeyMetadata Read(Stream& stream) { + ZcashdUnifiedKeyMetadata meta; + stream >> meta; + return meta; + } }; /** diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 51d21e529cd..60aef73fff2 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -62,6 +62,7 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed); namespace libzcash { +typedef uint256 SeedFingerprint; typedef uint32_t AccountId; /** From f139cdc4fe9ade92ef20244fd53f7652d77b46c0 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Thu, 23 Sep 2021 08:53:20 -0600 Subject: [PATCH 173/514] Add new and modify existing Orchard RPCs, non-functional The new RPCs aren't functional, only have argument parsing and sample outputs, guarded by experimental -orchardwallet flag. These changes used the tickets linked from https://github.com/zcash/zcash/issues/5056 as a guide. --- src/rpc/client.cpp | 4 + src/test/util_tests.cpp | 8 +- src/wallet/rpcwallet.cpp | 376 ++++++++++++++++++++++++--- src/wallet/test/rpc_wallet_tests.cpp | 3 +- 4 files changed, 353 insertions(+), 38 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 85a68a5a718..1637d30d18a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -116,6 +116,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_listunspent", 1 }, { "z_listunspent", 2 }, { "z_listunspent", 3 }, + { "z_getaddressforaccount", 0}, + { "z_getaddressforaccount", 1}, + { "z_getaddressforaccount", 2}, { "z_getbalance", 1}, { "z_getbalance", 2}, { "z_gettotalbalance", 0}, @@ -128,6 +131,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_sendmany", 1}, { "z_sendmany", 2}, { "z_sendmany", 3}, + { "z_sendmany", 4}, { "z_shieldcoinbase", 2}, { "z_shieldcoinbase", 3}, { "z_getoperationstatus", 0}, diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1b0b0d3602f..5834b3e0f6b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -521,14 +521,14 @@ BOOST_AUTO_TEST_CASE(test_ParseArbitraryInt) BOOST_CHECK(v.has_value()); BOOST_CHECK_EQUAL(v->size(), 1); BOOST_CHECK_EQUAL((*v)[0], 1); - v = ParseArbitraryInt(" 1"); + v = ParseArbitraryInt("2 "); BOOST_CHECK(v.has_value()); BOOST_CHECK_EQUAL(v->size(), 1); - BOOST_CHECK_EQUAL((*v)[0], 1); - v = ParseArbitraryInt(" \t1 "); + BOOST_CHECK_EQUAL((*v)[0], 2); + v = ParseArbitraryInt(" \t3 "); BOOST_CHECK(v.has_value()); BOOST_CHECK_EQUAL(v->size(), 1); - BOOST_CHECK_EQUAL((*v)[0], 1); + BOOST_CHECK_EQUAL((*v)[0], 3); // Leading zeros have no effect, does not mean octal v = ParseArbitraryInt("010\t"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5248b572f15..88769223e0b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -153,6 +153,7 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "getnewaddress ( \"\" )\n" + "\nDEPRECATED\n" "\nReturns a new Zcash address for receiving payments.\n" "\nArguments:\n" @@ -735,7 +736,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" 0") + "\nThe amount with at least 6 confirmations, very safe\n" + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" 6") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", 6") ); @@ -800,7 +801,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) + HelpExampleCli("getbalance", "*") + "\nThe total amount in the wallet at least 5 blocks confirmed\n" + HelpExampleCli("getbalance", "\"*\" 6") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("getbalance", "\"*\", 6") ); @@ -882,7 +883,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + "\nSend two amounts to two different addresses, subtract fee from amount:\n" + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\",\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("sendmany", "\"\", \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); @@ -1290,7 +1291,7 @@ UniValue listtransactions(const UniValue& params, bool fHelp) + HelpExampleCli("listtransactions", "") + "\nList transactions 100 to 120\n" + HelpExampleCli("listtransactions", "\"*\" 20 100") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); @@ -1843,7 +1844,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) + HelpExampleCli("signmessage", "\"zcashaddress\" \"test message\"") + "\nNow lock the wallet again by removing the passphrase\n" + HelpExampleCli("walletlock", "") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") ); @@ -1915,7 +1916,7 @@ UniValue lockunspent(const UniValue& params, bool fHelp) + HelpExampleCli("listlockunspent", "") + "\nUnlock the transaction again\n" + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") ); @@ -1989,7 +1990,7 @@ UniValue listlockunspent(const UniValue& params, bool fHelp) + HelpExampleCli("listlockunspent", "") + "\nUnlock the transaction again\n" + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("listlockunspent", "") ); @@ -2260,13 +2261,14 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) " \"address\" (string) zaddr\n" " ,...\n" " ]\n" - "\nResult\n" + "\nResult (output indices for only one pool will be present):\n" "[ (array of json object)\n" " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" " \"jsindex\" (sprout) : n, (numeric) the joinsplit index\n" " \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n" " \"outindex\" (sapling) : n, (numeric) the output index\n" + " \"actionindex\" (orchard) : n, (numeric) the output index\n" " \"confirmations\" : n, (numeric) the number of confirmations\n" " \"spendable\" : true|false, (boolean) true if note can be spent by wallet, false if address is watchonly\n" " \"address\" : \"address\", (string) the shielded address\n" @@ -2401,6 +2403,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) } results.push_back(obj); } + // TODO Orchard actions } return results; @@ -2971,6 +2974,7 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "z_getnewaddress ( type )\n" + "\nDEPRECATED\n" "\nReturns a new shielded address for receiving payments.\n" "\nWith no arguments, returns a Sapling address.\n" "\nArguments:\n" @@ -3007,6 +3011,124 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) } } +UniValue z_getnewaccount(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 0) + throw runtime_error( + "z_getnewaccount\n" + "\nPrepares and returns a new account, and its corresponding default address.\n" + "\nAccounts are numbered starting from zero; this RPC method selects the next" + "\navailable sequential account number within the UA-compatible HD seed phrase.\n" + "\nThe account will be prepared with spending keys for the best and second-best" + "\nshielded pools, and the transparent pool.\n" + "\nEach new account is a separate group of funds within the wallet, and adds an" + "\nadditional performance cost to wallet scanning. If you want a new address" + "\nfor an existing account, use the z_getaddressforaccount RPC method.\n" + "\nResult:\n" + "{\n" + " \"account\": n, (numeric) the new account number\n" + " \"unifiedaddress\" (string) The default address for this account\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_getnewaccount", "") + + HelpExampleRpc("z_getnewaccount", "") + ); + + if (!fExperimentalOrchardWallet) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: the Orchard wallet experimental extensions are disabled."); + } + int64_t account = 999999; // TODO placeholder + UniValue result(UniValue::VOBJ); + result.pushKV("account", account); + result.pushKV("unifiedaddress", "TODO"); + return result; +} + +UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "z_getaddressforaccount account ( diversifier_index [\"pool\", ...] )\n" + "\nFor the given account number, derives a Unified Address in accordance" + "\nwith the remaining arguments:\n" + "\n- If no list of pools is given, the best and second-best shielded pools," + "\n along with the transparent pool, will be used." + "\n- If no diversifier index is given (or the string \"*\"), the next unused" + "\n index (that is valid for the list of pools) will be selected.\n" + "\nThe account number must have been previously generated by a call to the" + "\nz_getnewaccount RPC method.\n" + "\nOnce a Unified Address has been derived at a specific diversifier index," + "\nre-deriving it (via a subsequent call to z_getaddressforaccount with the" + "\nsame account and index) will produce the same address with the same list" + "\nof pools. An error will be returned if a different list of pools is given.\n" + "\nResult:\n" + "{\n" + " \"account\": n, (numeric) the specified account number\n" + " \"diversifier_index\": n, (numeric) the index specified or chosen\n" + " \"pools\": [\"pool\",...]\", (json array of string) the pools (e.g. \"transparent\", \"orchard\") for which the UA contains receivers\n" + " \"unifiedaddress\" (string) The corresponding address\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_getaddressforaccount", "4") + + HelpExampleCli("z_getaddressforaccount", "4 1") + + HelpExampleCli("z_getaddressforaccount", "4 1 '[\"transparent\",\"sapling\",\"orchard\"]'") + + HelpExampleRpc("z_getaddressforaccount", "4") + ); + + if (!fExperimentalOrchardWallet) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: the Orchard wallet experimental extensions are disabled."); + } + int64_t account = params[0].get_int64(); + if (account < 0 || account >= ZCASH_LEGACY_ACCOUNT) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid account number, must be 0 <= account <= (2^31)-2."); + } + // TODO: Check that the account is known to the wallet. + std::vector parsed_diversifier_index; + if (params.size() >= 2) { + if (params[1].getType() != UniValue::VNUM) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid diversifier index, must be an unsigned integer."); + } + auto parsed_diversifier_index_opt = ParseArbitraryInt(params[1].getValStr()); + if (!parsed_diversifier_index_opt.has_value()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "diversifier index must be a decimal integer."); + } + parsed_diversifier_index = parsed_diversifier_index_opt.value(); + if (parsed_diversifier_index.size() > ZC_DIVERSIFIER_SIZE) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "diversifier index is too large."); + } + } else { + // TODO get next unused diversifier index from wallet + } + // TODO: + // diversifier_t diversifier{}; + // std::copy(parsed_diversifier_index.begin(), parsed_diversifier_index.end(), diversifier.begin()); + UniValue pools(UniValue::VARR); + if (params.size() >= 3) { + pools = params[2].get_array(); + for (unsigned int i = 0; i < pools.size(); i++) { + const std::string& p = pools[i].get_str(); + if (!(p == "transparent" || p == "sapling" || p == "orchard")) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "pool arguments must be \"transparent\", \"sapling\", or \"orchard\""); + } + } + } else { + // default is all + pools.push_back("transparent"); + pools.push_back("sapling"); + pools.push_back("orchard"); + } + UniValue result(UniValue::VOBJ); + result.pushKV("account", account); + result.pushKV("diversifier_index", params[1].write()); + result.pushKV("pools", pools); + result.pushKV("unifiedaddress", "TODO"); + return result; +} + UniValue z_listaddresses(const UniValue& params, bool fHelp) { @@ -3016,7 +3138,8 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "z_listaddresses ( includeWatchonly )\n" - "\nReturns the list of Sprout and Sapling shielded addresses belonging to the wallet.\n" + "\nDEPRECATED\n" + "\nReturns the list of shielded addresses belonging to the wallet.\n" "\nArguments:\n" "1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n" "\nResult:\n" @@ -3059,6 +3182,43 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) return ret; } +UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "z_listunifiedreceivers unified_address\n" + "\nReturns the (per-pool) receivers contained within the provided UA;" + "\nthe UA may not have receivers for some pools.\n" + "\nTransactions that send funds to any of the receivers returned by this RPC" + "\nmethod will be detected by the wallet as having been sent to the unified" + "\naddress.\n" + "\nArguments:\n" + "1. unified_address (string) The unified address\n" + "\nResult:\n" + "{\n" + " \"transparent\": \"address\", (string) The legacy transparent address (P2PKH or P2SH)\n" + " \"sapling\": \"address\", (string) The legacy Sapling address\n" + " \"orchard\": \"address\" (string) The single-receiver Unified Address for the Orchard receiver\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_listunifiedreceivers", "") + + HelpExampleRpc("z_listunifiedreceivers", "") + ); + + if (!fExperimentalOrchardWallet) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: the Orchard wallet experimental extensions are disabled."); + } + std::string ua = params[0].get_str(); + UniValue result(UniValue::VOBJ); + result.pushKV("transparent", "TODO"); + result.pushKV("sapling", "TODO"); + result.pushKV("orchard", "TODO " + ua); + return result; +} + CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ignoreUnspendable=true) { std::set destinations; vector vecOutputs; @@ -3152,22 +3312,23 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) "z_listreceivedbyaddress \"address\" ( minconf )\n" "\nReturn a list of amounts received by a zaddr belonging to the node's wallet.\n" "\nArguments:\n" - "1. \"address\" (string) The private address.\n" - "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "\nResult:\n" + "1. \"address\" (string) The shielded address.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult (output indices for only one pool will be present):\n" "{\n" - " \"txid\": \"txid\", (string) the transaction id\n" - " \"amount\": xxxxx, (numeric) the amount of value in the note\n" - " \"amountZat\" : xxxx (numeric) The amount in " + MINOR_CURRENCY_UNIT + "\n" - " \"memo\": xxxxx, (string) hexadecimal string representation of memo field\n" - " \"confirmations\" : n, (numeric) the number of confirmations\n" - " \"blockheight\": n, (numeric) The block height containing the transaction\n" - " \"blockindex\": n, (numeric) The block index containing the transaction.\n" - " \"blocktime\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" - " \"jsindex\" (sprout) : n, (numeric) the joinsplit index\n" - " \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n" + " \"txid\": \"txid\", (string) the transaction id\n" + " \"amount\": xxxxx, (numeric) the amount of value in the note\n" + " \"amountZat\" : xxxx (numeric) The amount in " + MINOR_CURRENCY_UNIT + "\n" + " \"memo\": xxxxx, (string) hexadecimal string representation of memo field\n" + " \"confirmations\" : n, (numeric) the number of confirmations\n" + " \"blockheight\": n, (numeric) The block height containing the transaction\n" + " \"blockindex\": n, (numeric) The block index containing the transaction.\n" + " \"blocktime\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"jsindex\" (sprout) : n, (numeric) the joinsplit index\n" + " \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n" " \"outindex\" (sapling) : n, (numeric) the output index\n" - " \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n" + " \"actionindex\" (orchard) : n, (numeric) the output index\n" + " \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n" "}\n" "\nExamples:\n" + HelpExampleCli("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") @@ -3252,6 +3413,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) result.push_back(obj); } } + // TODO Unified address, or orchard address return result; } @@ -3263,11 +3425,13 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) if (fHelp || params.size() == 0 || params.size() > 3) throw runtime_error( "z_getbalance \"address\" ( minconf inZat )\n" + "\nDEPRECATED\n" "\nReturns the balance of a taddr or zaddr belonging to the node's wallet.\n" "\nCAUTION: If the wallet has only an incoming viewing key for this address, then spends cannot be" - "\ndetected, and so the returned balance may be larger than the actual balance.\n" + "\ndetected, and so the returned balance may be larger than the actual balance." + "\nThe argument address may not be a Unified Address; please use z_getbalanceforaddress instead.\n" "\nArguments:\n" - "1. \"address\" (string) The selected address. It may be a transparent or private address.\n" + "1. \"address\" (string) The selected address. It may be a transparent or shielded address.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "3. inZat (bool, optional, default=false) Get the result amount in " + MINOR_CURRENCY_UNIT + " (as an integer).\n" "\nResult:\n" @@ -3277,7 +3441,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) + HelpExampleCli("z_getbalance", "\"myaddress\"") + "\nThe total amount received by address \"myaddress\" at least 5 blocks confirmed\n" + HelpExampleCli("z_getbalance", "\"myaddress\" 5") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("z_getbalance", "\"myaddress\", 5") ); @@ -3299,6 +3463,9 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) auto pa = keyIO.DecodePaymentAddress(fromaddress); fromTaddr = IsValidDestination(taddr); if (!fromTaddr) { + if (false /* Unified Address, see #5191 */) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Address cannot be a Unified Address, please use z_getbalanceforaddress instead."); + } if (!IsValidPaymentAddress(pa)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } @@ -3324,6 +3491,134 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) return ValueFromAmount(nBalance); } +UniValue z_getbalanceforaddress(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "z_getbalanceforaddress \"address\" ( minconf )\n" + "\nReturns the per-pool balances of a Unified Address belonging to the node's wallet." + "\nArguments:\n" + "1. \"address\" (string) The selected address. It may be a transparent or shielded address.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "{\n" + " \"pools\": {\n" + " \"transparent\": {\n" + " \"valueZat\": amount (numeric) The amount held in the transparent pool by this account\n" + " \"},\n" + " \"sprout\": {\n" + " \"valueZat\": amount (numeric) The amount held in the sprout pool by this account\n" + " \"},\n" + " \"sapling\": {\n" + " \"valueZat\": amount (numeric) The amount held in the sapling pool by this account\n" + " \"},\n" + " \"orchard\": {\n" + " \"valueZat\": amount (numeric) The amount held in the orchard pool by this account\n" + " \"}\n" + " \"},\n" + " \"minimum_confirmations\": n (numeric) The given minconf argument\n" + "}\n" + "Result amounts are in units of " + MINOR_CURRENCY_UNIT + ".\n" + "Pools for which the balance is zero are not shown.\n" + "\nExamples:\n" + "\nThe per-pool amount received by address \"myaddress\" with at least 1 block confirmed\n" + + HelpExampleCli("z_getbalanceforaddress", "\"myaddress\"") + + "\nThe per-pool amount received by address \"myaddress\" with at least 5 blocks confirmed\n" + + HelpExampleCli("z_getbalanceforaddress", "\"myaddress\" 5") + + "\nAs a JSON RPC call\n" + + HelpExampleRpc("z_getbalanceforaddress", "\"myaddress\", 5") + ); + + if (!fExperimentalOrchardWallet) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: the Orchard wallet experimental extensions are disabled."); + } + int minconf = 1; + if (params.size() > 1) { + minconf = params[1].get_int(); + if (minconf < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); + } + } + UniValue pools(UniValue::VOBJ); + pools.pushKV("transparent", 99999.99); + pools.pushKV("sprout", 99999.99); + pools.pushKV("sapling", 99999.99); + pools.pushKV("orchard", 99999.99); + + UniValue result(UniValue::VOBJ); + result.pushKV("pools", pools); + result.pushKV("minimum_confirmations", minconf); + + return result; +} + +UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "z_getbalanceforaccount account ( minconf )\n" + "\nReturns the spendable pool balances of the given account." + "\nArguments:\n" + "1. account (numeric) The account number.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "{\n" + " \"pools\": {\n" + " \"transparent\": {\n" + " \"valueZat\": amount (numeric) The amount held in the transparent pool by this account\n" + " \"},\n" + " \"sprout\": {\n" + " \"valueZat\": amount (numeric) The amount held in the sprout pool by this account\n" + " \"},\n" + " \"sapling\": {\n" + " \"valueZat\": amount (numeric) The amount held in the sapling pool by this account\n" + " \"},\n" + " \"orchard\": {\n" + " \"valueZat\": amount (numeric) The amount held in the orchard pool by this account\n" + " \"}\n" + " \"},\n" + " \"minimum_confirmations\": n (numeric) The given minconf argument\n" + "}\n" + "Result amounts are in units of " + MINOR_CURRENCY_UNIT + ".\n" + "Pools for which the balance is zero are not shown.\n" + "\nExamples:\n" + "\nThe per-pool amount received by account 4 with at least 1 block confirmed\n" + + HelpExampleCli("z_getbalanceforaccount", "4") + + "\nThe per-pool amount received by account 4 with at least 5 block confirmations\n" + + HelpExampleCli("z_getbalanceforaccount", "4 5") + + "\nAs a JSON RPC call\n" + + HelpExampleRpc("z_getbalanceforaccount", "4 5") + ); + + if (!fExperimentalOrchardWallet) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: the Orchard wallet experimental extensions are disabled."); + } + int64_t account = params[0].get_int64(); + int minconf = 1; + if (params.size() > 1) { + minconf = params[1].get_int(); + if (minconf < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); + } + } + UniValue pools(UniValue::VOBJ); + pools.pushKV("transparent", 99999.99); + pools.pushKV("sprout", 99999.99); + pools.pushKV("sapling", 99999.99); + pools.pushKV("orchard", 99999.99); + + UniValue result(UniValue::VOBJ); + result.pushKV("pools", pools); + result.pushKV("minimum_confirmations", minconf); + + return result; +} UniValue z_gettotalbalance(const UniValue& params, bool fHelp) { @@ -3343,7 +3638,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" - " \"private\": xxxxx, (numeric) the total balance of shielded funds (in both Sprout and Sapling addresses)\n" + " \"private\": xxxxx, (numeric) the total balance of shielded funds (in all shielded addresses)\n" " \"total\": xxxxx, (numeric) the total balance of both transparent and shielded funds\n" "}\n" "\nExamples:\n" @@ -3351,7 +3646,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) + HelpExampleCli("z_gettotalbalance", "") + "\nThe total amount in the wallet at least 5 blocks confirmed\n" + HelpExampleCli("z_gettotalbalance", "5") + - "\nAs a json rpc call\n" + "\nAs a JSON RPC call\n" + HelpExampleRpc("z_gettotalbalance", "5") ); @@ -3400,14 +3695,16 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"txid\" : \"transactionid\", (string) The transaction id\n" " \"spends\" : [\n" " {\n" - " \"type\" : \"sprout|sapling\", (string) The type of address\n" + " \"type\" : \"sprout|sapling|orchard\", (string) The shielded pool\n" " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" " \"jsSpend\" : n, (numeric, sprout) the index of the spend within the JSDescription\n" " \"spend\" : n, (numeric, sapling) the index of the spend within vShieldedSpend\n" + " \"actionspend\" : n, (numeric, orchard) the index of the action within orchard bundle\n" " \"txidPrev\" : \"transactionid\", (string) The id for the transaction this note was created in\n" " \"jsPrev\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" " \"jsOutputPrev\" : n, (numeric, sprout) the index of the output within the JSDescription\n" " \"outputPrev\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n" + " \"actionPrev\" : n, (numeric, orchard) the index of the action within the orchard bundle\n" " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" @@ -3416,10 +3713,11 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " ],\n" " \"outputs\" : [\n" " {\n" - " \"type\" : \"sprout|sapling\", (string) The type of address\n" + " \"type\" : \"sprout|sapling|orchard\", (string) The shielded pool\n" " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" " \"jsOutput\" : n, (numeric, sprout) the index of the output within the JSDescription\n" " \"output\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n" + " \"actionoutput\" : n, (numeric, orchard) the index of the action within the orchard bundle\n" " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" " \"outgoing\" : true|false (boolean, sapling) True if the output is not for an address in the wallet\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" @@ -3608,6 +3906,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) addMemo(entry, memo); outputs.push_back(entry); } + // TODO unified addresses, orchard, see #5186 entry.pushKV("spends", spends); entry.pushKV("outputs", outputs); @@ -3736,9 +4035,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 4) + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( - "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee )\n" + "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee ) ( revealamount )\n" "\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision." "\nChange generated from one or more transparent addresses flows to a new transparent" "\naddress, while change generated from a shielded address returns to itself." @@ -3751,15 +4050,17 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) " The following special strings are also accepted:\n" " - \"ANY_TADDR\": Select non-coinbase UTXOs from any transparent addresses belonging to the wallet.\n" " Use z_shieldcoinbase to shield coinbase UTXOs from multiple transparent addresses.\n" + " If the address is a UA, transfer from the most recent pool with sufficient funds\n" "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n" " [{\n" - " \"address\":address (string, required) The address is a taddr or zaddr\n" + " \"address\":address (string, required) The address is a taddr, zaddr, or Unified Address\n" " \"amount\":amount (numeric, required) The numeric amount in " + CURRENCY_UNIT + " is the value\n" " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" " }, ... ]\n" "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n" "4. fee (numeric, optional, default=" + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" + "5. revealamount (boolean, optional, default=false\n" "\nResult:\n" "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" "\nExamples:\n" @@ -3773,6 +4074,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) ThrowIfInitialBlockDownload(); // Check that the from address is valid. + // Unified address (UA) allowed here (#5185) auto fromaddress = params[0].get_str(); bool fromTaddr = false; bool fromSapling = false; @@ -3831,6 +4133,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s); } + // Unified address (UA) allowed here (#5184) string address = find_value(o, "address").get_str(); bool isZaddr = false; CTxDestination taddr = keyIO.DecodeDestination(address); @@ -3994,6 +4297,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) } } } + bool reveal_amount{false}; + if (params.size() > 4) reveal_amount = params[4].get_bool(); // Use input parameters as the optional context info to be returned by z_getoperationstatus and z_getoperationresult. UniValue o(UniValue::VOBJ); @@ -4980,6 +5285,8 @@ static const CRPCCommand commands[] = { "wallet", "z_listunspent", &z_listunspent, false }, { "wallet", "z_getbalance", &z_getbalance, false }, { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, + { "wallet", "z_getbalanceforaddress", &z_getbalanceforaddress, false }, + { "wallet", "z_getbalanceforaccount", &z_getbalanceforaccount, false }, { "wallet", "z_mergetoaddress", &z_mergetoaddress, false }, { "wallet", "z_sendmany", &z_sendmany, false }, { "wallet", "z_setmigration", &z_setmigration, false }, @@ -4989,7 +5296,10 @@ static const CRPCCommand commands[] = { "wallet", "z_getoperationresult", &z_getoperationresult, true }, { "wallet", "z_listoperationids", &z_listoperationids, true }, { "wallet", "z_getnewaddress", &z_getnewaddress, true }, + { "wallet", "z_getnewaccount", &z_getnewaccount, true }, { "wallet", "z_listaddresses", &z_listaddresses, true }, + { "wallet", "z_listunifiedreceivers", &z_listunifiedreceivers, true }, + { "wallet", "z_getaddressforaccount", &z_getaddressforaccount, true }, { "wallet", "z_exportkey", &z_exportkey, true }, { "wallet", "z_importkey", &z_importkey, true }, { "wallet", "z_exportviewingkey", &z_exportviewingkey, true }, diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 7b91c216752..9181651384d 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -1128,7 +1128,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) BOOST_CHECK_THROW(CallRPC("z_sendmany"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_sendmany toofewargs"), runtime_error); - BOOST_CHECK_THROW(CallRPC("z_sendmany just too many args here"), runtime_error); + // too many arguments: + BOOST_CHECK_THROW(CallRPC("z_sendmany addr [] 1 0.001 true true"), runtime_error); // bad from address BOOST_CHECK_THROW(CallRPC("z_sendmany " From 99b2098f89b72d29c5e4d5772831c3c364c23e84 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 16 Dec 2021 08:58:14 -0700 Subject: [PATCH 174/514] Batch-verify Orchard transactions at the block level. Fixes #5316 --- src/main.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 91863a38f6e..842ffcdfc15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5374,12 +5374,13 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, // Flags used to permit skipping checks for efficiency auto verifier = ProofVerifier::Disabled(); // No need to verify JoinSplits twice bool fCheckTransactions = true; - // We may as well check Orchard authorizations if we are checking - // transactions, since we can batch-validate them. - auto orchardAuth = orchard::AuthValidator::Batch(); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { + // We may as well check Orchard authorizations if we are checking + // transactions, since we can batch-validate them. + auto orchardAuth = orchard::AuthValidator::Batch(); + boost::this_thread::interruption_point(); uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); if (pindex->nHeight < chainActive.Height()-nCheckDepth) @@ -5421,6 +5422,10 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } } + if (!orchardAuth.Validate()) { + return error("VerifyDB(): Orchard batch validation failed for block at height %d", pindex->nHeight); + } + if (ShutdownRequested()) return true; } From e25784426174f56fd4b76c689bc84f38d1c4f27b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Dec 2021 16:55:11 +0000 Subject: [PATCH 175/514] cargo update In the three days since the last update, `futures 0.3.18` was yanked due to panics: https://github.com/rust-lang/futures-rs/issues/2529 --- Cargo.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94e8f8d0c60..bc2de37d232 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -497,31 +497,32 @@ checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" [[package]] name = "futures-channel" -version = "0.3.18" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.18" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" [[package]] name = "futures-task" -version = "0.3.18" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" [[package]] name = "futures-util" -version = "0.3.18" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" dependencies = [ + "autocfg", "futures-core", "futures-task", "pin-project-lite", @@ -1005,9 +1006,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "opaque-debug" @@ -1177,9 +1178,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" dependencies = [ "unicode-xid", ] @@ -1538,11 +1539,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" dependencies = [ - "autocfg", "libc", "mio", "pin-project-lite", @@ -1552,9 +1552,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ "proc-macro2", "quote", From 0d310a79ca09d6448a92d3f4f8dfbd10bca0c56a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Dec 2021 17:08:03 +0000 Subject: [PATCH 176/514] depends: Update Boost to 1.78.0 --- depends/packages/boost.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 2f961fa7b11..f64c3474e1c 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,8 +1,8 @@ package=boost -$(package)_version=1_77_0 +$(package)_version=1_78_0 $(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$(subst _,.,$($(package)_version))/source/ $(package)_file_name=boost_$($(package)_version).tar.bz2 -$(package)_sha256_hash=fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854 +$(package)_sha256_hash=8681f175d4bdb26c52222665793eef08490d7758529330f98d3b29dd0735bccc $(package)_dependencies=native_b2 ifneq ($(host_os),darwin) From c37a69a357cc7fc680347d2cca8270c519143a10 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Dec 2021 17:55:35 +0000 Subject: [PATCH 177/514] depends Update Rust to 1.57.0 Also adds a developer script to make updating the hashes easier. --- contrib/devtools/update-rust-hashes.sh | 37 ++++++++++++++++++++++++++ depends/packages/native_rust.mk | 17 ++++++------ 2 files changed, 46 insertions(+), 8 deletions(-) create mode 100755 contrib/devtools/update-rust-hashes.sh diff --git a/contrib/devtools/update-rust-hashes.sh b/contrib/devtools/update-rust-hashes.sh new file mode 100755 index 00000000000..ad036bebf1e --- /dev/null +++ b/contrib/devtools/update-rust-hashes.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +export LC_ALL=C + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +RUST_PACKAGE="$SCRIPT_DIR/../../depends/packages/native_rust.mk" + +RUST_VERSION=$( cat $RUST_PACKAGE | grep -oP "_version=\K.*" ) + +update_hash() { + url="https://static.rust-lang.org/dist/$1-$RUST_VERSION-$2.tar.gz" + echo "Fetching $url" + hash=$( curl $url | sha256sum | awk '{print $1}' ) + sed -i "/\$(package)_$3_$4=/c\\\$(package)_$3_$4=$hash" $RUST_PACKAGE +} + +update_rust_hash() { + update_hash rust $1 sha256_hash $2 +} + +update_stdlib_hash() { + update_hash rust-std $1 rust_std_sha256_hash $1 +} + +# For native targets +# update_rust_hash RUST_TARGET MAKEFILE_PACKAGE_IDENTIFIER +update_rust_hash aarch64-unknown-linux-gnu aarch64_linux +update_rust_hash x86_64-apple-darwin darwin +update_rust_hash x86_64-unknown-linux-gnu linux +update_rust_hash x86_64-unknown-freebsd freebsd + +# For cross-compilation targets +# update_stdlib_hash RUST_TARGET +update_stdlib_hash aarch64-unknown-linux-gnu +update_stdlib_hash x86_64-apple-darwin +update_stdlib_hash x86_64-pc-windows-gnu +update_stdlib_hash x86_64-unknown-freebsd diff --git a/depends/packages/native_rust.mk b/depends/packages/native_rust.mk index 045df44444c..3b3f0ea4c6a 100644 --- a/depends/packages/native_rust.mk +++ b/depends/packages/native_rust.mk @@ -1,14 +1,14 @@ package=native_rust -$(package)_version=1.56.1 +$(package)_version=1.57.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=a6be5d045183a0b12dddf0d81633e2a64e63e4c2dfa44eb7593970c1ef93a98f +$(package)_sha256_hash_linux=ea0253784b2e5c22659ff148d492a68d2e11da734491714ebc61cc93896efcda $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=8d65ef02a123c23be00101fb204d28b60498b9145dd2ee8edabf0afde6e01e55 +$(package)_sha256_hash_darwin=15ceffc4743434c19d08f73fb4edd6642b7fd8162ed7101d3e6ca2c691fcb699 $(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz -$(package)_sha256_hash_freebsd=94e2c8b44af125ca8ba1a1ded7e7b9c5acb27e52acec4ab483d5ed9a8528c5a9 +$(package)_sha256_hash_freebsd=ebe96fa1f15e8d70c91e81aab7e0c341717b909225029f37d52fbdfa506e3fab $(package)_file_name_aarch64_linux=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_aarch64_linux=69792887357c8dd78c5424f0b4a624578296796d99edf6c30ebe2acc2b939aa3 +$(package)_sha256_hash_aarch64_linux=d66847f7cf7b548ecb328c400ac4f691ee2aea6ff5cd9286ad8733239569556c # Mapping from GCC canonical hosts to Rust targets # If a mapping is not present, we assume they are identical, unless $host_os is @@ -17,9 +17,10 @@ $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu $(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu # Mapping from Rust targets to SHA-256 hashes -$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=d577c25879cf160ec1a04d5101971dd684f9b4f87b3cb463a7521b676dc3df89 -$(package)_rust_std_sha256_hash_x86_64-apple-darwin=a1cedfaea1508bf3bfc8a77d82d15c693b41e70e56fad930d24f21f0bce5052a -$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=8c5d425d2882a93827850672a70bfc2e643cae425aaffa9dafa6808fcd4bc798 +$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=4c70901d1cbddec9ea99fbd62b20f454d30e1ffbb48a21169ac823b3f02a1fbc +$(package)_rust_std_sha256_hash_x86_64-apple-darwin=c1eb892ddb50ebeed288b7aa8171ad46d62362bb26b2d82d2b463dfd45606dc2 +$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=75c910899ed36a90b155e3a01c21b863000675867efc56f2b68c44edd4b7e18c +$(package)_rust_std_sha256_hash_x86_64-unknown-freebsd=1528a4bc7e3ba42da164bcc7b952dfa73048333c5b9254ce2d03db6bab6081e8 define rust_target $(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2)))) From 407a0d932438b9cbd0d61dc51f0ea610de9ab019 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Dec 2021 17:58:05 +0000 Subject: [PATCH 178/514] qa: Postpone recent CCache releases --- qa/zcash/postponed-updates.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qa/zcash/postponed-updates.txt b/qa/zcash/postponed-updates.txt index 98d1657f716..a3a8d8e1035 100644 --- a/qa/zcash/postponed-updates.txt +++ b/qa/zcash/postponed-updates.txt @@ -13,6 +13,8 @@ native_ccache 4.3 2022-02-01 native_ccache 4.4 2022-02-01 native_ccache 4.4.1 2022-02-01 native_ccache 4.4.2 2022-02-01 +native_ccache 4.5 2022-02-01 +native_ccache 4.5.1 2022-02-01 # Clang is currently pinned to LLVM 13 From 09e0af815f6dd03d715fba975f58d7f5f85e74e1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Dec 2021 21:54:48 +0000 Subject: [PATCH 179/514] Revert "lint: Fix false positive" This reverts commit fb38cf0d90d3b2b7b888040a601d884262d05ca0. The lint fix caused a problem on macOS, where the escaped double quote was interpreted as part of an argument, and not as defining an argument. We will need to find another way to address the lint. Closes zcash/zcash#5379. --- zcutil/fetch-params.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcutil/fetch-params.sh b/zcutil/fetch-params.sh index fba5de0787d..69d2e964db4 100755 --- a/zcutil/fetch-params.sh +++ b/zcutil/fetch-params.sh @@ -21,7 +21,7 @@ DOWNLOAD_URL="https://download.z.cash/downloads" IPFS_HASH="/ipfs/QmXRHVGLQBiKwvNq7c2vPxAKz1zRVmMYbmt7G5TQss7tY7" SHA256CMD="$(command -v sha256sum || echo shasum)" -SHA256ARGS="$(command -v sha256sum >/dev/null || echo \"-a 256\")" +SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')" WGETCMD="$(command -v wget || echo '')" IPFSCMD="$(command -v ipfs || echo '')" From 1b7a031e7b0a77b4a53a56484792ba342f1405b8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 16 Dec 2021 22:22:05 +0000 Subject: [PATCH 180/514] rust: Remove misleading log message We use the `zcash_address` crate to parse Unified Addresses (which we currently do nothing with). That crate returns an `UnsupportedAddress` error for unhandled address kinds, which we were previously logging. However, we _do_ support the other address kinds; we just parse them in the C++ code. Closes zcash/zcash#5321. --- src/rust/src/address_ffi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index a77488743b3..cf2e54bccc6 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -139,8 +139,8 @@ pub extern "C" fn zcash_address_parse_unified( let ua: UnifiedAddressHelper = match addr.convert() { Ok(ua) => ua, - Err(e) => { - tracing::error!("{}", e); + Err(_) => { + // `KeyIO::DecodePaymentAddress` handles the rest of the address kinds. return false; } }; From c937ba5ae5443b39fa53945348c875ca844193b1 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Fri, 3 Dec 2021 11:02:59 -0700 Subject: [PATCH 181/514] getblocktemplate: add NU5 commitments to new `defaultroots` section - merkleroot - authdataroot - chainhistoryroot - blockcommitmentshash --- qa/rpc-tests/getblocktemplate.py | 32 +++++++++++++++++++++----------- src/rpc/mining.cpp | 29 ++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index 223132da48f..e7a8633ee03 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -4,9 +4,15 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, connect_nodes_bi, \ - start_nodes - +from test_framework.util import ( + assert_equal, + BLOSSOM_BRANCH_ID, + CANOPY_BRANCH_ID, + HEARTWOOD_BRANCH_ID, + NU5_BRANCH_ID, + nuparams, + start_nodes, +) class GetBlockTemplateTest(BitcoinTestFramework): ''' @@ -15,12 +21,16 @@ class GetBlockTemplateTest(BitcoinTestFramework): def __init__(self): super().__init__() - self.num_nodes = 2 + self.num_nodes = 1 self.setup_clean_chain = True def setup_network(self, split=False): - self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) - connect_nodes_bi(self.nodes,0,1) + args = [nuparams(BLOSSOM_BRANCH_ID, 1), + nuparams(HEARTWOOD_BRANCH_ID, 1), + nuparams(CANOPY_BRANCH_ID, 1), + nuparams(NU5_BRANCH_ID, 1), + ] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [args] * self.num_nodes) self.is_network_split=False self.sync_all() @@ -51,15 +61,15 @@ def run_test(self): # Test 5: General checks tmpl = node.getblocktemplate() assert_equal(16, len(tmpl['noncerange'])) + # should be proposing height 2, since current tip is height 1 + assert_equal(2, tmpl['height']) # Test 6: coinbasetxn checks - assert('foundersreward' in tmpl['coinbasetxn']) assert(tmpl['coinbasetxn']['required']) - # Test 7: hashFinalSaplingRoot checks - assert('finalsaplingroothash' in tmpl) - finalsaplingroothash = '3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb' - assert_equal(finalsaplingroothash, tmpl['finalsaplingroothash']) + # Test 7: blockcommitmentshash checks + assert('blockcommitmentshash' in tmpl) + assert('00' * 32 != tmpl['finalsaplingroothash']) if __name__ == '__main__': GetBlockTemplateTest().main() diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 76b6d35f8ef..7ed48d59a4e 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -428,6 +428,11 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "amounts, use 'getblocksubsidy HEIGHT' passing in the height returned\n" "by this API.\n" + "\nThe roots returned in 'defaultroots' are only valid if the block template is\n" + "used unmodified. If any part of the block template marked as 'mutable' in the\n" + "output is mutated, these roots may need to be recomputed. For more information\n" + "on the derivation process, see ZIP 244.\n" + "\nArguments:\n" "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" " {\n" @@ -753,15 +758,29 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) aMutable.push_back("prevblock"); } + auto hashAuthDataRoot = pblock->BuildAuthDataMerkleTree(); + std::string hashBlockCommitments_hex = DeriveBlockCommitmentsHash( + pblocktemplate->hashChainHistoryRoot, + hashAuthDataRoot).GetHex(); UniValue result(UniValue::VOBJ); result.pushKV("capabilities", aCaps); result.pushKV("version", pblock->nVersion); result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); - result.pushKV("blockcommitmentshash", pblock->hashBlockCommitments.GetHex()); - // Deprecated; remove in a future release. - result.pushKV("lightclientroothash", pblock->hashBlockCommitments.GetHex()); - // Deprecated; remove in a future release. - result.pushKV("finalsaplingroothash", pblock->hashBlockCommitments.GetHex()); + // The following 3 are deprecated; remove in a future release. + result.pushKV("blockcommitmentshash", hashBlockCommitments_hex); + result.pushKV("lightclientroothash", hashBlockCommitments_hex); + result.pushKV("finalsaplingroothash", hashBlockCommitments_hex); + { + // These are items in the result object that are valid only if the + // block template returned by this RPC is used unmodified. Otherwise, + // these values must be recomputed. + UniValue defaults(UniValue::VOBJ); + defaults.pushKV("merkleroot", pblock->BuildMerkleTree().GetHex()); + defaults.pushKV("authdataroot", hashAuthDataRoot.GetHex()); + defaults.pushKV("chainhistoryroot", pblocktemplate->hashChainHistoryRoot.GetHex()); + defaults.pushKV("blockcommitmentshash", hashBlockCommitments_hex); + result.pushKV("defaultroots", defaults); + } result.pushKV("transactions", transactions); if (coinbasetxn) { assert(txCoinbase.isObject()); From 6d60f1a9c9feaac8c893d3338e194f45cace9e2b Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 11 Feb 2019 11:59:34 -0500 Subject: [PATCH 182/514] [rpc] mining: Omit uninitialized currentblockweight, currentblocktx zcash/zcash: The `getmininginfo` RPC now omits `currentblockize` and `currentblocktx` when a block was never assembled via RPC on this node during its current process instantiation. The relevant RPCs are `generate` (regtest only) and `getblocktemplate`. Blocks are also assembled when running the internal miner (`zcashd -gen=1`), after the node mines its first block. (cherry picked from commit bitcoin/bitcoin@fa178a6385bf300499fb18940051fc4142fb5b6b) --- doc/release-notes.md | 8 ++++-- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/getmininginfo.py | 47 +++++++++++++++++++++++++++++++++++ src/main.h | 4 +-- src/miner.cpp | 8 +++--- src/rpc/mining.cpp | 8 +++--- 6 files changed, 64 insertions(+), 12 deletions(-) create mode 100755 qa/rpc-tests/getmininginfo.py diff --git a/doc/release-notes.md b/doc/release-notes.md index 1d68f1c351c..49091187c91 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,9 +4,13 @@ release-notes at release time) Notable changes =============== -RPC changes ------------ +Updated RPCs +------------ - Fixed an issue where `ERROR: spent index not enabled` would be logged unnecessarily on nodes that have either insightexplorer or lightwalletd configuration options enabled. + +- The `getmininginfo` RPC now omits `currentblockize` and `currentblocktx` + when a block was never assembled via RPC on this node during its current + process instantiation. (#5404) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index c7a1057e00d..0db8b16b086 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -103,6 +103,7 @@ 'disablewallet.py', 'keypool.py', 'getblocktemplate.py', + 'getmininginfo.py', 'bip65-cltv-p2p.py', 'bipdersig-p2p.py', 'invalidblockrequest.py', diff --git a/qa/rpc-tests/getmininginfo.py b/qa/rpc-tests/getmininginfo.py new file mode 100755 index 00000000000..a1edaf12e37 --- /dev/null +++ b/qa/rpc-tests/getmininginfo.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import start_nodes + + +class GetMiningInfoTest(BitcoinTestFramework): + ''' + Test getmininginfo. + ''' + + def __init__(self): + super().__init__() + self.num_nodes = 1 + self.setup_clean_chain = True + + def setup_network(self, split=False): + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) + self.is_network_split = False + self.sync_all() + + def run_test(self): + node = self.nodes[0] + + info = node.getmininginfo() + assert(info['blocks'] == 0) + # No blocks have been mined yet, so these fields should not be present. + assert('currentblocksize' not in info) + assert('currentblocktx' not in info) + + node.generate(1) + + info = node.getmininginfo() + assert(info['blocks'] == 1) + # One block has been mined, so these fields should now be present. + assert('currentblocksize' in info) + assert('currentblocktx' in info) + assert(info['currentblocksize'] > 0) + # The transaction count doesn't include the coinbase + assert(info['currentblocktx'] == 0) + + +if __name__ == '__main__': + GetMiningInfoTest().main() diff --git a/src/main.h b/src/main.h index ab2cb54b85f..a76fe63e647 100644 --- a/src/main.h +++ b/src/main.h @@ -157,8 +157,8 @@ extern CCriticalSection cs_main; extern CTxMemPool mempool; typedef boost::unordered_map BlockMap; extern BlockMap mapBlockIndex; -extern uint64_t nLastBlockTx; -extern uint64_t nLastBlockSize; +extern std::optional last_block_num_txs; +extern std::optional last_block_size; extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; diff --git a/src/miner.cpp b/src/miner.cpp index f48137860b6..cb2c10c3bb6 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -71,8 +71,8 @@ class COrphan } }; -uint64_t nLastBlockTx = 0; -uint64_t nLastBlockSize = 0; +std::optional last_block_num_txs; +std::optional last_block_size; // We want to sort transactions by priority and fee rate, so: typedef boost::tuple TxPriority; @@ -610,8 +610,8 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre } } - nLastBlockTx = nBlockTx; - nLastBlockSize = nBlockSize; + last_block_num_txs = nBlockTx; + last_block_size = nBlockSize; LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); // Create coinbase tx diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 76b6d35f8ef..52403e97e96 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -322,8 +322,8 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"blocks\": nnn, (numeric) The current block\n" - " \"currentblocksize\": nnn, (numeric) The last block size\n" - " \"currentblocktx\": nnn, (numeric) The last block transaction\n" + " \"currentblocksize\": nnn, (numeric, optional) The block size of the last assembled block (only present if a block was ever assembled)\n" + " \"currentblocktx\": nnn, (numeric, optional) The number of block non-coinbase transactions of the last assembled block (only present if a block was ever assembled)\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" " \"errors\": \"...\" (string) Current errors\n" " \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n" @@ -344,8 +344,8 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.pushKV("blocks", (int)chainActive.Height()); - obj.pushKV("currentblocksize", (uint64_t)nLastBlockSize); - obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx); + if (last_block_size.has_value()) obj.pushKV("currentblocksize", last_block_size.value()); + if (last_block_num_txs.has_value()) obj.pushKV("currentblocktx", last_block_num_txs.value()); obj.pushKV("difficulty", (double)GetNetworkDifficulty()); auto warnings = GetWarnings("statusbar"); obj.pushKV("errors", warnings.first); From d85bfb4744d1428ecf762c4218ba5505c385dad1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 18 Dec 2021 00:20:18 +0000 Subject: [PATCH 183/514] Migrate to latest revisions of Zcash Rust crates --- Cargo.lock | 87 +++++++++++++++++++++++-------------- Cargo.toml | 22 +++++----- src/rust/src/address_ffi.rs | 15 ++++--- src/rust/src/rustzcash.rs | 6 +-- 4 files changed, 76 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc2de37d232..72c51670940 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0944d18a9a37691b87733b39c9360c9950af9aa5f97e2455bc108d8eb64fc1c1" dependencies = [ "bitvec", - "blake2s_simd", + "blake2s_simd 0.5.11", "byteorder", "crossbeam-channel", "ff", @@ -171,6 +171,17 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "blake2b_simd" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + [[package]] name = "blake2s_simd" version = "0.5.11" @@ -182,6 +193,17 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "blake2s_simd" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -444,18 +466,18 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ - "blake2b_simd", + "blake2b_simd 1.0.0", "byteorder", ] [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ - "blake2b_simd", + "blake2b_simd 1.0.0", ] [[package]] @@ -579,7 +601,7 @@ version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f186b85ed81082fb1cf59d52b0111f02915e89a4ac61d292b38d075e570f3a9" dependencies = [ - "blake2b_simd", + "blake2b_simd 0.5.11", "ff", "group", "pasta_curves", @@ -680,8 +702,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.1.0" -source = "git+https://github.com/zcash/incrementalmerkletree?rev=b7bd6246122a6e9ace8edb51553fbf5228906cbb#b7bd6246122a6e9ace8edb51553fbf5228906cbb" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186fd3ab92aeac865d4b80b410de9a7b341d31ba8281373caed0b6d17b2b5e96" dependencies = [ "serde", ] @@ -763,8 +786,8 @@ name = "librustzcash" version = "0.2.0" dependencies = [ "bellman", - "blake2b_simd", - "blake2s_simd", + "blake2b_simd 1.0.0", + "blake2s_simd 1.0.0", "bls12_381", "byteorder", "ed25519-zebra", @@ -1018,14 +1041,15 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.0.0" -source = "git+https://github.com/zcash/orchard.git?rev=2c8241f25b943aa05203eacf9905db117c69bd29#2c8241f25b943aa05203eacf9905db117c69bd29" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31f03b6d0aee6d993cac35388b818e04f076ded0ab284979e4d7cd5a8b3c2be" dependencies = [ "aes", "arrayvec 0.7.2", "bigint", "bitvec", - "blake2b_simd", + "blake2b_simd 1.0.0", "ff", "fpe", "group", @@ -1102,7 +1126,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d647d91972bad78120fd61e06b225fcda117805c9bbf17676b51bd03a251278b" dependencies = [ - "blake2b_simd", + "blake2b_simd 0.5.11", "ff", "group", "lazy_static", @@ -1311,10 +1335,11 @@ dependencies = [ [[package]] name = "reddsa" -version = "0.0.0" -source = "git+https://github.com/str4d/redjubjub.git?rev=416a6a8ebf8bd42c114c938883016c04f338de72#416a6a8ebf8bd42c114c938883016c04f338de72" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e2c94bca3445cae0d55dff7370e29c24885d2403a1665ce19c106c79455e6" dependencies = [ - "blake2b_simd", + "blake2b_simd 0.5.11", "byteorder", "digest", "group", @@ -1790,10 +1815,10 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ "bech32", - "blake2b_simd", + "blake2b_simd 1.0.0", "bs58", "f4jumble", "zcash_encoding", @@ -1802,7 +1827,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ "byteorder", "nonempty", @@ -1811,24 +1836,20 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ "bigint", - "blake2b_simd", + "blake2b_simd 1.0.0", "byteorder", ] [[package]] name = "zcash_note_encryption" -version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +version = "0.1.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ - "blake2b_simd", - "byteorder", "chacha20", "chacha20poly1305", - "ff", - "group", "rand_core 0.6.3", "subtle", ] @@ -1836,13 +1857,13 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ "aes", "bip0039", "bitvec", - "blake2b_simd", - "blake2s_simd", + "blake2b_simd 1.0.0", + "blake2s_simd 1.0.0", "bls12_381", "byteorder", "chacha20poly1305", @@ -1870,10 +1891,10 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" dependencies = [ "bellman", - "blake2b_simd", + "blake2b_simd 1.0.0", "bls12_381", "byteorder", "directories", diff --git a/Cargo.toml b/Cargo.toml index c23a774650f..6d9428b9563 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,17 +27,17 @@ crate-type = ["staticlib"] [dependencies] bellman = "0.11" -blake2b_simd = "0.5" -blake2s_simd = "0.5" +blake2b_simd = "1" +blake2s_simd = "1" bls12_381 = "0.6" byteorder = "1" group = "0.11" -incrementalmerkletree = "0.1" +incrementalmerkletree = "0.2" libc = "0.2" jubjub = "0.8" memuse = "0.2" nonempty = "0.7" -orchard = "0.0" +orchard = "=0.1.0-beta.1" subtle = "2.2" rand_core = "0.6" tracing = "0.1" @@ -45,7 +45,7 @@ tracing-core = "0.1" tracing-appender = "0.2" zcash_address = "0.0" zcash_history = "0.2" -zcash_note_encryption = "0.0" +zcash_note_encryption = "0.1" zcash_primitives = "0.5" zcash_proofs = "0.5" ed25519-zebra = "3" @@ -69,10 +69,8 @@ panic = 'abort' codegen-units = 1 [patch.crates-io] -incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } -orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index cf2e54bccc6..e4782763667 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -5,7 +5,10 @@ use std::{ }; use libc::{c_char, c_void}; -use zcash_address::{unified, FromAddress, Network, ToAddress, ZcashAddress}; +use zcash_address::{ + unified::{self, Container, Encoding}, + FromAddress, Network, ToAddress, ZcashAddress, +}; use zcash_primitives::sapling; pub type UnifiedAddressObj = NonNull; @@ -69,8 +72,8 @@ impl UnifiedAddressHelper { } self.ua - .receivers() - .into_iter() + .items_as_parsed() + .iter() .map(|receiver| match receiver { unified::Receiver::Orchard(data) => { // ZIP 316: Senders MUST reject Unified Addresses in which any @@ -92,7 +95,7 @@ impl UnifiedAddressHelper { // ZIP 316: Senders MUST reject Unified Addresses in which any // constituent address does not meet the validation requirements of // its Receiver Encoding. - if sapling::PaymentAddress::from_bytes(&data).is_none() { + if sapling::PaymentAddress::from_bytes(data).is_none() { tracing::error!("Unified Address contains invalid Sapling receiver"); false } else { @@ -106,7 +109,7 @@ impl UnifiedAddressHelper { (p2pkh_cb.unwrap())(ua_obj, data.as_ptr()) }, unified::Receiver::Unknown { typecode, data } => unsafe { - (unknown_cb.unwrap())(ua_obj, typecode, data.as_ptr(), data.len()) + (unknown_cb.unwrap())(ua_obj, *typecode, data.as_ptr(), data.len()) }, }) .all(|b| b) @@ -209,7 +212,7 @@ pub extern "C" fn zcash_address_serialize_unified( } }; - let ua: unified::Address = match receivers.try_into() { + let ua = match unified::Address::try_from_items_preserving_order(receivers) { Ok(ua) => ua, Err(e) => { tracing::error!("{}", e); diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index 399e806d571..9e9d9bd902d 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -1089,9 +1089,9 @@ pub extern "C" fn librustzcash_zip32_xfvk_address( .expect("valid ExtendedFullViewingKey"); let j = zip32::DiversifierIndex(unsafe { *j }); - let addr = match xfvk.address(j) { - Ok(addr) => addr, - Err(_) => return false, + let addr = match xfvk.find_address(j) { + Some(addr) => addr, + None => return false, }; let j_ret = unsafe { &mut *j_ret }; From 8387aa40cf23e07bdd8346fa898a022e3f595063 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 18 Dec 2021 00:40:26 +0000 Subject: [PATCH 184/514] Update release notes --- doc/release-notes.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/release-notes.md b/doc/release-notes.md index 49091187c91..a94a7919a17 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,6 +4,23 @@ release-notes at release time) Notable changes =============== +Wallet +------ + +From this release, newly-created wallets will save the chain name ("Zcash") and +network identifier (e.g. "main") to the `wallet.dat` file. This will enable the +`zcashd` node to check on subsequent starts that the `wallet.dat` file matches +the node's configuration. Existing wallets will start saving this information in +a later release. + +`libzcash_script` +----------------- + +Two new APIs have been added to this library (`zcash_script_legacy_sigop_count` +and `zcash_script_legacy_sigop_count_precomputed`), for counting the number of +signature operations in the transparent inputs and outputs of a transaction. +The presence of these APIs is indicated by a library API version of 2. + Updated RPCs ------------ From d4b850d3f7579c167177b69472143b9b5268b163 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 18 Dec 2021 04:23:05 +0000 Subject: [PATCH 185/514] make-release.py: Versioning changes for 4.6.0-rc1. --- README.md | 2 +- configure.ac | 6 +++--- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 6 +++--- src/deprecation.h | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 230a51221f5..1f974a3d3ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.5.1-1 +Zcash 4.6.0-rc1 =========== diff --git a/configure.ac b/configure.ac index cbd7e8c6518..43b90999d3d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) -define(_CLIENT_VERSION_MINOR, 5) -define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 51) +define(_CLIENT_VERSION_MINOR, 6) +define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_BUILD, 25) 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))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 4f6f2cdfbd4..c7507bc89d8 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.5.1-1" +name: "zcash-4.6.0-rc1" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index 326d068e792..fe2e3f4d08e 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,9 +16,9 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 4 -#define CLIENT_VERSION_MINOR 5 -#define CLIENT_VERSION_REVISION 1 -#define CLIENT_VERSION_BUILD 51 +#define CLIENT_VERSION_MINOR 6 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 25 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/deprecation.h b/src/deprecation.h index 6ea5b0cb7d9..24316025b8a 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -10,7 +10,7 @@ // Per https://zips.z.cash/zip-0200 // Shut down nodes running this version of code, 16 weeks' worth of blocks after the estimated // release block height. A warning is shown during the 14 days' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 1396476; +static const int APPROX_RELEASE_HEIGHT = 1498500; static const int RELEASE_TO_DEPRECATION_WEEKS = 16; static const int EXPECTED_BLOCKS_PER_HOUR = 3600 / Consensus::POST_BLOSSOM_POW_TARGET_SPACING; static_assert(EXPECTED_BLOCKS_PER_HOUR == 48, "The value of Consensus::POST_BLOSSOM_POW_TARGET_SPACING was chosen such that this assertion holds."); From 1c910cf9ccb1c1b5b9083af25bec380873c5b7f1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 18 Dec 2021 04:25:50 +0000 Subject: [PATCH 186/514] make-release.py: Updated manpages for 4.6.0-rc1. --- doc/man/zcash-cli.1 | 6 +++--- doc/man/zcash-tx.1 | 6 +++--- doc/man/zcashd.1 | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 152d423b1c6..2eecd326f64 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "October 2021" "zcash-cli v4.5.1-1" "User Commands" +.TH ZCASH-CLI "1" "December 2021" "zcash-cli v4.6.0-rc1" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.5.1-1 +zcash-cli \- manual page for zcash-cli v4.6.0-rc1 .SH DESCRIPTION -Zcash RPC client version v4.5.1\-1 +Zcash RPC client version v4.6.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 07d0a5b9e26..9e395912fb8 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "October 2021" "zcash-tx v4.5.1-1" "User Commands" +.TH ZCASH-TX "1" "December 2021" "zcash-tx v4.6.0-rc1" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.5.1-1 +zcash-tx \- manual page for zcash-tx v4.6.0-rc1 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.5.1\-1 +Zcash zcash\-tx utility version v4.6.0\-rc1 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index d86cd12b850..b4d9e4a4419 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "October 2021" "zcashd v4.5.1-1" "User Commands" +.TH ZCASHD "1" "December 2021" "zcashd v4.6.0-rc1" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.5.1-1 +zcashd \- manual page for zcashd v4.6.0-rc1 .SH DESCRIPTION -Zcash Daemon version v4.5.1\-1 +Zcash Daemon version v4.6.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . From 9c4a1e7ad701670c7acfaa553671925c62b6e622 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 18 Dec 2021 04:25:50 +0000 Subject: [PATCH 187/514] make-release.py: Updated release notes and changelog for 4.6.0-rc1. --- contrib/debian/changelog | 6 + doc/release-notes/release-notes-4.6.0-rc1.md | 111 +++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 doc/release-notes/release-notes-4.6.0-rc1.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 244daae2180..6cac5c33a53 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.6.0~rc1) stable; urgency=medium + + * 4.6.0-rc1 release. + + -- Electric Coin Company Sat, 18 Dec 2021 04:25:50 +0000 + zcash (4.5.1+1) stable; urgency=medium * 4.5.1-1 release. diff --git a/doc/release-notes/release-notes-4.6.0-rc1.md b/doc/release-notes/release-notes-4.6.0-rc1.md new file mode 100644 index 00000000000..1bfd543d0f1 --- /dev/null +++ b/doc/release-notes/release-notes-4.6.0-rc1.md @@ -0,0 +1,111 @@ +Notable changes +=============== + +Wallet +------ + +From this release, newly-created wallets will save the chain name ("Zcash") and +network identifier (e.g. "main") to the `wallet.dat` file. This will enable the +`zcashd` node to check on subsequent starts that the `wallet.dat` file matches +the node's configuration. Existing wallets will start saving this information in +a later release. + +`libzcash_script` +----------------- + +Two new APIs have been added to this library (`zcash_script_legacy_sigop_count` +and `zcash_script_legacy_sigop_count_precomputed`), for counting the number of +signature operations in the transparent inputs and outputs of a transaction. +The presence of these APIs is indicated by a library API version of 2. + +Updated RPCs +------------ + +- Fixed an issue where `ERROR: spent index not enabled` would be logged + unnecessarily on nodes that have either insightexplorer or lightwalletd + configuration options enabled. + +- The `getmininginfo` RPC now omits `currentblockize` and `currentblocktx` + when a block was never assembled via RPC on this node during its current + process instantiation. (#5404) + +Changelog +========= + +Alex Wied (1): + Update support for FreeBSD + +Charlie O'Keefe (1): + Add buster to the list of suites used by gitian + +Dimitris Apostolou (1): + Fix typos + +Jack Grigg (22): + contrib: Update Debian copyright file to follow the v1 format + contrib: Add license information for libc++ and libevent + cargo update + tracing-subscriber 0.3 + Bump all postponed dependencies + depends: Update Rust to 1.56.1 + depends: Update Clang / libcxx to LLVM 13 + rust: Move `incremental_sinsemilla_tree_ffi` into crate root + CI: Add Pyflakes to lints workflow + cargo update + ed25519-zebra 3 + cargo update + cargo update + depends: Update Boost to 1.78.0 + depends Update Rust to 1.57.0 + qa: Postpone recent CCache releases + Revert "lint: Fix false positive" + rust: Remove misleading log message + Migrate to latest revisions of Zcash Rust crates + Update release notes + make-release.py: Versioning changes for 4.6.0-rc1. + make-release.py: Updated manpages for 4.6.0-rc1. + +Janito Vaqueiro Ferreira Filho (1): + Move `CurrentTxVersionInfo` into a new file + +Kris Nuttycombe (7): + Add BIP 44 coin type to persisted wallet state. + Persist network id string instead of bip44 coin type. + Add a check to test that wallet load fails if we're on the wrong network. + Remove unused `AddDestData` method. + Fix wallet-related wording in doc/reduce-traffic.md + Rename OrchardMerkleTree -> OrchardMerkleFrontier + Batch-verify Orchard transactions at the block level. + +Larry Ruane (6): + add TestSetIBD(bool) for testing + Disable IBD for all boost tests + better wallet network info error handling + test: automatically add missing nuparams + Don't log 'ERROR: spent index not enabled' + getblocktemplate: add NU5 commitments to new `defaultroots` section + +Marco Falke (1): + [rpc] mining: Omit uninitialized currentblockweight, currentblocktx + +Marshall Gaucher (1): + Update entrypoint.sh + +Sasha (1): + fix typo in docker run's volume argument + +sgmoore (1): + Update reduce-traffic.md - add one word + +Jack Grigg (1): + contrib: Add space between URL and period + +teor (7): + Add sigop count functions to zcash_script library + Increment the zcash_script API version + Remove an unused header + Use correct copyright header + Remove redundant variable + Explain UINT_MAX error return value + Explain how to get rid of a tiny duplicated function + From 4e928fa67ff9632746d530ddfb2ddcce7ceec83d Mon Sep 17 00:00:00 2001 From: Sasha <2592730+superbaud@users.noreply.github.com> Date: Mon, 20 Dec 2021 16:25:05 -0800 Subject: [PATCH 188/514] update hash for librustzcash --- .cargo/config.offline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config.offline b/.cargo/config.offline index 260b4c3ae83..073216a8747 100644 --- a/.cargo/config.offline +++ b/.cargo/config.offline @@ -18,7 +18,7 @@ replace-with = "vendored-sources" [source."https://github.com/zcash/librustzcash.git"] git = "https://github.com/zcash/librustzcash.git" -rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" +rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" replace-with = "vendored-sources" [source."https://github.com/zcash/orchard.git"] From 87101eeaedc3a058de342bc7244c1e463e322cc2 Mon Sep 17 00:00:00 2001 From: Sasha <2592730+superbaud@users.noreply.github.com> Date: Mon, 20 Dec 2021 17:11:25 -0800 Subject: [PATCH 189/514] only librustzcash in config.offline --- .cargo/config.offline | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.cargo/config.offline b/.cargo/config.offline index 073216a8747..1f8659ab357 100644 --- a/.cargo/config.offline +++ b/.cargo/config.offline @@ -1,30 +1,10 @@ [source.crates-io] replace-with = "vendored-sources" -[source."https://github.com/ZcashFoundation/ed25519-zebra.git"] -git = "https://github.com/ZcashFoundation/ed25519-zebra.git" -rev = "d3512400227a362d08367088ffaa9bd4142a69c7" -replace-with = "vendored-sources" - -[source."https://github.com/str4d/redjubjub.git"] -git = "https://github.com/str4d/redjubjub.git" -rev = "416a6a8ebf8bd42c114c938883016c04f338de72" -replace-with = "vendored-sources" - -[source."https://github.com/zcash/incrementalmerkletree"] -git = "https://github.com/zcash/incrementalmerkletree" -rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" -replace-with = "vendored-sources" - [source."https://github.com/zcash/librustzcash.git"] git = "https://github.com/zcash/librustzcash.git" rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" replace-with = "vendored-sources" -[source."https://github.com/zcash/orchard.git"] -git = "https://github.com/zcash/orchard.git" -rev = "2c8241f25b943aa05203eacf9905db117c69bd29" -replace-with = "vendored-sources" - [source.vendored-sources] # The directory for this source is set to RUST_VENDORED_SOURCES by src/Makefile.am From 04c292a3792cf2cf46f1145612e8558843b8e653 Mon Sep 17 00:00:00 2001 From: Charlie O'Keefe Date: Tue, 21 Dec 2021 09:17:51 -0700 Subject: [PATCH 190/514] Add libtinfo5 to gitian packages list libtinfo5 is a build dependency of zcashd https://zcash.readthedocs.io/en/latest/rtd_pages/Debian-Ubuntu-build.html --- contrib/gitian-descriptors/gitian-linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index c7507bc89d8..886567e9f32 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -18,6 +18,7 @@ packages: - "g++-multilib" - "git-core" - "libc6-dev" +- "libtinfo5" - "libtool" - "libxml2" - "m4" From 8c0177a5071920b90e5f4c05be35becec1d2cff2 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Sun, 19 Dec 2021 13:49:14 -0700 Subject: [PATCH 191/514] test: fix bugs in test framework --- qa/rpc-tests/test_framework/mininode.py | 7 ++++++- qa/rpc-tests/test_framework/zip244.py | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 1a6b1083718..11635f838d1 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -660,7 +660,7 @@ def deserialize(self, f): self.encCiphertext = f.read(580) self.outCiphertext = f.read(80) self.zkproof = Groth16Proof() - self.zkproof.deserialize() + self.zkproof.deserialize(f) def serialize(self): r = b"" @@ -971,6 +971,8 @@ def __init__(self, tx=None): self.nLockTime = 0 self.nExpiryHeight = 0 self.valueBalance = 0 + self.saplingBundle = SaplingBundle() + self.orchardBundle = OrchardBundle() self.shieldedSpends = [] self.shieldedOutputs = [] self.vJoinSplit = [] @@ -988,6 +990,8 @@ def __init__(self, tx=None): self.nLockTime = tx.nLockTime self.nExpiryHeight = tx.nExpiryHeight self.valueBalance = tx.valueBalance + self.saplingBundle = copy.deepcopy(tx.saplingBundle) + self.orchardBundle = copy.deepcopy(tx.orchardBundle) self.shieldedSpends = copy.deepcopy(tx.shieldedSpends) self.shieldedOutputs = copy.deepcopy(tx.shieldedOutputs) self.vJoinSplit = copy.deepcopy(tx.vJoinSplit) @@ -1075,6 +1079,7 @@ def serialize(self): # Common transaction fields r += struct.pack(" 0: digest.update(sapling_spends_digest(saplingBundle)) digest.update(sapling_outputs_digest(saplingBundle)) - digest.update(struct.pack(' Date: Sun, 19 Dec 2021 13:49:38 -0700 Subject: [PATCH 192/514] test: Use result of getblocktemplate to submitblock This will ensure that miners can use the values returned by getblocktemplate (in particular, the block commitment hash) to submit a valid block using the submitblock RPC. --- qa/rpc-tests/getblocktemplate.py | 88 +++++++++++++++++++++----------- src/rpc/mining.cpp | 8 ++- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index e7a8633ee03..fff46296181 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -3,6 +3,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . +from io import BytesIO +import codecs from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, @@ -10,13 +12,24 @@ CANOPY_BRANCH_ID, HEARTWOOD_BRANCH_ID, NU5_BRANCH_ID, + get_coinbase_address, + hex_str_to_bytes, nuparams, start_nodes, + wait_and_assert_operationid_status, ) +from test_framework.mininode import ( + CTransaction, +) +from test_framework.blocktools import( + create_block +) +from decimal import Decimal class GetBlockTemplateTest(BitcoinTestFramework): ''' - Test getblocktemplate. + Test getblocktemplate, ensure that a block created from its result + can be submitted and accepted. ''' def __init__(self): @@ -25,7 +38,8 @@ def __init__(self): self.setup_clean_chain = True def setup_network(self, split=False): - args = [nuparams(BLOSSOM_BRANCH_ID, 1), + args = [ + nuparams(BLOSSOM_BRANCH_ID, 1), nuparams(HEARTWOOD_BRANCH_ID, 1), nuparams(CANOPY_BRANCH_ID, 1), nuparams(NU5_BRANCH_ID, 1), @@ -36,40 +50,56 @@ def setup_network(self, split=False): def run_test(self): node = self.nodes[0] - node.generate(1) # Mine a block to leave initial block download + print("Generating blocks") + node.generate(110) + + print("Add transactions to the mempool so they will be in the template") + # This part of the test should be improved, submit some V4 transactions + # and varying combinations of shielded and transparent + for _ in range(5): + # submit a tx with a shielded output + taddr0 = get_coinbase_address(node) + zaddr = node.z_getnewaddress('sapling') + recipients = [{"address": zaddr, "amount": Decimal('0.1')}] + myopid = node.z_sendmany(taddr0, recipients, 1, 0) + wait_and_assert_operationid_status(node, myopid) + + # submit a tx with a transparent output + outputs = {node.getnewaddress():0.2} + node.sendmany('', outputs) - # Test 1: Default to coinbasetxn - tmpl = node.getblocktemplate() - assert('coinbasetxn' in tmpl) - assert('coinbasevalue' not in tmpl) + print("Getting block template") + gbt = node.getblocktemplate() - # Test 2: Get coinbasetxn if requested - tmpl = node.getblocktemplate({'capabilities': ['coinbasetxn']}) - assert('coinbasetxn' in tmpl) - assert('coinbasevalue' not in tmpl) + prevhash = int(gbt['previousblockhash'], 16) + blockcommitmentshash = int(gbt['defaultroots']['blockcommitmentshash'], 16) + nTime = gbt['mintime'] + nBits = int(gbt['bits'], 16) - # Test 3: coinbasevalue not supported if requested - tmpl = node.getblocktemplate({'capabilities': ['coinbasevalue']}) - assert('coinbasetxn' in tmpl) - assert('coinbasevalue' not in tmpl) + f = BytesIO(hex_str_to_bytes(gbt['coinbasetxn']['data'])) + coinbase = CTransaction() + coinbase.deserialize(f) - # Test 4: coinbasevalue not supported if both requested - tmpl = node.getblocktemplate({'capabilities': ['coinbasetxn', 'coinbasevalue']}) - assert('coinbasetxn' in tmpl) - assert('coinbasevalue' not in tmpl) + print("Creating_block from template (simulating a miner)") + block = create_block(prevhash, coinbase, nTime, nBits, blockcommitmentshash) - # Test 5: General checks - tmpl = node.getblocktemplate() - assert_equal(16, len(tmpl['noncerange'])) - # should be proposing height 2, since current tip is height 1 - assert_equal(2, tmpl['height']) + # copy the non-coinbase transactions from the block template to the block + for gbt_tx in gbt['transactions']: + f = BytesIO(hex_str_to_bytes(gbt_tx['data'])) + tx = CTransaction() + tx.deserialize(f) + block.vtx.append(tx) + block.hashMerkleRoot = int(gbt['defaultroots']['merkleroot'], 16) + assert_equal(block.hashMerkleRoot, block.calc_merkle_root(), "merkleroot") + assert_equal(len(block.vtx), len(gbt['transactions']) + 1, "number of transactions") + assert_equal(block.hashPrevBlock, int(gbt['previousblockhash'], 16), "prevhash") + block.solve() + block = block.serialize() + block = codecs.encode(block, 'hex_codec') - # Test 6: coinbasetxn checks - assert(tmpl['coinbasetxn']['required']) + print("Submitting block") + node.submitblock(block) - # Test 7: blockcommitmentshash checks - assert('blockcommitmentshash' in tmpl) - assert('00' * 32 != tmpl['finalsaplingroothash']) if __name__ == '__main__': GetBlockTemplateTest().main() diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 36da7d44392..48e0d69326e 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -449,9 +449,15 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "{\n" " \"version\" : n, (numeric) The block version\n" " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" - " \"blockcommitmentshash\" : \"xxxx\", (string) The hash of the block commitments field in the block header\n" + " \"blockcommitmentshash\" : \"xxxx\", (string) (DEPRECATED) The hash of the block commitments field in the block header\n" " \"lightclientroothash\" : \"xxxx\", (string) (DEPRECATED) The hash of the light client root field in the block header\n" " \"finalsaplingroothash\" : \"xxxx\", (string) (DEPRECATED) The hash of the light client root field in the block header\n" + " \"defaultroots\" : { (json object) root hashes that need to be recomputed if the transaction set is modified\n" + " \"merkleroot\" : \"xxxx\" (string) The hash of the transactions in the block header\n" + " \"authdataroot\" : \"xxxx\" (string) The hash of the authorizing data merkel tree\n" + " \"chainhistoryroot\" : \"xxxx\" (string) The hash of the chain history\n" + " \"blockcommitmentshash\" : \"xxxx\" (string) The hash of the block commitments field in the block header\n" + " }\n" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" From ef068c51a9849779b5355ac1469a05c7196551a8 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 30 Nov 2021 08:30:50 -0700 Subject: [PATCH 193/514] Store ufvks to the wallet database. --- src/keystore.cpp | 28 +++++++++++++++ src/keystore.h | 15 ++++++++ src/wallet/wallet.cpp | 78 ++++++++++++++++++++++++++++++++--------- src/wallet/wallet.h | 29 ++++++++++++--- src/wallet/walletdb.cpp | 16 +++++++++ src/wallet/walletdb.h | 6 ++++ 6 files changed, 151 insertions(+), 21 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index e17ac4f0ca3..a4779a3dd54 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -287,3 +287,31 @@ bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymen GetSaplingFullViewingKey(ivk, extfvk) && GetSaplingSpendingKey(extfvk, extskOut); } + +// +// Unified Keys +// + +bool CBasicKeyStore::AddUnifiedFullViewingKey( + const libzcash::UFVKId& keyId, + const libzcash::ZcashdUnifiedFullViewingKey &ufvk) +{ + LOCK(cs_KeyStore); + + auto tKey = ufvk.GetTransparentKey(); + if (tKey.has_value()) { + // FIXME: this is probably not the right thing; we need to actually track + // addresses, not the id at the FVK level? + mapTKeyUnified.insert(std::make_pair(tKey.value().GetPubKey().GetID(), keyId)); + } + + auto saplingKey = ufvk.GetSaplingKey(); + if (saplingKey.has_value()) { + auto ivk = saplingKey.value().fvk.in_viewing_key(); + mapSaplingKeyUnified.insert(std::make_pair(ivk, keyId)); + } + + mapUnifiedFullViewingKeys.insert({keyId, ufvk}); + + return true; +} diff --git a/src/keystore.h b/src/keystore.h index c2e40b399e4..cb1114665e1 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -12,6 +12,7 @@ #include "script/standard.h" #include "sync.h" #include "zcash/address/mnemonic.h" +#include "zcash/address/unified.h" #include "zcash/Address.hpp" #include "zcash/NoteEncryption.hpp" @@ -100,6 +101,12 @@ class CKeyStore virtual bool GetSproutViewingKey( const libzcash::SproutPaymentAddress &address, libzcash::SproutViewingKey& vkOut) const =0; + + //! Unified addresses and keys + virtual bool AddUnifiedFullViewingKey( + const libzcash::UFVKId& keyId, + const libzcash::ZcashdUnifiedFullViewingKey &ufvk + ) = 0; }; typedef std::map KeyMap; @@ -142,6 +149,10 @@ class CBasicKeyStore : public CKeyStore SaplingFullViewingKeyMap mapSaplingFullViewingKeys; SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys; + // Unified key support + std::map mapTKeyUnified; + std::map mapSaplingKeyUnified; + std::map mapUnifiedFullViewingKeys; public: bool SetMnemonicSeed(const MnemonicSeed& seed); bool HaveMnemonicSeed() const; @@ -314,6 +325,10 @@ class CBasicKeyStore : public CKeyStore virtual bool GetSproutViewingKey( const libzcash::SproutPaymentAddress &address, libzcash::SproutViewingKey& vkOut) const; + + virtual bool AddUnifiedFullViewingKey( + const libzcash::UFVKId& keyId, + const libzcash::ZcashdUnifiedFullViewingKey &ufvk); }; typedef std::vector > CKeyingMaterial; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 11012f41352..e3d4d7c7295 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -195,7 +195,9 @@ bool CWallet::AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &sk) return true; } -bool CWallet::AddSaplingFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk) +bool CWallet::AddSaplingFullViewingKey( + const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::optional& ufvkId) { AssertLockHeld(cs_wallet); @@ -287,7 +289,7 @@ CPubKey CWallet::GenerateNewKey() // if we did not successfully generate a key, try again. } while (!extKey.has_value()); - auto pubkey = AddKey(seed.Fingerprint(), extKey.value()); + auto pubkey = AddTransparentSecretKey(seed.Fingerprint(), extKey.value()); // Update the persisted chain information if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) { @@ -297,7 +299,10 @@ CPubKey CWallet::GenerateNewKey() return pubkey; } -CPubKey CWallet::AddKey(const uint256& seedFingerprint, const std::pair& extSecret) +CPubKey CWallet::AddTransparentSecretKey( + const uint256& seedFingerprint, + const std::pair& extSecret, + const std::optional& ufvkId) { CKey secret = extSecret.first.key; CPubKey pubkey = secret.GetPubKey(); @@ -311,13 +316,16 @@ CPubKey CWallet::AddKey(const uint256& seedFingerprint, const std::pair& ufvkId) { AssertLockHeld(cs_wallet); // mapKeyMetadata if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) @@ -334,11 +342,13 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) if (!fFileBacked) return true; + if (!IsCrypted()) { return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } + return true; } @@ -456,29 +466,42 @@ std::optional using TxSpendMap = std::multimap; + /** * Used to keep track of spent outpoints, and * detect and report conflicts (double-spends or @@ -711,6 +716,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ typedef TxSpendMap TxSpends; TxSpends mapTxSpends; + /** * Used to keep track of spent Notes, and * detect and report conflicts (double-spends). @@ -804,7 +810,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree); /* Add an extended secret key to the wallet. Internal use only. */ - CPubKey AddKey(const uint256& seedFingerprint, const std::pair& extSecret); + CPubKey AddTransparentSecretKey( + const uint256& seedFingerprint, + const std::pair& extSecret, + const std::optional& ufvkId = std::nullopt); protected: bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); @@ -831,8 +840,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::set setKeyPool; std::map mapKeyMetadata; + std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; + std::map, libzcash::ZcashdUnifiedKeyMetadata> mapUnifiedKeyMetadata; typedef std::map MasterKeyMap; @@ -999,7 +1010,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ CPubKey GenerateNewKey(); //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool AddKeyPubKey( + const CKey& key, + const CPubKey &pubkey, + const std::optional& ufvkId = std::nullopt); //! Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } //! Load metadata (used by LoadWallet) @@ -1078,7 +1092,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! full viewing key to disk. Inside CCryptoKeyStore and CBasicKeyStore, //! CBasicKeyStore::AddSaplingFullViewingKey is called directly when adding a //! full viewing key to the keystore, to avoid this override. - bool AddSaplingFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk); + bool AddSaplingFullViewingKey( + const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::optional& ufvkId = std::nullopt); bool AddSaplingIncomingViewingKey( const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingPaymentAddress &addr); @@ -1115,11 +1131,16 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); //! Add the specified unified spending key to the wallet with the provided key - //! metadata. + //! metadata. -- TODO, this should probably not be part of the public API? bool AddUnifiedSpendingKey( const libzcash::ZcashdUnifiedSpendingKey& sk, const libzcash::ZcashdUnifiedKeyMetadata& metadata); + bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); + bool LoadUnifiedFullViewingKey( + const libzcash::UFVKId& keyId, + const libzcash::UnifiedFullViewingKey &key); + void LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta); /** diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 8d32d9c34c4..852c5043807 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -217,6 +217,22 @@ bool CWalletDB::EraseSaplingExtendedFullViewingKey( return Erase(std::make_pair(std::string("sapextfvk"), extfvk)); } +// +// Unified address & key storage +// + +bool CWalletDB::WriteUnifiedFullViewingKey( + const libzcash::UFVKId& ufvkId, + const libzcash::UnifiedFullViewingKey& ufvk) +{ + nWalletDBUpdateCounter++; + return Write(std::make_pair(std::string("unifiedfvk"), ufvkId), ufvk.Encode(Params())); +} + +// +// +// + bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdateCounter++; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index f87fcb44b0a..2eb85252643 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -249,6 +249,12 @@ class CWalletDB : public CDB bool WriteSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk); bool EraseSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk); + /// Unified key support. + + bool WriteUnifiedFullViewingKey( + const libzcash::UFVKId& ufvkId, + const libzcash::UnifiedFullViewingKey& ufvk); + static void IncrementUpdateCounter(); static unsigned int GetUpdateCounter(); private: From e56f252a861a87c09f99f01c399d62846f56d025 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 8 Dec 2021 18:13:28 -0700 Subject: [PATCH 194/514] Add unified address tracking to KeyStore --- src/keystore.cpp | 30 +++++++++++++++++++++++------- src/keystore.h | 22 +++++++++++++++++++++- src/zcash/Address.cpp | 31 +++++++++++++++++++++++++++++++ src/zcash/Address.hpp | 6 ++++++ 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index a4779a3dd54..4230fd7cb2d 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -298,13 +298,6 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( { LOCK(cs_KeyStore); - auto tKey = ufvk.GetTransparentKey(); - if (tKey.has_value()) { - // FIXME: this is probably not the right thing; we need to actually track - // addresses, not the id at the FVK level? - mapTKeyUnified.insert(std::make_pair(tKey.value().GetPubKey().GetID(), keyId)); - } - auto saplingKey = ufvk.GetSaplingKey(); if (saplingKey.has_value()) { auto ivk = saplingKey.value().fvk.in_viewing_key(); @@ -315,3 +308,26 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( return true; } + +bool CBasicKeyStore::AddUnifiedAddress( + const libzcash::UFVKId& keyId, + const libzcash::UnifiedAddress& ua) +{ + LOCK(cs_KeyStore); + + // It's only necessary to add p2pkh and p2sh components of + // the UA; all other lookups of the associated UFVK will be + // made via the protocol-specific viewing key that is used + // to trial-decrypt a transaction. + + auto p2pkhReceiver = ua.GetP2PKHReceiver(); + if (p2pkhReceiver.has_value()) { + mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), keyId)); + } + + auto p2shReceiver = ua.GetP2SHReceiver(); + if (p2shReceiver.has_value()) { + mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), keyId)); + } +} + diff --git a/src/keystore.h b/src/keystore.h index cb1114665e1..dd731195a23 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -107,6 +107,11 @@ class CKeyStore const libzcash::UFVKId& keyId, const libzcash::ZcashdUnifiedFullViewingKey &ufvk ) = 0; + + virtual bool AddUnifiedAddress( + const libzcash::UFVKId& keyId, + const libzcash::UnifiedAddress &ua + ) = 0; }; typedef std::map KeyMap; @@ -128,6 +133,12 @@ typedef std::map< // Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses. typedef std::map SaplingIncomingViewingKeyMap; +struct UnifiedAddressMetadata { + libzcash::UFVKId keyId; + libzcash::diversifier_index_t j; + std::vector receiverTypes; +}; + /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore { @@ -150,7 +161,8 @@ class CBasicKeyStore : public CKeyStore SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys; // Unified key support - std::map mapTKeyUnified; + std::map mapP2PKHUnified; + std::map mapP2SHUnified; std::map mapSaplingKeyUnified; std::map mapUnifiedFullViewingKeys; public: @@ -329,6 +341,14 @@ class CBasicKeyStore : public CKeyStore virtual bool AddUnifiedFullViewingKey( const libzcash::UFVKId& keyId, const libzcash::ZcashdUnifiedFullViewingKey &ufvk); + + /** + * Add the transparent component of the unified address, if any, + * to the keystore to make it possible to identify the + */ + virtual bool AddUnifiedAddress( + const libzcash::UFVKId& keyId, + const libzcash::UnifiedAddress &ua); }; typedef std::vector > CKeyingMaterial; diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index af2a1d873f5..ee6f30645e4 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -43,6 +43,37 @@ bool UnifiedAddress::AddReceiver(Receiver receiver) { return true; } +std::optional UnifiedAddress::GetP2PKHReceiver() const { + for (const auto& r : receivers) { + if (std::holds_alternative(r)) { + return std::get(r); + } + } + + return std::nullopt; +} + +std::optional UnifiedAddress::GetP2SHReceiver() const { + for (const auto& r : receivers) { + if (std::holds_alternative(r)) { + return std::get(r); + } + } + + return std::nullopt; +} + +std::optional UnifiedAddress::GetSaplingReceiver() const { + for (const auto& r : receivers) { + if (std::holds_alternative(r)) { + return std::get(r); + } + } + + return std::nullopt; +} + + std::pair AddressInfoFromSpendingKey::operator()(const SproutSpendingKey &sk) const { return std::make_pair("sprout", sk.address()); } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 99b9e4a436f..88f7e2f8864 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -123,6 +123,12 @@ class UnifiedAddress { return receivers.size(); } + std::optional GetP2PKHReceiver() const; + + std::optional GetP2SHReceiver() const; + + std::optional GetSaplingReceiver() const; + friend inline bool operator==(const UnifiedAddress& a, const UnifiedAddress& b) { return a.receivers == b.receivers; } From d869d7f4a30ba227ea45e9a841849d2634947575 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 9 Dec 2021 12:05:59 -0700 Subject: [PATCH 195/514] Load unified full viewing keys from the walletdb. --- src/wallet/wallet.cpp | 98 ++++++++++++++++++++--------------------- src/wallet/wallet.h | 10 +---- src/wallet/walletdb.cpp | 24 ++++++++++ 3 files changed, 72 insertions(+), 60 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e3d4d7c7295..851ad6a59ea 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -430,6 +430,7 @@ std::pair CWallet::GenerateN throw std::runtime_error( "CWallet::GenerateNewUnifiedSpendingKey(): Wallet is missing mnemonic seed metadata."); } + CHDChain& hdChain = mnemonicHDChain.value(); while (true) { auto usk = GenerateUnifiedSpendingKeyForAccount(hdChain.GetAccountCounter()); @@ -449,6 +450,8 @@ std::pair CWallet::GenerateN std::optional> CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { + AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata + auto seed = GetMnemonicSeed(); if (!seed.has_value()) { throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed."); @@ -456,60 +459,54 @@ std::optional> GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); - //! Add the specified unified spending key to the wallet with the provided key - //! metadata. -- TODO, this should probably not be part of the public API? - bool AddUnifiedSpendingKey( - const libzcash::ZcashdUnifiedSpendingKey& sk, - const libzcash::ZcashdUnifiedKeyMetadata& metadata); - bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - bool LoadUnifiedFullViewingKey( - const libzcash::UFVKId& keyId, - const libzcash::UnifiedFullViewingKey &key); + bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key); void LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 852c5043807..041c9445d28 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -649,6 +649,30 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> pwallet->vchDefaultKey; } + else if (strType == "unifiedfvk") + { + libzcash::UFVKId fp; + ssKey >> fp; + + std::string ufvkenc; + ssValue >> ufvkenc; + + auto ufvkopt = libzcash::UnifiedFullViewingKey::Decode(ufvkenc, Params()); + if (ufvkopt.has_value()) { + auto ufvk = ufvkopt.value(); + if (fp != ufvk.GetKeyID(Params())) { + strErr = "Error reading wallet database: key fingerprint did not match decoded key"; + return false; + } + if (!pwallet->LoadUnifiedFullViewingKey(ufvk)) { + strErr = "Error reading wallet database: LoadUnifiedFullViewingKey failed."; + return false; + } + } else { + strErr = "Error reading wallet database: failed to decode unified full viewing key."; + return false; + } + } else if (strType == "pool") { int64_t nIndex; From ca20cf512ca869412f1916be770fc53ea25e22a5 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 9 Dec 2021 14:20:51 -0700 Subject: [PATCH 196/514] Add key metadata to walletdb. --- src/wallet/wallet.cpp | 37 ++++++---- src/wallet/wallet.h | 9 +-- src/wallet/walletdb.cpp | 17 ++++- src/wallet/walletdb.h | 133 +++++++++++++++++++++++++++++++++- src/zcash/Address.hpp | 3 + src/zcash/address/unified.cpp | 25 +------ src/zcash/address/unified.h | 85 +--------------------- 7 files changed, 175 insertions(+), 134 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 851ad6a59ea..a9f1eb9f105 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -423,7 +423,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi return false; } -std::pair CWallet::GenerateNewUnifiedSpendingKey() { +std::pair CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); if (!mnemonicHDChain.has_value()) { @@ -448,7 +448,7 @@ std::pair CWallet::GenerateN } } -std::optional> +std::optional> CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata @@ -459,8 +459,12 @@ std::optional mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; - - std::map, libzcash::ZcashdUnifiedKeyMetadata> mapUnifiedKeyMetadata; + std::map mapUnifiedKeyMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1122,18 +1121,18 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Generate the unified spending key from the wallet's mnemonic seed //! for the next unused account identifier. - std::pair + std::pair GenerateNewUnifiedSpendingKey(); //! Generate the next available unified spending key from the wallet's //! mnemonic seed. - std::optional> + std::optional> GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key); - void LoadUnifiedKeyMetadata(const libzcash::ZcashdUnifiedKeyMetadata &meta); + void LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &meta); /** * Increment the next transaction order id diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 041c9445d28..6b0660810d1 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -221,11 +221,17 @@ bool CWalletDB::EraseSaplingExtendedFullViewingKey( // Unified address & key storage // -bool CWalletDB::WriteUnifiedFullViewingKey( - const libzcash::UFVKId& ufvkId, - const libzcash::UnifiedFullViewingKey& ufvk) +bool CWalletDB::WriteUnifiedSpendingKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata& keymeta) { nWalletDBUpdateCounter++; + auto ufvkId = keymeta.GetKeyID(); + return Write(std::make_pair(std::string("unifiedskmeta"), ufvkId), keymeta); +} + +bool CWalletDB::WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk) +{ + nWalletDBUpdateCounter++; + auto ufvkId = ufvk.GetKeyID(Params()); return Write(std::make_pair(std::string("unifiedfvk"), ufvkId), ufvk.Encode(Params())); } @@ -673,6 +679,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return false; } } + else if (strType == "unifiedskmeta") + { + auto keymeta = ZcashdUnifiedSpendingKeyMetadata::Read(ssValue); + pwallet->LoadUnifiedKeyMetadata(keymeta); + } else if (strType == "pool") { int64_t nIndex; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2eb85252643..124a78f736c 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -172,6 +172,134 @@ class CKeyMetadata } }; +class ZcashdUnifiedSpendingKeyMetadata { +private: + libzcash::SeedFingerprint seedFp; + uint32_t bip44CoinType; + libzcash::AccountId accountId; + libzcash::UFVKId ufvkId; + + ZcashdUnifiedSpendingKeyMetadata() {} +public: + ZcashdUnifiedSpendingKeyMetadata( + libzcash::SeedFingerprint seedFp, + uint32_t bip44CoinType, + libzcash::AccountId accountId, + libzcash::UFVKId ufvkId): + seedFp(seedFp), bip44CoinType(bip44CoinType), accountId(accountId), ufvkId(ufvkId) {} + + /** Returns the fingerprint of the HD seed used to generate this key. */ + const libzcash::SeedFingerprint& GetSeedFingerprint() const { + return seedFp; + } + /** Returns the ZIP 32 account id for which this key was generated. */ + uint32_t GetBip44CoinType() const { + return bip44CoinType; + } + /** Returns the ZIP 32 account id for which this key was generated. */ + libzcash::AccountId GetAccountId() const { + return accountId; + } + /** Returns the fingerprint of the ufvk this key was generated. */ + const libzcash::UFVKId& GetKeyID() const { + return ufvkId; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(seedFp); + READWRITE(bip44CoinType); + READWRITE(accountId); + READWRITE(ufvkId); + } + + template + static ZcashdUnifiedSpendingKeyMetadata Read(Stream& stream) { + ZcashdUnifiedSpendingKeyMetadata meta; + stream >> meta; + return meta; + } +}; + +class ZcashdUnifiedAddressMetadata; + +// Serialization wrapper for reading and writing ReceiverType +// in CompactSize format. +class ReceiverTypeSer { +private: + libzcash::ReceiverType t; + + friend class ZcashdUnifiedAddressMetadata; +public: + ReceiverTypeSer() {} // for serialization only + ReceiverTypeSer(libzcash::ReceiverType t): t(t) {} + + template + void Serialize(Stream &s) const { + WriteCompactSize(s, (uint64_t) t); + } + + template + void Unserialize(Stream& s) { + t = (libzcash::ReceiverType) ReadCompactSize(s); + } +}; + +class ZcashdUnifiedAddressMetadata { +private: + libzcash::UFVKId ufvkId; + libzcash::diversifier_index_t diversifierIndex; + std::vector receiverTypes; + + ZcashdUnifiedAddressMetadata() {} +public: + ZcashdUnifiedAddressMetadata( + libzcash::UFVKId ufvkId, + libzcash::diversifier_index_t diversifierIndex, + std::vector receiverTypes): + ufvkId(ufvkId), diversifierIndex(diversifierIndex), receiverTypes(receiverTypes) {} + + libzcash::UFVKId GetKeyID() const { + return ufvkId; + } + libzcash::diversifier_index_t GetDiversifierIndex() const { + return diversifierIndex; + } + const std::vector& GetReceiverTypes() const { + return receiverTypes; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(ufvkId); + READWRITE(diversifierIndex); + if (ser_action.ForRead()) { + std::vector serReceiverTypes; + READWRITE(serReceiverTypes); + receiverTypes.clear(); + for (ReceiverTypeSer r : serReceiverTypes) + receiverTypes.push_back(r.t); + } else { + std::vector serReceiverTypes; + for (libzcash::ReceiverType r : receiverTypes) + serReceiverTypes.push_back(ReceiverTypeSer(r)); + READWRITE(serReceiverTypes); + } + } + + template + static ZcashdUnifiedAddressMetadata Read(Stream& stream) { + ZcashdUnifiedAddressMetadata meta; + stream >> meta; + return meta; + } +}; + + /** Access to the wallet database */ class CWalletDB : public CDB { @@ -251,9 +379,8 @@ class CWalletDB : public CDB /// Unified key support. - bool WriteUnifiedFullViewingKey( - const libzcash::UFVKId& ufvkId, - const libzcash::UnifiedFullViewingKey& ufvk); + bool WriteUnifiedSpendingKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata& keymeta); + bool WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk); static void IncrementUpdateCounter(); static unsigned int GetUpdateCounter(); diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 88f7e2f8864..580e01bc26c 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -15,6 +15,9 @@ #include #include +const unsigned char ZCASH_UFVK_ID_PERSONAL[BLAKE2bPersonalBytes] = + {'Z', 'c', 'a', 's', 'h', '_', 'U', 'F', 'V', 'K', '_', 'I', 'd', '_', 'F', 'P'}; + namespace libzcash { /** Protocol addresses that can receive funds in a transaction. */ diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index ed6bc2445af..3d328467e3a 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -12,23 +12,7 @@ using namespace libzcash; // Unified Keys // -std::optional ZcashdUnifiedKeyMetadata::TransparentKeyPath() const { - if(std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::P2PKH) != receiverTypes.end()) { - return libzcash::Bip44TransparentAccountKeyPath(bip44CoinType, accountId); - } else { - return std::nullopt; - } -} - -std::optional ZcashdUnifiedKeyMetadata::SaplingKeyPath() const { - if(std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::Sapling) != receiverTypes.end()) { - return libzcash::Bip44TransparentAccountKeyPath(bip44CoinType, accountId); - } else { - return std::nullopt; - } -} - -std::optional> ZcashdUnifiedSpendingKey::ForAccount( +std::optional ZcashdUnifiedSpendingKey::ForAccount( const HDSeed& seed, uint32_t bip44CoinType, AccountId accountId) { @@ -41,12 +25,7 @@ std::optional> Zca auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); usk.saplingKey = saplingKey.first; - ZcashdUnifiedKeyMetadata keyMetadata( - seed.Fingerprint(), - bip44CoinType, accountId, - { ReceiverType::P2PKH, ReceiverType::Sapling }); - - return std::make_pair(usk, keyMetadata); + return usk; } ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index c86f83cf9fe..c8cce33cff4 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -8,9 +8,6 @@ #include "zip32.h" #include "bip44.h" -const unsigned char ZCASH_UFVK_ID_PERSONAL[BLAKE2bPersonalBytes] = - {'Z', 'c', 'a', 's', 'h', '_', 'U', 'F', 'V', 'K', '_', 'I', 'd', '_', 'F', 'P'}; - namespace libzcash { enum class ReceiverType: uint32_t { @@ -20,92 +17,12 @@ enum class ReceiverType: uint32_t { Orchard = 0x03 }; -class ZcashdUnifiedKeyMetadata; - -// Serialization wrapper for reading and writing ReceiverType -// in CompactSize format. -class ReceiverTypeSer { -private: - ReceiverType t; - - friend class ZcashdUnifiedKeyMetadata; -public: - ReceiverTypeSer() {} // for serialization only - ReceiverTypeSer(ReceiverType t): t(t) {} - - template - void Serialize(Stream &s) const { - WriteCompactSize(s, (uint64_t) t); - } - - template - void Unserialize(Stream& s) { - t = (ReceiverType) ReadCompactSize(s); - } -}; - class ZcashdUnifiedSpendingKey; -class ZcashdUnifiedFullViewingKey; - // prototypes for the classes handling ZIP-316 encoding (in Address.hpp) class UnifiedAddress; class UnifiedFullViewingKey; -class ZcashdUnifiedKeyMetadata { -private: - SeedFingerprint seedFp; - uint32_t bip44CoinType; - libzcash::AccountId accountId; - std::vector receiverTypes; - - ZcashdUnifiedKeyMetadata() {} -public: - ZcashdUnifiedKeyMetadata( - SeedFingerprint seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, std::vector receiverTypes): - seedFp(seedFp), bip44CoinType(bip44CoinType), accountId(accountId), receiverTypes(receiverTypes) {} - - const SeedFingerprint& GetSeedFingerprint() const { - return seedFp; - } - libzcash::AccountId GetAccountId() const { - return accountId; - } - const std::vector& GetReceiverTypes() const { - return receiverTypes; - } - std::optional TransparentKeyPath() const; - std::optional SaplingKeyPath() const; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(seedFp); - READWRITE(bip44CoinType); - READWRITE(accountId); - if (ser_action.ForRead()) { - std::vector serReceiverTypes; - READWRITE(serReceiverTypes); - receiverTypes.clear(); - for (ReceiverTypeSer r : serReceiverTypes) - receiverTypes.push_back(r.t); - } else { - std::vector serReceiverTypes; - for (ReceiverType r : receiverTypes) - serReceiverTypes.push_back(ReceiverTypeSer(r)); - READWRITE(serReceiverTypes); - } - } - - template - static ZcashdUnifiedKeyMetadata Read(Stream& stream) { - ZcashdUnifiedKeyMetadata meta; - stream >> meta; - return meta; - } -}; - /** * An internal-only type for unified full viewing keys that represents only the * set of receiver types that are supported by zcashd. This type does not @@ -149,7 +66,7 @@ class ZcashdUnifiedSpendingKey { ZcashdUnifiedSpendingKey() {} public: - static std::optional> ForAccount( + static std::optional ForAccount( const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); From b29ca10b8d96a0b9ffbaafdbf810b652af9f0b9b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 9 Dec 2021 18:34:04 -0700 Subject: [PATCH 197/514] Add unified address generation. Generate unified addresses from UFVKs, and add the associated metadata to the wallet database. --- src/keystore.cpp | 12 +++- src/keystore.h | 10 +++- src/wallet/wallet.cpp | 110 ++++++++++++++++++++++++++++++++-- src/wallet/wallet.h | 30 +++++++++- src/wallet/walletdb.cpp | 12 ++++ src/wallet/walletdb.h | 9 +-- src/zcash/address/unified.cpp | 42 ++++++++++--- src/zcash/address/unified.h | 34 ++++++++++- 8 files changed, 233 insertions(+), 26 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index 4230fd7cb2d..54fdb2a92ce 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -309,7 +309,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( return true; } -bool CBasicKeyStore::AddUnifiedAddress( +void CBasicKeyStore::AddUnifiedAddress( const libzcash::UFVKId& keyId, const libzcash::UnifiedAddress& ua) { @@ -331,3 +331,13 @@ bool CBasicKeyStore::AddUnifiedAddress( } } +std::optional CBasicKeyStore::GetUnifiedFullViewingKey( + const libzcash::UFVKId& keyId) +{ + auto mi = mapUnifiedFullViewingKeys.find(keyId); + if (mi != mapUnifiedFullViewingKeys.end()) { + return mi->second; + } else { + return std::nullopt; + } +} diff --git a/src/keystore.h b/src/keystore.h index dd731195a23..3f611fa39e7 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -108,10 +108,13 @@ class CKeyStore const libzcash::ZcashdUnifiedFullViewingKey &ufvk ) = 0; - virtual bool AddUnifiedAddress( + virtual void AddUnifiedAddress( const libzcash::UFVKId& keyId, const libzcash::UnifiedAddress &ua ) = 0; + + virtual std::optional GetUnifiedFullViewingKey( + const libzcash::UFVKId& keyId) = 0; }; typedef std::map KeyMap; @@ -346,9 +349,12 @@ class CBasicKeyStore : public CKeyStore * Add the transparent component of the unified address, if any, * to the keystore to make it possible to identify the */ - virtual bool AddUnifiedAddress( + virtual void AddUnifiedAddress( const libzcash::UFVKId& keyId, const libzcash::UnifiedAddress &ua); + + virtual std::optional GetUnifiedFullViewingKey( + const libzcash::UFVKId& keyId); }; typedef std::vector > CKeyingMaterial; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a9f1eb9f105..16c6019a6d8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -474,7 +474,7 @@ std::optional> CWallet::GetUnifiedFullViewingKeyByAccount(libzcash::AccountId accountId) { + if (!mnemonicHDChain.has_value()) { + throw std::runtime_error( + "CWallet::GenerateNewUnifiedSpendingKey(): Wallet is missing mnemonic seed metadata."); + } + + auto seedfp = mnemonicHDChain.value().GetSeedFingerprint(); + auto i = mapUnifiedKeyMetadata.find(std::make_pair(seedfp, accountId)); + if (i != mapUnifiedKeyMetadata.end()) { + auto keyId = i->second.GetKeyID(); + auto key = CCryptoKeyStore::GetUnifiedFullViewingKey(keyId); + if (key.has_value()) { + return std::make_pair(keyId, key.value()); + } else { + return std::nullopt; + } + } else { + return std::nullopt; + } +} + +UAGenerationResult CWallet::GenerateUnifiedAddress( + const libzcash::AccountId& accountId, + const libzcash::diversifier_index_t& j, + const std::set& receiverTypes) +{ + if (!libzcash::HasShielded(receiverTypes)) { + return AddressGenerationError::InvalidReceiverTypes; + } + + auto identifiedKey = GetUnifiedFullViewingKeyByAccount(accountId); + if (identifiedKey.has_value()) { + auto ufvkid = identifiedKey.value().first; + auto ufvk = identifiedKey.value().second; + + // Check whether an address has already been generated for this + // diversifier index. If so, ensure that the set of receiver types + // being requested is the same as the set of receiver types that was + // previously generated; if so, return the previously generated address, + // otherwise return an error. + if (mapUnifiedAddressMetadata.count(ufvkid) > 0) { + const auto& accountKeys = mapUnifiedAddressMetadata.at(ufvkid); + if (accountKeys.count(j) > 0) { + if (accountKeys.at(j) == receiverTypes) { + ZcashdUnifiedAddressMetadata addrmeta(ufvkid, j, receiverTypes); + auto addr = ufvk.Address(j, receiverTypes); + return std::make_pair(addr.value(), addrmeta); + } else { + return AddressGenerationError::ExistingAddressMismatch; + } + } + } + + // Find a working diversifier and construct the associated address. + auto found = ufvk.FindAddress(j, receiverTypes); + auto diversifierIndex = found.second; + + // Persist the newly created address to the keystore + AddUnifiedAddress(ufvkid, found.first); + + // Save the metadata for the generated address so that we can re-derive + // it in the future. + ZcashdUnifiedAddressMetadata addrmeta(ufvkid, found.second, receiverTypes); + mapUnifiedAddressMetadata[ufvkid].insert({diversifierIndex, receiverTypes}); + if (fFileBacked) { + CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta); + } + return std::make_pair(found.first, addrmeta); + } else { + return AddressGenerationError::NoSuchAccount; + } +} + bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key) { - auto keyId = key.GetKeyID(Params()); + auto ufvkid = key.GetKeyID(Params()); auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(key); - return CCryptoKeyStore::AddUnifiedFullViewingKey(keyId, zufvk); + if (mapUnifiedAddressMetadata.count(ufvkid) > 0) { + // restore unified addresses that have been previously generated to the + // keystore + for (const auto &[j, receiverTypes] : mapUnifiedAddressMetadata[ufvkid]) { + auto addr = zufvk.Address(j, receiverTypes).value(); + AddUnifiedAddress(ufvkid, addr); + } + } + return CCryptoKeyStore::AddUnifiedFullViewingKey(ufvkid, zufvk); +} + +void CWallet::LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta) +{ + AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata + auto metaKey = std::make_pair(skmeta.GetSeedFingerprint(), skmeta.GetAccountId()); + mapUnifiedKeyMetadata.insert({metaKey, skmeta}); } -void CWallet::LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &meta) +bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta) { AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata - mapUnifiedKeyMetadata.insert({meta.GetKeyID(), meta}); + auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID()); + if (ufvk.has_value()) { + // restore unified addresses that have been previously generated + auto addr = ufvk.value().Address(addrmeta.GetDiversifierIndex(), addrmeta.GetReceiverTypes()); + if (addr.has_value()) { + AddUnifiedAddress(addrmeta.GetKeyID(), addr.value()); + } else { + // an error has occurred; the ufvk is loaded but cannot reproduce the + // address identified by the address metadata. + return false; + } + } + return true; } void CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d497f5b5698..2d05c82c184 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -402,6 +402,17 @@ class CMerkleTx : public CTransaction bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectAbsurdFee=true); }; +enum class AddressGenerationError { + NoSuchAccount, + InvalidReceiverTypes, + ExistingAddressMismatch, + NoSaplingAddressForDiversifier +}; + +typedef std::variant< + std::pair, + AddressGenerationError> UAGenerationResult; + /** * A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. @@ -843,7 +854,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; - std::map mapUnifiedKeyMetadata; + std::map, ZcashdUnifiedSpendingKeyMetadata> mapUnifiedKeyMetadata; + std::map>> mapUnifiedAddressMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1129,10 +1141,22 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::optional> GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); + //! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account. + std::optional> + GetUnifiedFullViewingKeyByAccount(libzcash::AccountId account); + + //! Generate a new unified address for the specified account, diversifier, and + //! set of receiver types. + UAGenerationResult GenerateUnifiedAddress( + const libzcash::AccountId& accountId, + const libzcash::diversifier_index_t& j, + const std::set& receivers); + bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key); + bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - void LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &meta); + void LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta); + bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta); /** * Increment the next transaction order id diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 6b0660810d1..72080dc19c1 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -235,6 +235,13 @@ bool CWalletDB::WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey return Write(std::make_pair(std::string("unifiedfvk"), ufvkId), ufvk.Encode(Params())); } +bool CWalletDB::WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta) +{ + nWalletDBUpdateCounter++; + auto ufvkId = addrmeta.GetKeyID(); + return Write(std::make_pair(std::string("unifiedaddrmeta"), ufvkId), addrmeta); +} + // // // @@ -684,6 +691,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, auto keymeta = ZcashdUnifiedSpendingKeyMetadata::Read(ssValue); pwallet->LoadUnifiedKeyMetadata(keymeta); } + else if (strType == "unifiedaddrmeta") + { + auto keymeta = ZcashdUnifiedAddressMetadata::Read(ssValue); + pwallet->LoadUnifiedAddressMetadata(keymeta); + } else if (strType == "pool") { int64_t nIndex; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 124a78f736c..71f5404d2d2 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -251,14 +251,14 @@ class ZcashdUnifiedAddressMetadata { private: libzcash::UFVKId ufvkId; libzcash::diversifier_index_t diversifierIndex; - std::vector receiverTypes; + std::set receiverTypes; ZcashdUnifiedAddressMetadata() {} public: ZcashdUnifiedAddressMetadata( libzcash::UFVKId ufvkId, libzcash::diversifier_index_t diversifierIndex, - std::vector receiverTypes): + std::set receiverTypes): ufvkId(ufvkId), diversifierIndex(diversifierIndex), receiverTypes(receiverTypes) {} libzcash::UFVKId GetKeyID() const { @@ -267,7 +267,7 @@ class ZcashdUnifiedAddressMetadata { libzcash::diversifier_index_t GetDiversifierIndex() const { return diversifierIndex; } - const std::vector& GetReceiverTypes() const { + const std::set& GetReceiverTypes() const { return receiverTypes; } @@ -282,7 +282,7 @@ class ZcashdUnifiedAddressMetadata { READWRITE(serReceiverTypes); receiverTypes.clear(); for (ReceiverTypeSer r : serReceiverTypes) - receiverTypes.push_back(r.t); + receiverTypes.insert(r.t); } else { std::vector serReceiverTypes; for (libzcash::ReceiverType r : receiverTypes) @@ -381,6 +381,7 @@ class CWalletDB : public CDB bool WriteUnifiedSpendingKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata& keymeta); bool WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk); + bool WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta); static void IncrementUpdateCounter(); static unsigned int GetUpdateCounter(); diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index 3d328467e3a..1bf4b13b52b 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -12,6 +12,14 @@ using namespace libzcash; // Unified Keys // +bool libzcash::HasShielded(const std::set& receiverTypes) { + auto has_shielded = [](ReceiverType r) { + // TODO: update this as support for new protocols is added. + return r == ReceiverType::Sapling; + }; + return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_shielded) != receiverTypes.end(); +} + std::optional ZcashdUnifiedSpendingKey::ForAccount( const HDSeed& seed, uint32_t bip44CoinType, @@ -63,10 +71,17 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingK return result; } -std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { - UnifiedAddress ua; +std::optional ZcashdUnifiedFullViewingKey::Address( + const diversifier_index_t& j, + const std::set& receiverTypes) const +{ + if (!HasShielded(receiverTypes)) { + throw std::runtime_error("Unified addresses must include a shielded receiver."); + } - if (saplingKey.has_value()) { + UnifiedAddress ua; + if (saplingKey.has_value() && + std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::Sapling) != receiverTypes.end()) { auto saplingAddress = saplingKey.value().Address(j); if (saplingAddress.has_value()) { ua.AddReceiver(saplingAddress.value()); @@ -75,7 +90,8 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_i } } - if (transparentKey.has_value()) { + if (transparentKey.has_value() && + std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::P2PKH) != receiverTypes.end()) { const auto& tkey = transparentKey.value(); auto childIndex = j.ToTransparentChildIndex(); if (!childIndex.has_value()) return std::nullopt; @@ -98,12 +114,20 @@ std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_i return ua; } -std::pair ZcashdUnifiedFullViewingKey::FindAddress(diversifier_index_t j) const { - auto addr = Address(j); +std::pair ZcashdUnifiedFullViewingKey::FindAddress( + const diversifier_index_t& j, + const std::set& receiverTypes) const { + diversifier_index_t j0(j); + auto addr = Address(j0, receiverTypes); while (!addr.has_value()) { - if (!j.increment()) + if (!j0.increment()) throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; - addr = Address(j); + addr = Address(j0, receiverTypes); } - return std::make_pair(addr.value(), j); + return std::make_pair(addr.value(), j0); +} + +std::pair ZcashdUnifiedFullViewingKey::FindAddress( + const diversifier_index_t& j) const { + return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling, ReceiverType::Orchard}); } diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index c8cce33cff4..53a4755cb45 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -17,6 +17,12 @@ enum class ReceiverType: uint32_t { Orchard = 0x03 }; +/** + * Test whether the specified list of receiver types contains a + * shielded receiver type + */ +bool HasShielded(const std::set& receiverTypes); + class ZcashdUnifiedSpendingKey; // prototypes for the classes handling ZIP-316 encoding (in Address.hpp) @@ -51,9 +57,33 @@ class ZcashdUnifiedFullViewingKey { return saplingKey; } - std::optional Address(diversifier_index_t j) const; + /** + * Creates a new unified address having the specified receiver types, at the specified + * diversifier index, unless the diversifer index would generate an invalid receiver. + * Returns `std::nullopt` if the diversifier index does not produce a valid receiver + * for one or more of the specified receiver types; under this circumstance, the caller + * should usually try successive diversifier indices until the operation returns a + * non-null value. + * + * This method will throw if `receiverTypes` does not include a shielded receiver type. + */ + std::optional Address( + const diversifier_index_t& j, + const std::set& receiverTypes) const; + + /** + * Find the smallest diversifier index >= `j` such that it generates a valid + * unified address according to the conditions specified in the documentation + * for the `Address` method above, and returns the newly created address along + * with the diversifier index used to produce it. + * + * This method will throw if `receiverTypes` does not include a shielded receiver type. + */ + std::pair FindAddress( + const diversifier_index_t& j, + const std::set& receiverTypes) const; - std::pair FindAddress(diversifier_index_t j) const; + std::pair FindAddress(const diversifier_index_t& j) const; }; /** From 290985ba482bbec637eb881f89ba9305c4e2f7e1 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 10 Dec 2021 13:35:32 -0700 Subject: [PATCH 198/514] AddTransparentSecretKey does not need to take a CExtKey --- src/wallet/wallet.cpp | 15 ++++++++++----- src/wallet/wallet.h | 2 +- src/zcash/address/bip44.cpp | 8 ++++---- src/zcash/address/bip44.h | 4 ++-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 16c6019a6d8..24d3efc04f6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -282,7 +282,7 @@ CPubKey CWallet::GenerateNewKey() BIP44CoinType(), ZCASH_LEGACY_ACCOUNT).value(); - std::optional> extKey = std::nullopt; + std::optional> extKey = std::nullopt; do { extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter()); hdChain.IncrementLegacyTKeyCounter(); @@ -301,10 +301,10 @@ CPubKey CWallet::GenerateNewKey() CPubKey CWallet::AddTransparentSecretKey( const uint256& seedFingerprint, - const std::pair& extSecret, + const std::pair& extSecret, const std::optional& ufvkId) { - CKey secret = extSecret.first.key; + CKey secret = extSecret.first; CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); @@ -479,8 +479,10 @@ std::optional& extSecret, + const std::pair& extSecret, const std::optional& ufvkId = std::nullopt); protected: diff --git a/src/zcash/address/bip44.cpp b/src/zcash/address/bip44.cpp index 3456cc3c1dd..075c2491f2a 100644 --- a/src/zcash/address/bip44.cpp +++ b/src/zcash/address/bip44.cpp @@ -46,7 +46,7 @@ std::optional libzcash::Bip44AccountChains::ForAcc return Bip44AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); } -std::optional> libzcash::Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { +std::optional> libzcash::Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { auto childKey = external.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; @@ -56,10 +56,10 @@ std::optional> libzcash::Bip44AccountChains::Deriv + "0/" + std::to_string(addrIndex); - return std::make_pair(childKey.value(), hdKeypath); + return std::make_pair(childKey.value().key, hdKeypath); } -std::optional> libzcash::Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { +std::optional> libzcash::Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { auto childKey = internal.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; @@ -69,6 +69,6 @@ std::optional> libzcash::Bip44AccountChains::Deriv + "1/" + std::to_string(addrIndex); - return std::make_pair(childKey.value(), hdKeypath); + return std::make_pair(childKey.value().key, hdKeypath); } diff --git a/src/zcash/address/bip44.h b/src/zcash/address/bip44.h index cea73531245..502e892434e 100644 --- a/src/zcash/address/bip44.h +++ b/src/zcash/address/bip44.h @@ -29,8 +29,8 @@ class Bip44AccountChains { uint32_t bip44CoinType, libzcash::AccountId accountId); - std::optional> DeriveExternal(uint32_t addrIndex); - std::optional> DeriveInternal(uint32_t addrIndex); + std::optional> DeriveExternal(uint32_t addrIndex); + std::optional> DeriveInternal(uint32_t addrIndex); }; } //namespace libzcash From 83374425538ea8f5ff635630152778d1d77558d2 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 10 Dec 2021 14:38:29 -0700 Subject: [PATCH 199/514] Add newly generated transparent UA receivers to the wallet. --- src/wallet/wallet.cpp | 26 ++++++++++++++++++++++++-- src/wallet/wallet.h | 4 +++- src/zcash/address/bip44.h | 13 ++++++++++++- src/zcash/address/zip32.cpp | 4 ++-- src/zcash/address/zip32.h | 2 +- 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 24d3efc04f6..460cde9d11d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -564,6 +564,22 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( return AddressGenerationError::InvalidReceiverTypes; } + // The wallet must be unlocked in order to generate new transparent UA + // receivers, because we need to be able to add the secret key for the + // external child address at the diversifier index to the wallet's + // transparent backend in order to be able to detect transactions as + // ours rather than considering them as watch-only. + bool hasTransparent = receiverTypes.find(ReceiverType::P2PKH) != receiverTypes.end(); + if (hasTransparent) { + if (!j.ToTransparentChildIndex().has_value()) { + return AddressGenerationError::InvalidTransparentChildIndex; + } + + if (IsCrypted() || !GetMnemonicSeed().has_value()) { + return AddressGenerationError::WalletEncrypted; + } + } + auto identifiedKey = GetUnifiedFullViewingKeyByAccount(accountId); if (identifiedKey.has_value()) { auto ufvkid = identifiedKey.value().first; @@ -594,8 +610,14 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // Persist the newly created address to the keystore AddUnifiedAddress(ufvkid, found.first); - // If we have the associated spending key, add this to the keystore as one - // of our own addresses with AddTransparentSecretKey, + if (hasTransparent) { + // Regenerate the secret key for the transparent address and add it to + // the wallet. + auto seed = GetMnemonicSeed().value(); + auto b44 = libzcash::Bip44AccountChains::ForAccount(seed, BIP44CoinType(), accountId).value(); + auto key = b44.DeriveExternal(j.ToTransparentChildIndex().value()).value(); + AddTransparentSecretKey(seed.Fingerprint(), key, ufvkid); + } // Save the metadata for the generated address so that we can re-derive // it in the future. diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f5e3ebbf507..28664af62cd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -406,7 +406,9 @@ enum class AddressGenerationError { NoSuchAccount, InvalidReceiverTypes, ExistingAddressMismatch, - NoSaplingAddressForDiversifier + NoSaplingAddressForDiversifier, + WalletEncrypted, + InvalidTransparentChildIndex }; typedef std::variant< diff --git a/src/zcash/address/bip44.h b/src/zcash/address/bip44.h index 502e892434e..43235682a9e 100644 --- a/src/zcash/address/bip44.h +++ b/src/zcash/address/bip44.h @@ -29,8 +29,19 @@ class Bip44AccountChains { uint32_t bip44CoinType, libzcash::AccountId accountId); + /** + * Generate the key corresponding to the specified index at the "external child" + * level of the BIP44 path for the account. + */ std::optional> DeriveExternal(uint32_t addrIndex); - std::optional> DeriveInternal(uint32_t addrIndex); + + /** + * Generate the key corresponding to the specified index at the "internal child" + * level of the BIP44 path for the account. This should probably only usually be + * used at address index 0, but ordinarily it won't need to be used at all since + * all change should be shielded by default. + */ + std::optional> DeriveInternal(uint32_t addrIndex = 0); }; } //namespace libzcash diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index f5a753b3185..b22cfa045a2 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -48,12 +48,12 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed) { namespace libzcash { -std::optional diversifier_index_t::ToTransparentChildIndex() const { +std::optional diversifier_index_t::ToTransparentChildIndex() const { // ensure that the diversifier index is small enough for a t-addr if (MAX_TRANSPARENT_CHILD_IDX < *this) { return std::nullopt; } else { - return (unsigned int) GetUint64(0); + return (uint32_t) GetUint64(0); } } diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 60aef73fff2..d2ba55d8dd1 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -103,7 +103,7 @@ class diversifier_index_t : public base_blob<88> { return false; //overflow } - std::optional ToTransparentChildIndex() const; + std::optional ToTransparentChildIndex() const; friend bool operator<(const diversifier_index_t& a, const diversifier_index_t& b) { for (int i = 10; i >= 0; i--) { From 0dcdc28a38b3b2bea48a182f104b8033f1c5f6c4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 13 Dec 2021 15:11:29 -0700 Subject: [PATCH 200/514] Add CWallet::GetUnifiedForReceiver --- src/keystore.cpp | 53 ++++++++++++++++++++--- src/keystore.h | 48 ++++++++++++++++----- src/rust/include/librustzcash.h | 19 +++++++++ src/rust/src/rustzcash.rs | 16 +++++++ src/wallet/wallet.cpp | 75 +++++++++++++++++++++++++++++---- src/wallet/wallet.h | 15 +++++++ 6 files changed, 201 insertions(+), 25 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index 54fdb2a92ce..175b79215e5 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -311,7 +311,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( void CBasicKeyStore::AddUnifiedAddress( const libzcash::UFVKId& keyId, - const libzcash::UnifiedAddress& ua) + const std::pair& ua) { LOCK(cs_KeyStore); @@ -319,20 +319,21 @@ void CBasicKeyStore::AddUnifiedAddress( // the UA; all other lookups of the associated UFVK will be // made via the protocol-specific viewing key that is used // to trial-decrypt a transaction. + auto addrEntry = std::make_pair(keyId, ua.second); - auto p2pkhReceiver = ua.GetP2PKHReceiver(); + auto p2pkhReceiver = ua.first.GetP2PKHReceiver(); if (p2pkhReceiver.has_value()) { - mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), keyId)); + mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), addrEntry)); } - auto p2shReceiver = ua.GetP2SHReceiver(); + auto p2shReceiver = ua.first.GetP2SHReceiver(); if (p2shReceiver.has_value()) { - mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), keyId)); + mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), addrEntry)); } } std::optional CBasicKeyStore::GetUnifiedFullViewingKey( - const libzcash::UFVKId& keyId) + const libzcash::UFVKId& keyId) const { auto mi = mapUnifiedFullViewingKeys.find(keyId); if (mi != mapUnifiedFullViewingKeys.end()) { @@ -341,3 +342,43 @@ std::optional CBasicKeyStore::GetUnifiedF return std::nullopt; } } + +std::optional>> +CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) const +{ + return std::visit(FindUFVKId(*this), receiver); +} + +std::optional>> +FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const { + if (keystore.mapSaplingIncomingViewingKeys.count(saplingAddr) > 0) { + const auto& saplingIvk = keystore.mapSaplingIncomingViewingKeys.at(saplingAddr); + if (keystore.mapSaplingKeyUnified.count(saplingIvk) > 0) { + return std::make_pair(keystore.mapSaplingKeyUnified.at(saplingIvk), std::nullopt); + } else { + return std::nullopt; + } + } else { + return std::nullopt; + } +} +std::optional>> +FindUFVKId::operator()(const CScriptID& scriptId) const { + if (keystore.mapP2SHUnified.count(scriptId) > 0) { + return keystore.mapP2SHUnified.at(scriptId); + } else { + return std::nullopt; + } +} +std::optional>> +FindUFVKId::operator()(const CKeyID& keyId) const { + if (keystore.mapP2PKHUnified.count(keyId) > 0) { + return keystore.mapP2PKHUnified.at(keyId); + } else { + return std::nullopt; + } +} +std::optional>> +FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const { + return std::nullopt; +} diff --git a/src/keystore.h b/src/keystore.h index 3f611fa39e7..9d9001caf61 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -110,11 +110,17 @@ class CKeyStore virtual void AddUnifiedAddress( const libzcash::UFVKId& keyId, - const libzcash::UnifiedAddress &ua + const std::pair& ua ) = 0; virtual std::optional GetUnifiedFullViewingKey( - const libzcash::UFVKId& keyId) = 0; + const libzcash::UFVKId& keyId + ) const = 0; + + virtual std::optional>> + GetUFVKMetadataForReceiver( + const libzcash::Receiver& receiver + ) const = 0; }; typedef std::map KeyMap; @@ -136,11 +142,7 @@ typedef std::map< // Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses. typedef std::map SaplingIncomingViewingKeyMap; -struct UnifiedAddressMetadata { - libzcash::UFVKId keyId; - libzcash::diversifier_index_t j; - std::vector receiverTypes; -}; +class FindUFVKId; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore @@ -164,10 +166,12 @@ class CBasicKeyStore : public CKeyStore SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys; // Unified key support - std::map mapP2PKHUnified; - std::map mapP2SHUnified; + std::map> mapP2PKHUnified; + std::map> mapP2SHUnified; std::map mapSaplingKeyUnified; std::map mapUnifiedFullViewingKeys; + + friend class FindUFVKId; public: bool SetMnemonicSeed(const MnemonicSeed& seed); bool HaveMnemonicSeed() const; @@ -351,10 +355,15 @@ class CBasicKeyStore : public CKeyStore */ virtual void AddUnifiedAddress( const libzcash::UFVKId& keyId, - const libzcash::UnifiedAddress &ua); + const std::pair& ua); virtual std::optional GetUnifiedFullViewingKey( - const libzcash::UFVKId& keyId); + const libzcash::UFVKId& keyId) const; + + virtual std::optional>> + GetUFVKMetadataForReceiver( + const libzcash::Receiver& receiver + ) const; }; typedef std::vector > CKeyingMaterial; @@ -364,4 +373,21 @@ typedef std::map > Cr //! Sapling typedef std::map > CryptedSaplingSpendingKeyMap; +class FindUFVKId { +private: + const CBasicKeyStore& keystore; + +public: + FindUFVKId(const CBasicKeyStore& keystore): keystore(keystore) {} + + std::optional>> + operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const; + std::optional>> + operator()(const CScriptID& scriptId) const; + std::optional>> + operator()(const CKeyID& keyId) const; + std::optional>> + operator()(const libzcash::UnknownReceiver& receiver) const; +}; + #endif // BITCOIN_KEYSTORE_H diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index ae5dc4b84f9..e76af6ba11d 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -353,6 +353,25 @@ extern "C" { unsigned char *addr_ret ); + /** + * Decrypts a Sapling diversifier using the specified diversifier key + * to obtain the diversifier index `j` at which the diversivier was + * derived. + * + * Returns `true` if the diversifier decrypted successfully to an index, + * `false` otherwise. + * + * Arguments: + * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key + * - addr: [c_uchar; 11] the bytes of the diversifier + * - j_ret: [c_uchar; 11] array that will store the retulgin diversifier index + */ + bool librustzcash_sapling_diversifier_index( + const unsigned char *dk, + const unsigned char *d, + unsigned char *j_ret + ); + /// Fills the provided buffer with random bytes. This is intended to /// be a cryptographically secure RNG; it uses Rust's `OsRng`, which /// is implemented in terms of the `getrandom` crate. The first call diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index 86f11f4e5c6..6deb08ce91d 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -47,6 +47,7 @@ use zcash_primitives::{ constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, merkle_tree::MerklePath, sapling::{ + self, keys::FullViewingKey, note_encryption::sapling_ka_agree, redjubjub::{self, Signature}, @@ -1130,6 +1131,21 @@ pub extern "C" fn librustzcash_zip32_find_sapling_address( } } +#[no_mangle] +pub extern "C" fn librustzcash_sapling_diversifier_index( + dk: *const [c_uchar; 32], + d: *const [c_uchar; 11], + j_ret: *mut [c_uchar; 11], +) -> bool { + let dk = zip32::DiversifierKey(unsafe { *dk }); + let diversifier = sapling::Diversifier(unsafe { *d }); + let j_ret = unsafe { &mut *j_ret }; + + let j = dk.diversifier_index(&diversifier); + j_ret.copy_from_slice(&j.0); + true +} + #[no_mangle] pub extern "C" fn librustzcash_getrandom(buf: *mut u8, buf_len: usize) { let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 460cde9d11d..a6aec31101f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -604,11 +604,11 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( } // Find a working diversifier and construct the associated address. - auto found = ufvk.FindAddress(j, receiverTypes); - auto diversifierIndex = found.second; + auto foundAddress = ufvk.FindAddress(j, receiverTypes); + auto diversifierIndex = foundAddress.second; // Persist the newly created address to the keystore - AddUnifiedAddress(ufvkid, found.first); + AddUnifiedAddress(ufvkid, foundAddress); if (hasTransparent) { // Regenerate the secret key for the transparent address and add it to @@ -621,12 +621,12 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // Save the metadata for the generated address so that we can re-derive // it in the future. - ZcashdUnifiedAddressMetadata addrmeta(ufvkid, found.second, receiverTypes); + ZcashdUnifiedAddressMetadata addrmeta(ufvkid, foundAddress.second, receiverTypes); mapUnifiedAddressMetadata[ufvkid].insert({diversifierIndex, receiverTypes}); if (fFileBacked) { CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta); } - return std::make_pair(found.first, addrmeta); + return std::make_pair(foundAddress.first, addrmeta); } else { return AddressGenerationError::NoSuchAccount; } @@ -641,7 +641,7 @@ bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &k // keystore for (const auto &[j, receiverTypes] : mapUnifiedAddressMetadata[ufvkid]) { auto addr = zufvk.Address(j, receiverTypes).value(); - AddUnifiedAddress(ufvkid, addr); + AddUnifiedAddress(ufvkid, std::make_pair(addr, j)); } } return CCryptoKeyStore::AddUnifiedFullViewingKey(ufvkid, zufvk); @@ -660,9 +660,10 @@ bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &add auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID()); if (ufvk.has_value()) { // restore unified addresses that have been previously generated - auto addr = ufvk.value().Address(addrmeta.GetDiversifierIndex(), addrmeta.GetReceiverTypes()); + auto j = addrmeta.GetDiversifierIndex(); + auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes()); if (addr.has_value()) { - AddUnifiedAddress(addrmeta.GetKeyID(), addr.value()); + AddUnifiedAddress(addrmeta.GetKeyID(), std::make_pair(addr.value(), j)); } else { // an error has occurred; the ufvk is loaded but cannot reproduce the // address identified by the address metadata. @@ -5524,6 +5525,10 @@ void CWallet::GetFilteredNotes( } +std::optional CWallet::GetUnifiedForReceiver(const Receiver& receiver) { + return std::visit(LookupUnifiedAddress(*this), receiver); +} + // // Shielded key and address generalizations // @@ -5794,3 +5799,57 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } + +std::optional LookupUnifiedAddress::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const { + auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr); + if (ufvkPair.has_value()) { + auto ufvkid = ufvkPair.value().first; + auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid); + if (!(ufvk.has_value() && ufvk.value().GetSaplingKey().has_value())) { + throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no Sapling key part."); + } + + diversifier_index_t j; + if (wallet.mapUnifiedAddressMetadata.count(ufvkid) > 0 && + librustzcash_sapling_diversifier_index( + ufvk.value().GetSaplingKey().value().dk.begin(), + saplingAddr.d.begin(), + j.begin()) && + wallet.mapUnifiedAddressMetadata.at(ufvkid).count(j) > 0) { + + return ufvk.value().Address(j, wallet.mapUnifiedAddressMetadata.at(ufvkid).at(j)); + } else { + return std::nullopt; + } + } else { + return std::nullopt; + } +} +std::optional LookupUnifiedAddress::operator()(const CScriptID& scriptId) const { + return std::nullopt; +} +std::optional LookupUnifiedAddress::operator()(const CKeyID& keyId) const { + auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId); + if (ufvkPair.has_value()) { + auto ufvkid = ufvkPair.value().first; + // transparent address UFVK metadata is always accompanied by the child + // index at which the address was produced + diversifier_index_t j = ufvkPair.value().second.value(); + auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid); + if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) { + throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no P2PKH key part."); + } + + if (wallet.mapUnifiedAddressMetadata.count(ufvkid) > 0) { + return ufvk.value().Address(j, wallet.mapUnifiedAddressMetadata.at(ufvkid).at(j)); + } else { + return std::nullopt; + } + + } else { + return std::nullopt; + } +} +std::optional LookupUnifiedAddress::operator()(const libzcash::UnknownReceiver& receiver) const { + return std::nullopt; +} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 28664af62cd..c46e2d2e12b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1154,6 +1154,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface const libzcash::diversifier_index_t& j, const std::set& receivers); + std::optional GetUnifiedForReceiver(const libzcash::Receiver& receiver); + bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); @@ -1570,5 +1572,18 @@ class AddSpendingKeyToWallet KeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; +class LookupUnifiedAddress { +private: + const CWallet& wallet; + +public: + LookupUnifiedAddress(const CWallet& wallet): wallet(wallet) {} + + std::optional operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const; + std::optional operator()(const CScriptID& scriptId) const; + std::optional operator()(const CKeyID& keyId) const; + std::optional operator()(const libzcash::UnknownReceiver& receiver) const; +}; + #endif // BITCOIN_WALLET_WALLET_H From 700b98f0b0153892b1b9b26590e6c1df4274db94 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 14 Dec 2021 10:09:35 -0700 Subject: [PATCH 201/514] Add tests for keystore storage and retrieval of UFVKs. --- src/gtest/test_keystore.cpp | 31 +++++++++++++++++++++++++++++++ src/keystore.cpp | 8 ++++++++ src/zcash/address/unified.cpp | 6 ++---- src/zcash/address/unified.h | 5 +++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index c9b230b5934..ed43c26e2c4 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -14,6 +14,8 @@ #define MAKE_STRING(x) std::string((x), (x)+sizeof(x)) +using namespace libzcash; + const uint32_t SLIP44_TESTNET_TYPE = 1; TEST(KeystoreTests, StoreAndRetrieveMnemonicSeed) { @@ -514,4 +516,33 @@ TEST(KeystoreTests, StoreAndRetrieveSpendingKeyInEncryptedStore) { ASSERT_EQ(1, addrs.count(addr)); ASSERT_EQ(1, addrs.count(addr2)); } + +TEST(KeystoreTests, StoreAndRetrieveUFVK) { + SelectParams(CBaseChainParams::TESTNET); + CBasicKeyStore keyStore; + + auto seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed, SLIP44_TESTNET_TYPE, 0); + EXPECT_TRUE(usk.has_value()); + + auto zufvk = usk.value().ToFullViewingKey(); + auto ufvk = UnifiedFullViewingKey::FromZcashdUFVK(zufvk); + auto ufvkid = ufvk.GetKeyID(Params()); + + EXPECT_TRUE(keyStore.AddUnifiedFullViewingKey(ufvkid, zufvk)); + EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk); + + auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}); + EXPECT_TRUE(addrPair.first.GetSaplingReceiver().has_value()); + auto saplingReceiver = addrPair.first.GetSaplingReceiver().value(); + + auto saplingIvk = zufvk.GetSaplingKey().value().fvk.in_viewing_key(); + keyStore.AddSaplingIncomingViewingKey(saplingIvk, saplingReceiver); + + auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver); + EXPECT_TRUE(ufvkmeta.has_value()); + EXPECT_EQ(ufvkmeta.value().first, ufvkid); + EXPECT_FALSE(ufvkmeta.value().second.has_value()); +} + #endif diff --git a/src/keystore.cpp b/src/keystore.cpp index 175b79215e5..985e17e7e17 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -298,12 +298,20 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( { LOCK(cs_KeyStore); + // Add the Sapling component of the UFVK to the wallet. auto saplingKey = ufvk.GetSaplingKey(); if (saplingKey.has_value()) { auto ivk = saplingKey.value().fvk.in_viewing_key(); mapSaplingKeyUnified.insert(std::make_pair(ivk, keyId)); } + // We can't reasonably add the transparent component here, because + // of the way that transparent addresses are generated from the + // P2PHK part of the unified address. Instead, whenever a new + // unified address is generated, the keys associated with the + // transparent part of the address must be added to the keystore. + + // Add the UFVK by key identifier. mapUnifiedFullViewingKeys.insert({keyId, ufvk}); return true; diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index 1bf4b13b52b..e78d084c389 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -80,8 +80,7 @@ std::optional ZcashdUnifiedFullViewingKey::Address( } UnifiedAddress ua; - if (saplingKey.has_value() && - std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::Sapling) != receiverTypes.end()) { + if (saplingKey.has_value() && receiverTypes.count(ReceiverType::Sapling) > 0) { auto saplingAddress = saplingKey.value().Address(j); if (saplingAddress.has_value()) { ua.AddReceiver(saplingAddress.value()); @@ -90,8 +89,7 @@ std::optional ZcashdUnifiedFullViewingKey::Address( } } - if (transparentKey.has_value() && - std::find(receiverTypes.begin(), receiverTypes.end(), ReceiverType::P2PKH) != receiverTypes.end()) { + if (transparentKey.has_value() && receiverTypes.count(ReceiverType::P2PKH) > 0) { const auto& tkey = transparentKey.value(); auto childIndex = j.ToTransparentChildIndex(); if (!childIndex.has_value()) return std::nullopt; diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 53a4755cb45..f8385078d41 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -84,6 +84,11 @@ class ZcashdUnifiedFullViewingKey { const std::set& receiverTypes) const; std::pair FindAddress(const diversifier_index_t& j) const; + + friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b) + { + return a.transparentKey == b.transparentKey && a.saplingKey == b.saplingKey; + } }; /** From 009ba76b0e4e7e1b6e664bed02f4ee946938efe7 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 14 Dec 2021 16:46:36 -0700 Subject: [PATCH 202/514] Add test for wallet UA generation & detection. --- src/wallet/gtest/test_wallet.cpp | 73 +++++++++++++++++++++++++++++--- src/wallet/wallet.cpp | 1 - src/wallet/walletdb.h | 7 +++ src/zcash/Address.hpp | 3 ++ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index a915f83e4de..dbe3ee39418 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -20,6 +20,7 @@ #include using ::testing::Return; +using namespace libzcash; ACTION(ThrowLogicError) { throw std::logic_error("Boom"); @@ -187,6 +188,7 @@ TEST(WalletTests, FindUnspentSproutNotes) { CWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); + auto sk = libzcash::SproutSpendingKey::random(); wallet.AddSproutSpendingKey(sk); @@ -468,6 +470,7 @@ TEST(WalletTests, SetInvalidSaplingNoteDataInCWalletTx) { } TEST(WalletTests, CheckSproutNoteCommitmentAgainstNotePlaintext) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -492,6 +495,7 @@ TEST(WalletTests, CheckSproutNoteCommitmentAgainstNotePlaintext) { } TEST(WalletTests, GetSproutNoteNullifier) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -527,7 +531,6 @@ TEST(WalletTests, GetSproutNoteNullifier) { TEST(WalletTests, FindMySaplingNotes) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -562,6 +565,7 @@ TEST(WalletTests, FindMySaplingNotes) { } TEST(WalletTests, FindMySproutNotes) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -588,8 +592,10 @@ TEST(WalletTests, FindMySproutNotes) { } TEST(WalletTests, FindMySproutNotesInEncryptedWallet) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + uint256 r {GetRandHash()}; CKeyingMaterial vMasterKey (r.begin(), r.end()); @@ -619,6 +625,7 @@ TEST(WalletTests, FindMySproutNotesInEncryptedWallet) { } TEST(WalletTests, GetConflictedSproutNotes) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -779,6 +786,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) { } TEST(WalletTests, SproutNullifierIsSpent) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); @@ -821,7 +829,6 @@ TEST(WalletTests, SproutNullifierIsSpent) { TEST(WalletTests, SaplingNullifierIsSpent) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); @@ -878,6 +885,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) { } TEST(WalletTests, NavigateFromSproutNullifierToNote) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -906,7 +914,6 @@ TEST(WalletTests, NavigateFromSproutNullifierToNote) { TEST(WalletTests, NavigateFromSaplingNullifierToNote) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); @@ -998,6 +1005,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) { } TEST(WalletTests, SpentSproutNoteIsFromMe) { + SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -1238,8 +1246,10 @@ TEST(WalletTests, CachedWitnessesEmptyChain) { } TEST(WalletTests, CachedWitnessesChainTip) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + std::pair anchors1; CBlock block1; SproutMerkleTree sproutTree; @@ -1341,8 +1351,10 @@ TEST(WalletTests, CachedWitnessesChainTip) { } TEST(WalletTests, CachedWitnessesDecrementFirst) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + SproutMerkleTree sproutTree; SaplingMerkleTree saplingTree; @@ -1422,8 +1434,10 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) { } TEST(WalletTests, CachedWitnessesCleanIndex) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + std::vector blocks; std::vector indices; std::vector sproutNotes; @@ -1510,6 +1524,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) { } TEST(WalletTests, ClearNoteWitnessCache) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -1577,8 +1592,10 @@ TEST(WalletTests, ClearNoteWitnessCache) { } TEST(WalletTests, WriteWitnessCache) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + MockWalletDB walletdb; CBlockLocator loc; @@ -1664,9 +1681,9 @@ TEST(WalletTests, WriteWitnessCache) { TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) { SelectParams(CBaseChainParams::REGTEST); - TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + MockWalletDB walletdb; CBlockLocator loc; @@ -1746,8 +1763,10 @@ TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) { } TEST(WalletTests, UpdateSproutNullifierNoteMap) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + uint256 r {GetRandHash()}; CKeyingMaterial vMasterKey (r.begin(), r.end()); @@ -1782,6 +1801,7 @@ TEST(WalletTests, UpdateSproutNullifierNoteMap) { } TEST(WalletTests, UpdatedSproutNoteData) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -1831,7 +1851,6 @@ TEST(WalletTests, UpdatedSproutNoteData) { TEST(WalletTests, UpdatedSaplingNoteData) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); @@ -1941,6 +1960,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) { } TEST(WalletTests, MarkAffectedSproutTransactionsDirty) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -1974,7 +1994,6 @@ TEST(WalletTests, MarkAffectedSproutTransactionsDirty) { TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { auto consensusParams = RegtestActivateSapling(); - TestWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); @@ -2084,6 +2103,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { } TEST(WalletTests, SproutNoteLocking) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); @@ -2118,8 +2138,10 @@ TEST(WalletTests, SproutNoteLocking) { } TEST(WalletTests, SaplingNoteLocking) { + SelectParams(CBaseChainParams::REGTEST); TestWallet wallet(Params()); LOCK(wallet.cs_wallet); + SaplingOutPoint sop1 {uint256(), 1}; SaplingOutPoint sop2 {uint256(), 2}; @@ -2149,3 +2171,42 @@ TEST(WalletTests, SaplingNoteLocking) { EXPECT_FALSE(wallet.IsLockedNote(sop1)); EXPECT_FALSE(wallet.IsLockedNote(sop2)); } + +TEST(WalletTests, GenerateUnifiedAddress) { + SelectParams(CBaseChainParams::TESTNET); + TestWallet wallet(Params()); + + UAGenerationResult uaResult = wallet.GenerateUnifiedAddress(0, diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); + + // If the wallet does not have a mnemonic seed available, it is + // treated as if the wallet is encrypted. + UAGenerationResult expected = AddressGenerationError::WalletEncrypted; + EXPECT_EQ(uaResult, expected); + + wallet.GenerateNewSeed(); + EXPECT_FALSE(wallet.IsCrypted()); + EXPECT_TRUE(wallet.GetMnemonicSeed().has_value()); + + // If the user has not generated a unified spending key, + // we cannot create an address for the account corresponding + // to that spending key. + uaResult = wallet.GenerateUnifiedAddress(0, diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); + expected = AddressGenerationError::NoSuchAccount; + EXPECT_EQ(uaResult, expected); + + // Create an account, then generate an address for that account. + auto skpair = wallet.GenerateNewUnifiedSpendingKey(); + uaResult = wallet.GenerateUnifiedAddress( + skpair.second.GetAccountId(), + diversifier_index_t(0), + {ReceiverType::P2PKH, ReceiverType::Sapling}); + bool success = std::holds_alternative>(uaResult); + EXPECT_TRUE(success); + + auto zufvk = skpair.first.ToFullViewingKey(); + auto addrpair = std::get>(uaResult); + EXPECT_TRUE(addrpair.first.GetSaplingReceiver().has_value()); + + auto u4r = wallet.GetUnifiedForReceiver(addrpair.first.GetSaplingReceiver().value()); + EXPECT_EQ(u4r, addrpair.first); +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a6aec31101f..0de25d6819c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5845,7 +5845,6 @@ std::optional LookupUnifiedAddress::operator()(const C } else { return std::nullopt; } - } else { return std::nullopt; } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 71f5404d2d2..4310c58adb5 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -297,6 +297,13 @@ class ZcashdUnifiedAddressMetadata { stream >> meta; return meta; } + + friend inline bool operator==(const ZcashdUnifiedAddressMetadata& a, const ZcashdUnifiedAddressMetadata& b) { + return + a.ufvkId == b.ufvkId && + a.diversifierIndex == b.diversifierIndex && + a.receiverTypes == b.receiverTypes; + } }; diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 580e01bc26c..fb916ccfa90 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -135,6 +135,9 @@ class UnifiedAddress { friend inline bool operator==(const UnifiedAddress& a, const UnifiedAddress& b) { return a.receivers == b.receivers; } + friend inline bool operator!=(const UnifiedAddress& a, const UnifiedAddress& b) { + return a.receivers != b.receivers; + } friend inline bool operator<(const UnifiedAddress& a, const UnifiedAddress& b) { return a.receivers < b.receivers; } From b34fd6a8166658e2ca61bfc9a34fbe529c2cd9a0 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 15 Dec 2021 09:46:29 -0700 Subject: [PATCH 203/514] Add test for CKeyStore::AddUnifiedAddress --- src/gtest/test_keystore.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index ed43c26e2c4..d27c2dcacb4 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -545,4 +545,26 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { EXPECT_FALSE(ufvkmeta.value().second.has_value()); } +TEST(KeystoreTests, AddUnifiedAddress) { + SelectParams(CBaseChainParams::TESTNET); + CBasicKeyStore keyStore; + + auto seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE); + auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed, SLIP44_TESTNET_TYPE, 0); + EXPECT_TRUE(usk.has_value()); + + auto zufvk = usk.value().ToFullViewingKey(); + auto ufvk = UnifiedFullViewingKey::FromZcashdUFVK(zufvk); + auto ufvkid = ufvk.GetKeyID(Params()); + auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); + EXPECT_TRUE(addrPair.first.GetP2PKHReceiver().has_value()); + + keyStore.AddUnifiedAddress(ufvkid, addrPair); + + auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value()); + EXPECT_TRUE(ufvkmeta.has_value()); + EXPECT_EQ(ufvkmeta.value().first, ufvkid); +} + + #endif From 9b72fff365d56ca0aa9f044f897f0a3c8a67b2b9 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 20 Dec 2021 08:58:53 -0700 Subject: [PATCH 204/514] Fix handling of unified full viewing key metadata. This fixes two issues with the management of unified full viewing key metadata: * Adds a reverse mapping from ufvkid to account id. * Ensures that UFVK metadata is correctly initialized when USK metadata is loaded from disk. --- src/wallet/wallet.cpp | 68 ++++++++++++++++++++++++++++------------- src/wallet/wallet.h | 55 +++++++++++++++++++++++++++------ src/wallet/walletdb.cpp | 10 ++++-- 3 files changed, 100 insertions(+), 33 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0de25d6819c..3367aebcbac 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -450,7 +450,7 @@ std::pair CWallet::G std::optional> CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { - AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata + AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta auto seed = GetMnemonicSeed(); if (!seed.has_value()) { @@ -474,7 +474,7 @@ std::optional> CWallet::GetUnifie } auto seedfp = mnemonicHDChain.value().GetSeedFingerprint(); - auto i = mapUnifiedKeyMetadata.find(std::make_pair(seedfp, accountId)); - if (i != mapUnifiedKeyMetadata.end()) { + auto i = mapUnifiedSpendingKeyMeta.find(std::make_pair(seedfp, accountId)); + if (i != mapUnifiedSpendingKeyMeta.end()) { auto keyId = i->second.GetKeyID(); auto key = CCryptoKeyStore::GetUnifiedFullViewingKey(keyId); if (key.has_value()) { @@ -590,10 +590,10 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // being requested is the same as the set of receiver types that was // previously generated; if so, return the previously generated address, // otherwise return an error. - if (mapUnifiedAddressMetadata.count(ufvkid) > 0) { - const auto& accountKeys = mapUnifiedAddressMetadata.at(ufvkid); - if (accountKeys.count(j) > 0) { - if (accountKeys.at(j) == receiverTypes) { + if (mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0) { + auto receivers = mapUnifiedFullViewingKeyMeta.at(ufvkid).GetReceivers(j); + if (receivers.has_value()) { + if (receivers.value() == receiverTypes) { ZcashdUnifiedAddressMetadata addrmeta(ufvkid, j, receiverTypes); auto addr = ufvk.Address(j, receiverTypes); return std::make_pair(addr.value(), addrmeta); @@ -601,6 +601,8 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( return AddressGenerationError::ExistingAddressMismatch; } } + } else { + mapUnifiedFullViewingKeyMeta[ufvkid] = ZcashdUnifiedFullViewingKeyMetadata(accountId); } // Find a working diversifier and construct the associated address. @@ -622,7 +624,10 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // Save the metadata for the generated address so that we can re-derive // it in the future. ZcashdUnifiedAddressMetadata addrmeta(ufvkid, foundAddress.second, receiverTypes); - mapUnifiedAddressMetadata[ufvkid].insert({diversifierIndex, receiverTypes}); + + // we can safely ignore the return value here; we know that we're adding a new + // set of receivers given the receivers.has_value() check above. + mapUnifiedFullViewingKeyMeta[ufvkid].SetReceivers(diversifierIndex, receiverTypes); if (fFileBacked) { CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta); } @@ -636,10 +641,10 @@ bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &k { auto ufvkid = key.GetKeyID(Params()); auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(key); - if (mapUnifiedAddressMetadata.count(ufvkid) > 0) { + if (mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0) { // restore unified addresses that have been previously generated to the // keystore - for (const auto &[j, receiverTypes] : mapUnifiedAddressMetadata[ufvkid]) { + for (const auto &[j, receiverTypes] : mapUnifiedFullViewingKeyMeta.at(ufvkid).GetAllReceivers()) { auto addr = zufvk.Address(j, receiverTypes).value(); AddUnifiedAddress(ufvkid, std::make_pair(addr, j)); } @@ -647,16 +652,21 @@ bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &k return CCryptoKeyStore::AddUnifiedFullViewingKey(ufvkid, zufvk); } -void CWallet::LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta) +bool CWallet::LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta) { - AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata + AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta auto metaKey = std::make_pair(skmeta.GetSeedFingerprint(), skmeta.GetAccountId()); - mapUnifiedKeyMetadata.insert({metaKey, skmeta}); + mapUnifiedSpendingKeyMeta.insert({metaKey, skmeta}); + if (mapUnifiedFullViewingKeyMeta.count(skmeta.GetKeyID()) == 0) { + return mapUnifiedFullViewingKeyMeta[skmeta.GetKeyID()].SetAccountId(skmeta.GetAccountId()); + } + + return true; } bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta) { - AssertLockHeld(cs_wallet); // mapUnifiedKeyMetadata + AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID()); if (ufvk.has_value()) { // restore unified addresses that have been previously generated @@ -5810,14 +5820,19 @@ std::optional LookupUnifiedAddress::operator()(const l } diversifier_index_t j; - if (wallet.mapUnifiedAddressMetadata.count(ufvkid) > 0 && + if (wallet.mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0 && librustzcash_sapling_diversifier_index( ufvk.value().GetSaplingKey().value().dk.begin(), saplingAddr.d.begin(), - j.begin()) && - wallet.mapUnifiedAddressMetadata.at(ufvkid).count(j) > 0) { - - return ufvk.value().Address(j, wallet.mapUnifiedAddressMetadata.at(ufvkid).at(j)); + j.begin())) { + auto receivers = wallet.mapUnifiedFullViewingKeyMeta.at(ufvkid).GetReceivers(j); + if (receivers.has_value()) { + return ufvk.value().Address(j, receivers.value()); + } else { + // If we don't know the receiver types at which the address was originally + // generated, we can't reconstruct the address. + return std::nullopt; + } } else { return std::nullopt; } @@ -5834,14 +5849,23 @@ std::optional LookupUnifiedAddress::operator()(const C auto ufvkid = ufvkPair.value().first; // transparent address UFVK metadata is always accompanied by the child // index at which the address was produced + assert(ufvkPair.value().second.has_value()); diversifier_index_t j = ufvkPair.value().second.value(); auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid); if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) { throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no P2PKH key part."); } - if (wallet.mapUnifiedAddressMetadata.count(ufvkid) > 0) { - return ufvk.value().Address(j, wallet.mapUnifiedAddressMetadata.at(ufvkid).at(j)); + // Find the set of receivers at the diversifier index. If no metadata is available + // for the ufvk, or we do not know the receiver types for the address produced + // at this diversifier, we cannot reconstruct the address. + if (wallet.mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0) { + auto receivers = wallet.mapUnifiedFullViewingKeyMeta.at(ufvkid).GetReceivers(j); + if (receivers.has_value()) { + return ufvk.value().Address(j, receivers.value()); + } else { + return std::nullopt; + } } else { return std::nullopt; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c46e2d2e12b..1e94775c881 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -638,9 +638,6 @@ class CWalletTx : public CMerkleTx std::set GetConflicts() const; }; - - - class COutput { public: @@ -657,9 +654,6 @@ class COutput std::string ToString() const; }; - - - /** Private key that includes an expiration date in case it never gets used. */ class CWalletKey { @@ -687,6 +681,49 @@ class CWalletKey } }; +class ZcashdUnifiedFullViewingKeyMetadata +{ +private: + std::optional accountId; + std::map> addressReceivers; +public: + ZcashdUnifiedFullViewingKeyMetadata() {} + ZcashdUnifiedFullViewingKeyMetadata(libzcash::AccountId accountId): accountId(accountId) {} + + const std::map>& GetAllReceivers() const { + return addressReceivers; + } + + std::optional> GetReceivers( + const libzcash::diversifier_index_t& j) const { + if (addressReceivers.count(j) > 0) { + return addressReceivers.at(j); + } else { + return std::nullopt; + } + } + + bool SetReceivers( + const libzcash::diversifier_index_t& j, + const std::set& receivers) { + if (addressReceivers.count(j) > 0) { + return false; + } else { + addressReceivers[j] = receivers; + return true; + } + } + + bool SetAccountId(libzcash::AccountId accountIdIn) { + if (accountId.has_value()) { + return (accountIdIn == accountId.value()); + } else { + accountId = accountIdIn; + return true; + } + } +}; + /** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. @@ -856,8 +893,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; - std::map, ZcashdUnifiedSpendingKeyMetadata> mapUnifiedKeyMetadata; - std::map>> mapUnifiedAddressMetadata; + std::map, ZcashdUnifiedSpendingKeyMetadata> mapUnifiedSpendingKeyMeta; + std::map mapUnifiedFullViewingKeyMeta; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1159,7 +1196,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - void LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta); + bool LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta); bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta); /** diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 72080dc19c1..ed0a01268fd 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -689,12 +689,18 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, else if (strType == "unifiedskmeta") { auto keymeta = ZcashdUnifiedSpendingKeyMetadata::Read(ssValue); - pwallet->LoadUnifiedKeyMetadata(keymeta); + if (!pwallet->LoadUnifiedKeyMetadata(keymeta)) { + strErr = "Error reading wallet database: account ID mismatch for unified spending key."; + return false; + }; } else if (strType == "unifiedaddrmeta") { auto keymeta = ZcashdUnifiedAddressMetadata::Read(ssValue); - pwallet->LoadUnifiedAddressMetadata(keymeta); + if (!pwallet->LoadUnifiedAddressMetadata(keymeta)) { + strErr = "Error reading wallet database: cannot reproduce previously generated unified address."; + return false; + } } else if (strType == "pool") { From 735ecd0906c2dcae4477397dc92b15e0f05042f4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 22 Dec 2021 09:14:41 -0700 Subject: [PATCH 205/514] Apply suggestions from code review Co-authored-by: str4d --- src/gtest/test_keystore.cpp | 1 + src/rust/include/librustzcash.h | 2 +- src/wallet/gtest/test_wallet.cpp | 2 ++ src/wallet/wallet.cpp | 26 +++++++++++++++----------- src/wallet/wallet.h | 16 ++++++---------- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index d27c2dcacb4..5790391b899 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -528,6 +528,7 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { auto zufvk = usk.value().ToFullViewingKey(); auto ufvk = UnifiedFullViewingKey::FromZcashdUFVK(zufvk); auto ufvkid = ufvk.GetKeyID(Params()); + EXPECT_FALSE(keyStore.GetUnifiedFullViewingKey(ufvkid).has_value()); EXPECT_TRUE(keyStore.AddUnifiedFullViewingKey(ufvkid, zufvk)); EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk); diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index e76af6ba11d..8f6e2967970 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -364,7 +364,7 @@ extern "C" { * Arguments: * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key * - addr: [c_uchar; 11] the bytes of the diversifier - * - j_ret: [c_uchar; 11] array that will store the retulgin diversifier index + * - j_ret: [c_uchar; 11] array that will store the resulting diversifier index */ bool librustzcash_sapling_diversifier_index( const unsigned char *dk, diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index dbe3ee39418..a0b1a6c407d 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -2180,6 +2180,8 @@ TEST(WalletTests, GenerateUnifiedAddress) { // If the wallet does not have a mnemonic seed available, it is // treated as if the wallet is encrypted. + EXPECT_FALSE(wallet.IsCrypted()); + EXPECT_FALSE(wallet.GetMnemonicSeed().has_value()); UAGenerationResult expected = AddressGenerationError::WalletEncrypted; EXPECT_EQ(uaResult, expected); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3367aebcbac..f02b26f4bbd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -590,8 +590,9 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // being requested is the same as the set of receiver types that was // previously generated; if so, return the previously generated address, // otherwise return an error. - if (mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0) { - auto receivers = mapUnifiedFullViewingKeyMeta.at(ufvkid).GetReceivers(j); + auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvkid); + if (metadata != mapUnifiedFullViewingKeyMeta.end()) { + auto receivers = metadata->second.GetReceivers(j); if (receivers.has_value()) { if (receivers.value() == receiverTypes) { ZcashdUnifiedAddressMetadata addrmeta(ufvkid, j, receiverTypes); @@ -617,13 +618,13 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // the wallet. auto seed = GetMnemonicSeed().value(); auto b44 = libzcash::Bip44AccountChains::ForAccount(seed, BIP44CoinType(), accountId).value(); - auto key = b44.DeriveExternal(j.ToTransparentChildIndex().value()).value(); + auto key = b44.DeriveExternal(diversifierIndex.ToTransparentChildIndex().value()).value(); AddTransparentSecretKey(seed.Fingerprint(), key, ufvkid); } // Save the metadata for the generated address so that we can re-derive // it in the future. - ZcashdUnifiedAddressMetadata addrmeta(ufvkid, foundAddress.second, receiverTypes); + ZcashdUnifiedAddressMetadata addrmeta(ufvkid, diversifierIndex, receiverTypes); // we can safely ignore the return value here; we know that we're adding a new // set of receivers given the receivers.has_value() check above. @@ -641,10 +642,11 @@ bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &k { auto ufvkid = key.GetKeyID(Params()); auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(key); - if (mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0) { + auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvkid); + if (metadata != mapUnifiedFullViewingKeyMeta.end()) { // restore unified addresses that have been previously generated to the // keystore - for (const auto &[j, receiverTypes] : mapUnifiedFullViewingKeyMeta.at(ufvkid).GetAllReceivers()) { + for (const auto &[j, receiverTypes] : metadata->second.GetAllReceivers()) { auto addr = zufvk.Address(j, receiverTypes).value(); AddUnifiedAddress(ufvkid, std::make_pair(addr, j)); } @@ -5820,12 +5822,13 @@ std::optional LookupUnifiedAddress::operator()(const l } diversifier_index_t j; - if (wallet.mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0 && + auto metadata = wallet.mapUnifiedFullViewingKeyMeta.find(ufvkid); + if (metadata != wallet.mapUnifiedFullViewingKeyMeta.end()) { librustzcash_sapling_diversifier_index( ufvk.value().GetSaplingKey().value().dk.begin(), saplingAddr.d.begin(), - j.begin())) { - auto receivers = wallet.mapUnifiedFullViewingKeyMeta.at(ufvkid).GetReceivers(j); + j.begin()); + auto receivers = metadata->second.GetReceivers(j); if (receivers.has_value()) { return ufvk.value().Address(j, receivers.value()); } else { @@ -5859,8 +5862,9 @@ std::optional LookupUnifiedAddress::operator()(const C // Find the set of receivers at the diversifier index. If no metadata is available // for the ufvk, or we do not know the receiver types for the address produced // at this diversifier, we cannot reconstruct the address. - if (wallet.mapUnifiedFullViewingKeyMeta.count(ufvkid) > 0) { - auto receivers = wallet.mapUnifiedFullViewingKeyMeta.at(ufvkid).GetReceivers(j); + auto metadata = wallet.mapUnifiedFullViewingKeyMeta.find(ufvkid); + if (metadata != wallet.mapUnifiedFullViewingKeyMeta.end()) { + auto receivers = metadata->second.GetReceivers(j); if (receivers.has_value()) { return ufvk.value().Address(j, receivers.value()); } else { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1e94775c881..4714066141d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -696,8 +696,9 @@ class ZcashdUnifiedFullViewingKeyMetadata std::optional> GetReceivers( const libzcash::diversifier_index_t& j) const { - if (addressReceivers.count(j) > 0) { - return addressReceivers.at(j); + auto receivers = addressReceivers.find(j); + if (receivers != addressReceivers.end()) { + return receivers->second; } else { return std::nullopt; } @@ -706,12 +707,7 @@ class ZcashdUnifiedFullViewingKeyMetadata bool SetReceivers( const libzcash::diversifier_index_t& j, const std::set& receivers) { - if (addressReceivers.count(j) > 0) { - return false; - } else { - addressReceivers[j] = receivers; - return true; - } + return addressReceivers.insert(std::make_pair(j, receivers)).second; } bool SetAccountId(libzcash::AccountId accountIdIn) { @@ -753,7 +749,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool fBroadcastTransactions; /** - * A map from protocol-specifiec transaction output identifier to + * A map from a protocol-specific transaction output identifier to * a txid. */ template @@ -859,7 +855,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SyncMetaData(std::pair::iterator, typename TxSpendMap::iterator>); void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree); - /* Add an extended secret key to the wallet. Internal use only. */ + /* Add a transparent secret key to the wallet. Internal use only. */ CPubKey AddTransparentSecretKey( const uint256& seedFingerprint, const std::pair& extSecret, From 24e46a16f1e29bdb86308c70956cd26a351176eb Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 22 Dec 2021 13:26:32 -0700 Subject: [PATCH 206/514] Only derive ZcashdUnifiedFullViewingKey from UnifiedFullViewingKey This changes ZcashdUnifiedSpendingKey::ToUnifiedFullViewingKey to return a `UnifiedFullViewingKey` object. This makes the derivation of ZcashdUnifiedFullViewingKey values more regular, removing the need to be able to construct these values directly from the spending key. This change also modifies ZcashdUnifiedSpendingKey to remove the `std::optional` wrapper from its member keys. This optionality is spurious, because it is always possible to derive a "complete" spending key, and we do not support import of unified spending keys. --- src/gtest/test_keystore.cpp | 14 +++--- src/keystore.cpp | 5 +-- src/keystore.h | 2 - src/wallet/rpcdump.cpp | 2 +- src/wallet/wallet.cpp | 81 ++++++++++++++++------------------- src/wallet/wallet.h | 2 +- src/zcash/Address.cpp | 2 +- src/zcash/Address.hpp | 10 ++--- src/zcash/address/bip44.h | 4 ++ src/zcash/address/unified.cpp | 24 +++++------ src/zcash/address/unified.h | 35 +++++++++++---- 11 files changed, 93 insertions(+), 88 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 5790391b899..5b21d15bb42 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -525,12 +525,12 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed, SLIP44_TESTNET_TYPE, 0); EXPECT_TRUE(usk.has_value()); - auto zufvk = usk.value().ToFullViewingKey(); - auto ufvk = UnifiedFullViewingKey::FromZcashdUFVK(zufvk); - auto ufvkid = ufvk.GetKeyID(Params()); + auto ufvk = usk.value().ToFullViewingKey(); + auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), ufvk); + auto ufvkid = zufvk.GetKeyID(); EXPECT_FALSE(keyStore.GetUnifiedFullViewingKey(ufvkid).has_value()); - EXPECT_TRUE(keyStore.AddUnifiedFullViewingKey(ufvkid, zufvk)); + EXPECT_TRUE(keyStore.AddUnifiedFullViewingKey(zufvk)); EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk); auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}); @@ -554,9 +554,9 @@ TEST(KeystoreTests, AddUnifiedAddress) { auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed, SLIP44_TESTNET_TYPE, 0); EXPECT_TRUE(usk.has_value()); - auto zufvk = usk.value().ToFullViewingKey(); - auto ufvk = UnifiedFullViewingKey::FromZcashdUFVK(zufvk); - auto ufvkid = ufvk.GetKeyID(Params()); + auto ufvk = usk.value().ToFullViewingKey(); + auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), ufvk); + auto ufvkid = zufvk.GetKeyID(); auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); EXPECT_TRUE(addrPair.first.GetP2PKHReceiver().has_value()); diff --git a/src/keystore.cpp b/src/keystore.cpp index 985e17e7e17..169a0e97ce6 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -293,7 +293,6 @@ bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymen // bool CBasicKeyStore::AddUnifiedFullViewingKey( - const libzcash::UFVKId& keyId, const libzcash::ZcashdUnifiedFullViewingKey &ufvk) { LOCK(cs_KeyStore); @@ -302,7 +301,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( auto saplingKey = ufvk.GetSaplingKey(); if (saplingKey.has_value()) { auto ivk = saplingKey.value().fvk.in_viewing_key(); - mapSaplingKeyUnified.insert(std::make_pair(ivk, keyId)); + mapSaplingKeyUnified.insert(std::make_pair(ivk, ufvk.GetKeyID())); } // We can't reasonably add the transparent component here, because @@ -312,7 +311,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( // transparent part of the address must be added to the keystore. // Add the UFVK by key identifier. - mapUnifiedFullViewingKeys.insert({keyId, ufvk}); + mapUnifiedFullViewingKeys.insert({ufvk.GetKeyID(), ufvk}); return true; } diff --git a/src/keystore.h b/src/keystore.h index 9d9001caf61..662b49bcc2d 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -104,7 +104,6 @@ class CKeyStore //! Unified addresses and keys virtual bool AddUnifiedFullViewingKey( - const libzcash::UFVKId& keyId, const libzcash::ZcashdUnifiedFullViewingKey &ufvk ) = 0; @@ -346,7 +345,6 @@ class CBasicKeyStore : public CKeyStore libzcash::SproutViewingKey& vkOut) const; virtual bool AddUnifiedFullViewingKey( - const libzcash::UFVKId& keyId, const libzcash::ZcashdUnifiedFullViewingKey &ufvk); /** diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 4e302c95ab1..893129129fc 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -870,7 +870,7 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); } - auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey{}, viewingkey); + auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey(Params()), viewingkey); UniValue result(UniValue::VOBJ); const string strAddress = keyIO.EncodePaymentAddress(addrInfo.second); result.pushKV("type", addrInfo.first); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f02b26f4bbd..b5c0b44556c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -460,8 +460,7 @@ std::optional> CWallet::GetUnifiedFullViewingKeyByAccount(libzcash::AccountId accountId) { +std::optional CWallet::GetUnifiedFullViewingKeyByAccount(libzcash::AccountId accountId) { if (!mnemonicHDChain.has_value()) { throw std::runtime_error( - "CWallet::GenerateNewUnifiedSpendingKey(): Wallet is missing mnemonic seed metadata."); + "CWallet::GetUnifiedFullViewingKeyByAccount(): Wallet is missing mnemonic seed metadata."); } auto seedfp = mnemonicHDChain.value().GetSeedFingerprint(); auto i = mapUnifiedSpendingKeyMeta.find(std::make_pair(seedfp, accountId)); if (i != mapUnifiedSpendingKeyMeta.end()) { auto keyId = i->second.GetKeyID(); - auto key = CCryptoKeyStore::GetUnifiedFullViewingKey(keyId); - if (key.has_value()) { - return std::make_pair(keyId, key.value()); - } else { - return std::nullopt; - } + return CCryptoKeyStore::GetUnifiedFullViewingKey(keyId); } else { return std::nullopt; } @@ -582,20 +575,19 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( auto identifiedKey = GetUnifiedFullViewingKeyByAccount(accountId); if (identifiedKey.has_value()) { - auto ufvkid = identifiedKey.value().first; - auto ufvk = identifiedKey.value().second; + auto ufvk = identifiedKey.value(); // Check whether an address has already been generated for this // diversifier index. If so, ensure that the set of receiver types // being requested is the same as the set of receiver types that was // previously generated; if so, return the previously generated address, // otherwise return an error. - auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvkid); + auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvk.GetKeyID()); if (metadata != mapUnifiedFullViewingKeyMeta.end()) { auto receivers = metadata->second.GetReceivers(j); if (receivers.has_value()) { if (receivers.value() == receiverTypes) { - ZcashdUnifiedAddressMetadata addrmeta(ufvkid, j, receiverTypes); + ZcashdUnifiedAddressMetadata addrmeta(ufvk.GetKeyID(), j, receiverTypes); auto addr = ufvk.Address(j, receiverTypes); return std::make_pair(addr.value(), addrmeta); } else { @@ -603,7 +595,7 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( } } } else { - mapUnifiedFullViewingKeyMeta[ufvkid] = ZcashdUnifiedFullViewingKeyMetadata(accountId); + mapUnifiedFullViewingKeyMeta[ufvk.GetKeyID()] = ZcashdUnifiedFullViewingKeyMetadata(accountId); } // Find a working diversifier and construct the associated address. @@ -611,7 +603,7 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( auto diversifierIndex = foundAddress.second; // Persist the newly created address to the keystore - AddUnifiedAddress(ufvkid, foundAddress); + AddUnifiedAddress(ufvk.GetKeyID(), foundAddress); if (hasTransparent) { // Regenerate the secret key for the transparent address and add it to @@ -624,11 +616,11 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // Save the metadata for the generated address so that we can re-derive // it in the future. - ZcashdUnifiedAddressMetadata addrmeta(ufvkid, diversifierIndex, receiverTypes); + ZcashdUnifiedAddressMetadata addrmeta(ufvk.GetKeyID(), diversifierIndex, receiverTypes); // we can safely ignore the return value here; we know that we're adding a new // set of receivers given the receivers.has_value() check above. - mapUnifiedFullViewingKeyMeta[ufvkid].SetReceivers(diversifierIndex, receiverTypes); + mapUnifiedFullViewingKeyMeta[ufvk.GetKeyID()].SetReceivers(diversifierIndex, receiverTypes); if (fFileBacked) { CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta); } @@ -640,18 +632,17 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key) { - auto ufvkid = key.GetKeyID(Params()); - auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(key); - auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvkid); + auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key); + auto metadata = mapUnifiedFullViewingKeyMeta.find(zufvk.GetKeyID()); if (metadata != mapUnifiedFullViewingKeyMeta.end()) { // restore unified addresses that have been previously generated to the // keystore for (const auto &[j, receiverTypes] : metadata->second.GetAllReceivers()) { auto addr = zufvk.Address(j, receiverTypes).value(); - AddUnifiedAddress(ufvkid, std::make_pair(addr, j)); + AddUnifiedAddress(zufvk.GetKeyID(), std::make_pair(addr, j)); } } - return CCryptoKeyStore::AddUnifiedFullViewingKey(ufvkid, zufvk); + return CCryptoKeyStore::AddUnifiedFullViewingKey(zufvk); } bool CWallet::LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4714066141d..58e5feee743 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1177,7 +1177,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); //! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account. - std::optional> + std::optional GetUnifiedFullViewingKeyByAccount(libzcash::AccountId account); //! Generate a new unified address for the specified account, diversifier, and diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index ee6f30645e4..9b3f3bf711c 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -93,7 +93,7 @@ std::pair AddressInfoFromViewingKey::operator()(con std::pair AddressInfoFromViewingKey::operator()(const UnifiedFullViewingKey &ufvk) const { return std::make_pair( "unified", - ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(ufvk) + ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(keyConstants, ufvk) .FindAddress(diversifier_index_t(0)) .first ); diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index fb916ccfa90..23326059b2a 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -145,12 +145,6 @@ class UnifiedAddress { class UnifiedFullViewingKeyBuilder; -class UFVKId: public uint256 { -public: - UFVKId() : uint256() {} - UFVKId(const uint256& in) : uint256(in) {} -}; - /** * Wrapper for a zcash_address::unified::Ufvk. */ @@ -249,7 +243,11 @@ class AddressInfoFromSpendingKey { }; class AddressInfoFromViewingKey { +private: + const KeyConstants& keyConstants; + public: + AddressInfoFromViewingKey(const KeyConstants& keyConstants): keyConstants(keyConstants) {} std::pair operator()(const SproutViewingKey&) const; std::pair operator()(const struct SaplingExtendedFullViewingKey&) const; std::pair operator()(const UnifiedFullViewingKey&) const; diff --git a/src/zcash/address/bip44.h b/src/zcash/address/bip44.h index 43235682a9e..5e6a6a7e300 100644 --- a/src/zcash/address/bip44.h +++ b/src/zcash/address/bip44.h @@ -11,6 +11,10 @@ namespace libzcash { HDKeyPath Bip44TransparentAccountKeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId); +/** + * Derive a transparent extended key in compressed format for the specified + * seed, bip44 coin type, and account ID. + */ std::optional> DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); class Bip44AccountChains { diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index e78d084c389..a96b0a4e250 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -36,27 +36,23 @@ std::optional ZcashdUnifiedSpendingKey::ForAccount( return usk; } -ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { - ZcashdUnifiedFullViewingKey ufvk; +UnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { + UnifiedFullViewingKeyBuilder builder; - if (transparentKey.has_value()) { - auto extPubKey = transparentKey.value().Neuter(); - - // TODO: how to ensure that the pubkey is in its compressed representation? - // Is that already guaranteed? - ufvk.transparentKey = CChainablePubKey::FromParts(extPubKey.chaincode, extPubKey.pubkey).value(); - } - - if (saplingKey.has_value()) { - ufvk.saplingKey = saplingKey.value().ToXFVK(); - } + auto extPubKey = transparentKey.Neuter(); + builder.AddTransparentKey(CChainablePubKey::FromParts(extPubKey.chaincode, extPubKey.pubkey).value()); + builder.AddSaplingKey(saplingKey.ToXFVK()); - return ufvk; + // This call to .value() is safe as ZcashdUnifiedSpendingKey values are always + // constructed to contain all required components. + return builder.build().value(); } ZcashdUnifiedFullViewingKey ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey( + const KeyConstants& keyConstants, const UnifiedFullViewingKey& ufvk) { ZcashdUnifiedFullViewingKey result; + result.keyId = ufvk.GetKeyID(keyConstants); auto transparentKey = ufvk.GetTransparentKey(); if (transparentKey.has_value()) { diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index f8385078d41..0a0435cc5e0 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -5,8 +5,9 @@ #ifndef ZCASH_ZCASH_ADDRESS_UNIFIED_H #define ZCASH_ZCASH_ADDRESS_UNIFIED_H -#include "zip32.h" #include "bip44.h" +#include "key_constants.h" +#include "zip32.h" namespace libzcash { @@ -29,6 +30,16 @@ class ZcashdUnifiedSpendingKey; class UnifiedAddress; class UnifiedFullViewingKey; +/** + * An internal identifier for a unified full viewing key, derived as a + * blake2b hash of the serialized form of the UFVK. + */ +class UFVKId: public uint256 { +public: + UFVKId() : uint256() {} + UFVKId(const uint256& in) : uint256(in) {} +}; + /** * An internal-only type for unified full viewing keys that represents only the * set of receiver types that are supported by zcashd. This type does not @@ -37,6 +48,7 @@ class UnifiedFullViewingKey; */ class ZcashdUnifiedFullViewingKey { private: + UFVKId keyId; std::optional transparentKey; std::optional saplingKey; @@ -45,9 +57,16 @@ class ZcashdUnifiedFullViewingKey { friend class ZcashdUnifiedSpendingKey; public: /** - * This constructor is lossy, and does not support round-trip transformations. + * This constructor is lossy; it ignores unknown receiver types + * and therefore does not support round-trip transformations. */ - static ZcashdUnifiedFullViewingKey FromUnifiedFullViewingKey(const UnifiedFullViewingKey& ufvk); + static ZcashdUnifiedFullViewingKey FromUnifiedFullViewingKey( + const KeyConstants& keyConstants, + const UnifiedFullViewingKey& ufvk); + + const UFVKId& GetKeyID() const { + return keyId; + } const std::optional& GetTransparentKey() const { return transparentKey; @@ -96,8 +115,8 @@ class ZcashdUnifiedFullViewingKey { */ class ZcashdUnifiedSpendingKey { private: - std::optional transparentKey; - std::optional saplingKey; + CExtKey transparentKey; + SaplingExtendedSpendingKey saplingKey; ZcashdUnifiedSpendingKey() {} public: @@ -106,15 +125,15 @@ class ZcashdUnifiedSpendingKey { uint32_t bip44CoinType, libzcash::AccountId accountId); - const std::optional& GetTransparentKey() const { + const CExtKey& GetTransparentKey() const { return transparentKey; } - const std::optional& GetSaplingExtendedSpendingKey() const { + const SaplingExtendedSpendingKey& GetSaplingExtendedSpendingKey() const { return saplingKey; } - ZcashdUnifiedFullViewingKey ToFullViewingKey() const; + UnifiedFullViewingKey ToFullViewingKey() const; }; } //namespace libzcash From 900cd50741c64e34046909671602eff76d9fd13c Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 22 Dec 2021 13:57:37 -0700 Subject: [PATCH 207/514] Rename `ZcashdUnifiedSpendingKeyMetadata` -> `ZcashdUnifiedAccount` Also, fix a couple of minor documentation issues and make UA generation tests a little more robust. --- src/keystore.h | 14 ++++++++++---- src/rust/include/librustzcash.h | 5 +---- src/rust/src/rustzcash.rs | 3 +-- src/wallet/gtest/test_wallet.cpp | 5 ++++- src/wallet/wallet.cpp | 10 +++++----- src/wallet/wallet.h | 8 ++++---- src/wallet/walletdb.cpp | 18 +++++++++++++----- src/wallet/walletdb.h | 12 ++++++------ 8 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/keystore.h b/src/keystore.h index 662b49bcc2d..140baf7a963 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -107,6 +107,16 @@ class CKeyStore const libzcash::ZcashdUnifiedFullViewingKey &ufvk ) = 0; + /** + * Add the transparent component of the unified address, if any, + * to the keystore to make it possible to identify the unified + * full viewing key from which a transparent address was derived. + * It is not necessary for implementations to add shielded address + * components to the keystore because those will be automatically + * reconstructed when scanning the chain with a shielded incoming + * viewing key upon discovery of the address as having received + * funds. + */ virtual void AddUnifiedAddress( const libzcash::UFVKId& keyId, const std::pair& ua @@ -347,10 +357,6 @@ class CBasicKeyStore : public CKeyStore virtual bool AddUnifiedFullViewingKey( const libzcash::ZcashdUnifiedFullViewingKey &ufvk); - /** - * Add the transparent component of the unified address, if any, - * to the keystore to make it possible to identify the - */ virtual void AddUnifiedAddress( const libzcash::UFVKId& keyId, const std::pair& ua); diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index 8f6e2967970..b10e3e38052 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -358,15 +358,12 @@ extern "C" { * to obtain the diversifier index `j` at which the diversivier was * derived. * - * Returns `true` if the diversifier decrypted successfully to an index, - * `false` otherwise. - * * Arguments: * - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key * - addr: [c_uchar; 11] the bytes of the diversifier * - j_ret: [c_uchar; 11] array that will store the resulting diversifier index */ - bool librustzcash_sapling_diversifier_index( + void librustzcash_sapling_diversifier_index( const unsigned char *dk, const unsigned char *d, unsigned char *j_ret diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index 6deb08ce91d..6f138608c52 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -1136,14 +1136,13 @@ pub extern "C" fn librustzcash_sapling_diversifier_index( dk: *const [c_uchar; 32], d: *const [c_uchar; 11], j_ret: *mut [c_uchar; 11], -) -> bool { +) { let dk = zip32::DiversifierKey(unsafe { *dk }); let diversifier = sapling::Diversifier(unsafe { *d }); let j_ret = unsafe { &mut *j_ret }; let j = dk.diversifier_index(&diversifier); j_ret.copy_from_slice(&j.0); - true } #[no_mangle] diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index a0b1a6c407d..3e9349c7c5d 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -2205,9 +2205,12 @@ TEST(WalletTests, GenerateUnifiedAddress) { bool success = std::holds_alternative>(uaResult); EXPECT_TRUE(success); - auto zufvk = skpair.first.ToFullViewingKey(); + auto ufvk = skpair.first.ToFullViewingKey(); auto addrpair = std::get>(uaResult); EXPECT_TRUE(addrpair.first.GetSaplingReceiver().has_value()); + EXPECT_EQ( + addrpair.first.GetSaplingReceiver(), + ufvk.GetSaplingKey().value().Address(addrpair.second.GetDiversifierIndex())); auto u4r = wallet.GetUnifiedForReceiver(addrpair.first.GetSaplingReceiver().value()); EXPECT_EQ(u4r, addrpair.first); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b5c0b44556c..a77929962dc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -423,7 +423,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi return false; } -std::pair CWallet::GenerateNewUnifiedSpendingKey() { +std::pair CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); if (!mnemonicHDChain.has_value()) { @@ -448,7 +448,7 @@ std::pair CWallet::G } } -std::optional> +std::optional> CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta @@ -463,7 +463,7 @@ std::optional mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; - std::map, ZcashdUnifiedSpendingKeyMetadata> mapUnifiedSpendingKeyMeta; + std::map, ZcashdUnifiedAccount> mapUnifiedSpendingKeyMeta; std::map mapUnifiedFullViewingKeyMeta; typedef std::map MasterKeyMap; @@ -1168,12 +1168,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Generate the unified spending key from the wallet's mnemonic seed //! for the next unused account identifier. - std::pair + std::pair GenerateNewUnifiedSpendingKey(); //! Generate the next available unified spending key from the wallet's //! mnemonic seed. - std::optional> + std::optional> GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); //! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account. @@ -1192,7 +1192,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - bool LoadUnifiedKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata &skmeta); + bool LoadUnifiedAccount(const ZcashdUnifiedAccount &skmeta); bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta); /** diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index ed0a01268fd..712227c47df 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -221,11 +221,11 @@ bool CWalletDB::EraseSaplingExtendedFullViewingKey( // Unified address & key storage // -bool CWalletDB::WriteUnifiedSpendingKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata& keymeta) +bool CWalletDB::WriteUnifiedAccount(const ZcashdUnifiedAccount& keymeta) { nWalletDBUpdateCounter++; auto ufvkId = keymeta.GetKeyID(); - return Write(std::make_pair(std::string("unifiedskmeta"), ufvkId), keymeta); + return Write(std::make_pair(std::string("unifiedaccount"), keymeta), 0x00); } bool CWalletDB::WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk) @@ -686,10 +686,18 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return false; } } - else if (strType == "unifiedskmeta") + else if (strType == "unifiedaccount") { - auto keymeta = ZcashdUnifiedSpendingKeyMetadata::Read(ssValue); - if (!pwallet->LoadUnifiedKeyMetadata(keymeta)) { + auto acct = ZcashdUnifiedAccount::Read(ssKey); + + uint8_t value; + ssValue >> value; + if (value != 0x00) { + strErr = "Error reading wallet database: invalid unified account value."; + return false; + } + + if (!pwallet->LoadUnifiedAccount(acct)) { strErr = "Error reading wallet database: account ID mismatch for unified spending key."; return false; }; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 4310c58adb5..e3928d79851 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -172,16 +172,16 @@ class CKeyMetadata } }; -class ZcashdUnifiedSpendingKeyMetadata { +class ZcashdUnifiedAccount { private: libzcash::SeedFingerprint seedFp; uint32_t bip44CoinType; libzcash::AccountId accountId; libzcash::UFVKId ufvkId; - ZcashdUnifiedSpendingKeyMetadata() {} + ZcashdUnifiedAccount() {} public: - ZcashdUnifiedSpendingKeyMetadata( + ZcashdUnifiedAccount( libzcash::SeedFingerprint seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, @@ -216,8 +216,8 @@ class ZcashdUnifiedSpendingKeyMetadata { } template - static ZcashdUnifiedSpendingKeyMetadata Read(Stream& stream) { - ZcashdUnifiedSpendingKeyMetadata meta; + static ZcashdUnifiedAccount Read(Stream& stream) { + ZcashdUnifiedAccount meta; stream >> meta; return meta; } @@ -386,7 +386,7 @@ class CWalletDB : public CDB /// Unified key support. - bool WriteUnifiedSpendingKeyMetadata(const ZcashdUnifiedSpendingKeyMetadata& keymeta); + bool WriteUnifiedAccount(const ZcashdUnifiedAccount& keymeta); bool WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk); bool WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta); From ad5c4dcf099c7e18aed4b739c795e3027c45da76 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 22 Dec 2021 15:42:42 -0700 Subject: [PATCH 208/514] Remove unused ufvkid argument from AddTransparentSecretKey All of the necessary relationships between transparent receivers and UFVKs are set up by `AddUnifiedAddress`, so there's no need to do so as part of transparent key management otherwise. --- src/wallet/wallet.cpp | 10 ++++------ src/wallet/wallet.h | 8 ++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a77929962dc..bd31486b7d6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -301,8 +301,7 @@ CPubKey CWallet::GenerateNewKey() CPubKey CWallet::AddTransparentSecretKey( const uint256& seedFingerprint, - const std::pair& extSecret, - const std::optional& ufvkId) + const std::pair& extSecret) { CKey secret = extSecret.first; CPubKey pubkey = secret.GetPubKey(); @@ -316,7 +315,7 @@ CPubKey CWallet::AddTransparentSecretKey( if (nTimeFirstKey == 0 || keyMeta.nCreateTime < nTimeFirstKey) nTimeFirstKey = keyMeta.nCreateTime; - if (!AddKeyPubKey(secret, pubkey, ufvkId)) + if (!AddKeyPubKey(secret, pubkey)) throw std::runtime_error("CWallet::GenerateNewKey(): AddKeyPubKey failed"); return pubkey; @@ -324,8 +323,7 @@ CPubKey CWallet::AddTransparentSecretKey( bool CWallet::AddKeyPubKey( const CKey& secret, - const CPubKey &pubkey, - const std::optional& ufvkId) + const CPubKey &pubkey) { AssertLockHeld(cs_wallet); // mapKeyMetadata if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) @@ -611,7 +609,7 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( auto seed = GetMnemonicSeed().value(); auto b44 = libzcash::Bip44AccountChains::ForAccount(seed, BIP44CoinType(), accountId).value(); auto key = b44.DeriveExternal(diversifierIndex.ToTransparentChildIndex().value()).value(); - AddTransparentSecretKey(seed.Fingerprint(), key, ufvkid); + AddTransparentSecretKey(seed.Fingerprint(), key); } // Save the metadata for the generated address so that we can re-derive diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 36b9cc6eec7..5f5e440893c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -858,8 +858,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Add a transparent secret key to the wallet. Internal use only. */ CPubKey AddTransparentSecretKey( const uint256& seedFingerprint, - const std::pair& extSecret, - const std::optional& ufvkId = std::nullopt); + const std::pair& extSecret); protected: bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); @@ -1056,10 +1055,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ CPubKey GenerateNewKey(); //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey( - const CKey& key, - const CPubKey &pubkey, - const std::optional& ufvkId = std::nullopt); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); //! Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } //! Load metadata (used by LoadWallet) From baa3de9250d3f69ab0363ef21d58ec40ef8fd7fe Mon Sep 17 00:00:00 2001 From: Sasha Date: Thu, 23 Dec 2021 00:13:51 +0000 Subject: [PATCH 209/514] make-release.py: Versioning changes for 4.6.0. --- README.md | 2 +- configure.ac | 2 +- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 2 +- src/deprecation.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1f974a3d3ae..c6a6abf2c23 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.6.0-rc1 +Zcash 4.6.0 =========== diff --git a/configure.ac b/configure.ac index 43b90999d3d..d2dbb6ae7e5 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) define(_CLIENT_VERSION_MINOR, 6) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 25) +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))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 886567e9f32..9f72e1d22d4 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.6.0-rc1" +name: "zcash-4.6.0" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index fe2e3f4d08e..8bff582b679 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -18,7 +18,7 @@ #define CLIENT_VERSION_MAJOR 4 #define CLIENT_VERSION_MINOR 6 #define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 25 +#define CLIENT_VERSION_BUILD 50 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/deprecation.h b/src/deprecation.h index 24316025b8a..baece644225 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -10,7 +10,7 @@ // Per https://zips.z.cash/zip-0200 // Shut down nodes running this version of code, 16 weeks' worth of blocks after the estimated // release block height. A warning is shown during the 14 days' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 1498500; +static const int APPROX_RELEASE_HEIGHT = 1504042; static const int RELEASE_TO_DEPRECATION_WEEKS = 16; static const int EXPECTED_BLOCKS_PER_HOUR = 3600 / Consensus::POST_BLOSSOM_POW_TARGET_SPACING; static_assert(EXPECTED_BLOCKS_PER_HOUR == 48, "The value of Consensus::POST_BLOSSOM_POW_TARGET_SPACING was chosen such that this assertion holds."); From de4dd9f23161de5b6062146f5f8186ad4780cd14 Mon Sep 17 00:00:00 2001 From: Sasha Date: Thu, 23 Dec 2021 00:35:40 +0000 Subject: [PATCH 210/514] make-release.py: Updated manpages for 4.6.0. --- doc/man/zcash-cli.1 | 8 ++++---- doc/man/zcash-tx.1 | 8 ++++---- doc/man/zcashd.1 | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 2eecd326f64..0290a4767a1 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "December 2021" "zcash-cli v4.6.0-rc1" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. +.TH ZCASH-CLI "1" "December 2021" "zcash-cli v4.6.0" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.6.0-rc1 +zcash-cli \- manual page for zcash-cli v4.6.0 .SH DESCRIPTION -Zcash RPC client version v4.6.0\-rc1 +Zcash RPC client version v4.6.0 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 9e395912fb8..09873786354 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "December 2021" "zcash-tx v4.6.0-rc1" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. +.TH ZCASH-TX "1" "December 2021" "zcash-tx v4.6.0" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.6.0-rc1 +zcash-tx \- manual page for zcash-tx v4.6.0 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.6.0\-rc1 +Zcash zcash\-tx utility version v4.6.0 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index b4d9e4a4419..ab62ba23e8f 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "December 2021" "zcashd v4.6.0-rc1" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. +.TH ZCASHD "1" "December 2021" "zcashd v4.6.0" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.6.0-rc1 +zcashd \- manual page for zcashd v4.6.0 .SH DESCRIPTION -Zcash Daemon version v4.6.0\-rc1 +Zcash Daemon version v4.6.0 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -84,7 +84,7 @@ Keep at most unconnectable transactions in memory (default: 100) .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-16\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR From eeecf63daec52c169170fc6e4b6e70f782640266 Mon Sep 17 00:00:00 2001 From: Sasha Date: Thu, 23 Dec 2021 00:35:41 +0000 Subject: [PATCH 211/514] make-release.py: Updated release notes and changelog for 4.6.0. --- contrib/debian/changelog | 6 ++ doc/authors.md | 21 ++-- doc/release-notes.md | 27 ----- doc/release-notes/release-notes-4.6.0.md | 119 +++++++++++++++++++++++ 4 files changed, 137 insertions(+), 36 deletions(-) create mode 100644 doc/release-notes/release-notes-4.6.0.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 6cac5c33a53..3caf05a2246 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.6.0) stable; urgency=medium + + * 4.6.0 release. + + -- Electric Coin Company Thu, 23 Dec 2021 00:35:41 +0000 + zcash (4.6.0~rc1) stable; urgency=medium * 4.6.0-rc1 release. diff --git a/doc/authors.md b/doc/authors.md index 60093355b8c..9ca489b67c1 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,29 +1,29 @@ Zcash Contributors ================== -Jack Grigg (1123) +Jack Grigg (1124) Simon Liu (460) Sean Bowe (367) Daira Hopwood (270) Eirik Ogilvie-Wigley (216) -Kris Nuttycombe (174) +Kris Nuttycombe (181) Wladimir J. van der Laan (150) Alfredo Garcia (116) Taylor Hornby (114) -Marshall Gaucher (110) +Marshall Gaucher (111) Pieter Wuille (102) Jonas Schnelli (89) Jay Graber (89) -Marco Falke (81) +Marco Falke (82) Cory Fields (75) -Larry Ruane (61) +Larry Ruane (69) Ying Tong Lai (56) Nathan Wilcox (56) Matt Corallo (52) practicalswift (38) Kevin Gallagher (38) fanquake (36) -Dimitris Apostolou (34) +Dimitris Apostolou (35) Carl Dong (26) Gregory Maxwell (23) Jorge Timón (22) @@ -46,12 +46,13 @@ Alex Morcos (11) Philip Kaufmann (10) Peter Todd (10) João Barbosa (10) +Charlie O'Keefe (10) nomnombtc (9) Marius Kjærstad (9) +teor (8) kozyilmaz (8) Jeremy Rubin (8) Jeff Garzik (8) -Charlie O'Keefe (8) Ben Wilson (8) Karl-Johan Alm (7) ying tong (6) @@ -63,6 +64,7 @@ Chun Kuan Lee (6) Casey Rodarmor (6) jnewbery (5) ca333 (5) +Sasha (5) MeshCollider (5) Johnathan Corgan (5) George Tankersley (5) @@ -75,6 +77,7 @@ WO (4) Sjors Provoost (4) Russell Yanofsky (4) Nate Wilcox (4) +Alex Wied (4) mruddy (3) lpescher (3) isle2983 (3) @@ -90,7 +93,6 @@ Eric Lombrozo (3) Danny Willems (3) Anthony Towns (3) Alfie John (3) -Alex Wied (3) whythat (2) rofl0r (2) ptschip (2) @@ -134,7 +136,7 @@ vim88 (1) unsystemizer (1) tulip (1) tpantin (1) -teor (1) +sgmoore (1) randy-waterhouse (1) plutoforever (1) murrayn (1) @@ -202,6 +204,7 @@ Josh Ellithorpe (1) Jonas Nick (1) Jesse Cohen (1) Jeffrey Walton (1) +Janito Vaqueiro Ferreira Filho (1) Jainan-Tandel (1) Igor Cota (1) Ian T (1) diff --git a/doc/release-notes.md b/doc/release-notes.md index a94a7919a17..a29094b5174 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,30 +4,3 @@ release-notes at release time) Notable changes =============== -Wallet ------- - -From this release, newly-created wallets will save the chain name ("Zcash") and -network identifier (e.g. "main") to the `wallet.dat` file. This will enable the -`zcashd` node to check on subsequent starts that the `wallet.dat` file matches -the node's configuration. Existing wallets will start saving this information in -a later release. - -`libzcash_script` ------------------ - -Two new APIs have been added to this library (`zcash_script_legacy_sigop_count` -and `zcash_script_legacy_sigop_count_precomputed`), for counting the number of -signature operations in the transparent inputs and outputs of a transaction. -The presence of these APIs is indicated by a library API version of 2. - -Updated RPCs ------------- - -- Fixed an issue where `ERROR: spent index not enabled` would be logged - unnecessarily on nodes that have either insightexplorer or lightwalletd - configuration options enabled. - -- The `getmininginfo` RPC now omits `currentblockize` and `currentblocktx` - when a block was never assembled via RPC on this node during its current - process instantiation. (#5404) diff --git a/doc/release-notes/release-notes-4.6.0.md b/doc/release-notes/release-notes-4.6.0.md new file mode 100644 index 00000000000..dc79367f367 --- /dev/null +++ b/doc/release-notes/release-notes-4.6.0.md @@ -0,0 +1,119 @@ +Notable changes +=============== + +Wallet +------ + +From this release, newly-created wallets will save the chain name ("Zcash") and +network identifier (e.g. "main") to the `wallet.dat` file. This will enable the +`zcashd` node to check on subsequent starts that the `wallet.dat` file matches +the node's configuration. Existing wallets will start saving this information in +a later release. + +`libzcash_script` +----------------- + +Two new APIs have been added to this library (`zcash_script_legacy_sigop_count` +and `zcash_script_legacy_sigop_count_precomputed`), for counting the number of +signature operations in the transparent inputs and outputs of a transaction. +The presence of these APIs is indicated by a library API version of 2. + +Updated RPCs +------------ + +- Fixed an issue where `ERROR: spent index not enabled` would be logged + unnecessarily on nodes that have either insightexplorer or lightwalletd + configuration options enabled. + +- The `getmininginfo` RPC now omits `currentblockize` and `currentblocktx` + when a block was never assembled via RPC on this node during its current + process instantiation. (#5404) + +Changelog +========= + +Alex Wied (1): + Update support for FreeBSD + +Charlie O'Keefe (2): + Add buster to the list of suites used by gitian + Add libtinfo5 to gitian packages list + +Dimitris Apostolou (1): + Fix typos + +Jack Grigg (23): + contrib: Update Debian copyright file to follow the v1 format + contrib: Add license information for libc++ and libevent + cargo update + tracing-subscriber 0.3 + Bump all postponed dependencies + depends: Update Rust to 1.56.1 + depends: Update Clang / libcxx to LLVM 13 + rust: Move `incremental_sinsemilla_tree_ffi` into crate root + CI: Add Pyflakes to lints workflow + cargo update + ed25519-zebra 3 + cargo update + cargo update + depends: Update Boost to 1.78.0 + depends Update Rust to 1.57.0 + qa: Postpone recent CCache releases + Revert "lint: Fix false positive" + rust: Remove misleading log message + Migrate to latest revisions of Zcash Rust crates + Update release notes + make-release.py: Versioning changes for 4.6.0-rc1. + make-release.py: Updated manpages for 4.6.0-rc1. + make-release.py: Updated release notes and changelog for 4.6.0-rc1. + +Janito Vaqueiro Ferreira Filho (1): + Move `CurrentTxVersionInfo` into a new file + +Kris Nuttycombe (7): + Add BIP 44 coin type to persisted wallet state. + Persist network id string instead of bip44 coin type. + Add a check to test that wallet load fails if we're on the wrong network. + Remove unused `AddDestData` method. + Fix wallet-related wording in doc/reduce-traffic.md + Rename OrchardMerkleTree -> OrchardMerkleFrontier + Batch-verify Orchard transactions at the block level. + +Larry Ruane (8): + add TestSetIBD(bool) for testing + Disable IBD for all boost tests + better wallet network info error handling + test: automatically add missing nuparams + Don't log 'ERROR: spent index not enabled' + getblocktemplate: add NU5 commitments to new `defaultroots` section + test: fix bugs in test framework + test: Use result of getblocktemplate to submitblock + +Marco Falke (1): + [rpc] mining: Omit uninitialized currentblockweight, currentblocktx + +Marshall Gaucher (1): + Update entrypoint.sh + +Sasha (5): + fix typo in docker run's volume argument + update hash for librustzcash + only librustzcash in config.offline + make-release.py: Versioning changes for 4.6.0. + make-release.py: Updated manpages for 4.6.0. + +sgmoore (1): + Update reduce-traffic.md - add one word + +Jack Grigg (1): + contrib: Add space between URL and period + +teor (7): + Add sigop count functions to zcash_script library + Increment the zcash_script API version + Remove an unused header + Use correct copyright header + Remove redundant variable + Explain UINT_MAX error return value + Explain how to get rid of a tiny duplicated function + From 25278c312a9fff58135c5470a1a180f51906c437 Mon Sep 17 00:00:00 2001 From: Sasha Date: Wed, 22 Dec 2021 17:36:39 -0800 Subject: [PATCH 212/514] str4d suggested changes to release-notes-4.6.0.md --- doc/release-notes/release-notes-4.6.0.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/release-notes/release-notes-4.6.0.md b/doc/release-notes/release-notes-4.6.0.md index dc79367f367..26e1f36527b 100644 --- a/doc/release-notes/release-notes-4.6.0.md +++ b/doc/release-notes/release-notes-4.6.0.md @@ -21,11 +21,18 @@ The presence of these APIs is indicated by a library API version of 2. Updated RPCs ------------ +- The `getblocktemplate` RPC method output now includes a `defaultroots` field, + which provides various tree roots and block commitments matching the contents + of the block template. If any part of the block template marked as `mutable` + in the RPC method output is mutated, these roots may need to be recomputed. + For more information on the derivation process, see the block header changes + in [ZIP 244](https://zips.z.cash/zip-0244#block-header-changes). + - Fixed an issue where `ERROR: spent index not enabled` would be logged - unnecessarily on nodes that have either insightexplorer or lightwalletd + unnecessarily on nodes that have either `-insightexplorer` or `-lightwalletd` configuration options enabled. -- The `getmininginfo` RPC now omits `currentblockize` and `currentblocktx` +- The `getmininginfo` RPC now omits `currentblocksize` and `currentblocktx` when a block was never assembled via RPC on this node during its current process instantiation. (#5404) From 74e4bef6f2d107cc05ce3a9afff48c5eb17b2fcd Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 22 Dec 2021 18:20:53 -0700 Subject: [PATCH 213/514] Ensure that unified address metadata is always correctly populated. This reworks the way that address metadata is added to the wallet to make adding address metadata to the wallet's in-memory cache the responsibility of a single method rather than distributed across multiple places in the code. --- src/gtest/test_keystore.cpp | 2 +- src/keystore.cpp | 13 ++- src/keystore.h | 15 +-- src/util/match.h | 24 +++++ src/wallet/gtest/test_wallet.cpp | 18 ++-- src/wallet/test/rpc_wallet_tests.cpp | 1 - src/wallet/wallet.cpp | 137 ++++++++++++++------------- src/wallet/wallet.h | 37 +++++--- src/wallet/walletdb.cpp | 22 +++-- src/wallet/walletdb.h | 12 +-- src/zcash/Address.hpp | 21 ++++ 11 files changed, 186 insertions(+), 116 deletions(-) create mode 100644 src/util/match.h diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 5b21d15bb42..21c4282626f 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -560,7 +560,7 @@ TEST(KeystoreTests, AddUnifiedAddress) { auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); EXPECT_TRUE(addrPair.first.GetP2PKHReceiver().has_value()); - keyStore.AddUnifiedAddress(ufvkid, addrPair); + keyStore.AddUnifiedAddress(ufvkid, addrPair.second, addrPair.first); auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value()); EXPECT_TRUE(ufvkmeta.has_value()); diff --git a/src/keystore.cpp b/src/keystore.cpp index 169a0e97ce6..53817a0b79b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -316,9 +316,10 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( return true; } -void CBasicKeyStore::AddUnifiedAddress( +bool CBasicKeyStore::AddUnifiedAddress( const libzcash::UFVKId& keyId, - const std::pair& ua) + const libzcash::diversifier_index_t& diversifierIndex, + const libzcash::UnifiedAddress& ua) { LOCK(cs_KeyStore); @@ -326,17 +327,19 @@ void CBasicKeyStore::AddUnifiedAddress( // the UA; all other lookups of the associated UFVK will be // made via the protocol-specific viewing key that is used // to trial-decrypt a transaction. - auto addrEntry = std::make_pair(keyId, ua.second); + auto addrEntry = std::make_pair(keyId, diversifierIndex); - auto p2pkhReceiver = ua.first.GetP2PKHReceiver(); + auto p2pkhReceiver = ua.GetP2PKHReceiver(); if (p2pkhReceiver.has_value()) { mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), addrEntry)); } - auto p2shReceiver = ua.first.GetP2SHReceiver(); + auto p2shReceiver = ua.GetP2SHReceiver(); if (p2shReceiver.has_value()) { mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), addrEntry)); } + + return true; } std::optional CBasicKeyStore::GetUnifiedFullViewingKey( diff --git a/src/keystore.h b/src/keystore.h index 140baf7a963..d3e1c6c761c 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -117,10 +117,10 @@ class CKeyStore * viewing key upon discovery of the address as having received * funds. */ - virtual void AddUnifiedAddress( - const libzcash::UFVKId& keyId, - const std::pair& ua - ) = 0; + virtual bool AddUnifiedAddress( + const libzcash::UFVKId& keyId, + const libzcash::diversifier_index_t& diversifierIndex, + const libzcash::UnifiedAddress& ua) = 0; virtual std::optional GetUnifiedFullViewingKey( const libzcash::UFVKId& keyId @@ -357,9 +357,10 @@ class CBasicKeyStore : public CKeyStore virtual bool AddUnifiedFullViewingKey( const libzcash::ZcashdUnifiedFullViewingKey &ufvk); - virtual void AddUnifiedAddress( - const libzcash::UFVKId& keyId, - const std::pair& ua); + virtual bool AddUnifiedAddress( + const libzcash::UFVKId& keyId, + const libzcash::diversifier_index_t& diversifierIndex, + const libzcash::UnifiedAddress& ua); virtual std::optional GetUnifiedFullViewingKey( const libzcash::UFVKId& keyId) const; diff --git a/src/util/match.h b/src/util/match.h new file mode 100644 index 00000000000..d966ae4dac3 --- /dev/null +++ b/src/util/match.h @@ -0,0 +1,24 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_UTIL_MATCH_H +#define ZCASH_UTIL_MATCH_H + +// Helper for using `std::visit` with Rust-style match syntax. +// +// This can be used in place of defining an explicit visitor. `std::visit` requires an +// exhaustive match; to emulate Rust's catch-all binding, use an `(auto arg)` template +// operator(). +// +// Care must be taken that implicit conversions are handled correctly. For instance, a +// `(double arg)` operator() *will also* bind to `int` and `long`, if there isn't an +// earlier satisfying match. +// +// This corresponds to visitor example #4 in the `std::visit` documentation: +// https://en.cppreference.com/w/cpp/utility/variant/visit +template struct match : Ts... { using Ts::operator()...; }; +// explicit deduction guide (not needed as of C++20) +template match(Ts...) -> match; + +#endif // ZCASH_UTIL_MATCH_H diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 3e9349c7c5d..f0e6dce65cd 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -2199,19 +2199,19 @@ TEST(WalletTests, GenerateUnifiedAddress) { // Create an account, then generate an address for that account. auto skpair = wallet.GenerateNewUnifiedSpendingKey(); uaResult = wallet.GenerateUnifiedAddress( - skpair.second.GetAccountId(), + skpair.second, diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); - bool success = std::holds_alternative>(uaResult); - EXPECT_TRUE(success); + auto ua = std::get_if>(&uaResult); + EXPECT_NE(ua, nullptr); + + EXPECT_TRUE(ua->first.GetSaplingReceiver().has_value()); auto ufvk = skpair.first.ToFullViewingKey(); - auto addrpair = std::get>(uaResult); - EXPECT_TRUE(addrpair.first.GetSaplingReceiver().has_value()); EXPECT_EQ( - addrpair.first.GetSaplingReceiver(), - ufvk.GetSaplingKey().value().Address(addrpair.second.GetDiversifierIndex())); + ua->first.GetSaplingReceiver(), + ufvk.GetSaplingKey().value().Address(ua->second)); - auto u4r = wallet.GetUnifiedForReceiver(addrpair.first.GetSaplingReceiver().value()); - EXPECT_EQ(u4r, addrpair.first); + auto u4r = wallet.GetUnifiedForReceiver(ua->first.GetSaplingReceiver().value()); + EXPECT_EQ(u4r, ua->first); } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 307f5cc3167..0528ab0e654 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -79,7 +79,6 @@ static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) { auto usk = pwallet->GenerateUnifiedSpendingKeyForAccount(0); return usk.value() - .first .ToFullViewingKey() .GetSaplingKey().value() .FindAddress(libzcash::diversifier_index_t(0)).first; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bd31486b7d6..eab370f2131 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -421,7 +421,7 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi return false; } -std::pair CWallet::GenerateNewUnifiedSpendingKey() { +std::pair CWallet::GenerateNewUnifiedSpendingKey() { AssertLockHeld(cs_wallet); if (!mnemonicHDChain.has_value()) { @@ -431,7 +431,8 @@ std::pair CWallet::GenerateNewUn CHDChain& hdChain = mnemonicHDChain.value(); while (true) { - auto usk = GenerateUnifiedSpendingKeyForAccount(hdChain.GetAccountCounter()); + auto accountId = hdChain.GetAccountCounter(); + auto usk = GenerateUnifiedSpendingKeyForAccount(accountId); hdChain.IncrementAccountCounter(); if (usk.has_value()) { @@ -441,14 +442,14 @@ std::pair CWallet::GenerateNewUn "CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed"); } - return usk.value(); + return std::make_pair(usk.value(), accountId); } } } -std::optional> +std::optional CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) { - AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta + AssertLockHeld(cs_wallet); // mapUnifiedAccountKeys auto seed = GetMnemonicSeed(); if (!seed.has_value()) { @@ -457,11 +458,10 @@ std::optional CWallet::GetUnifiedFullViewingKeyByAc } auto seedfp = mnemonicHDChain.value().GetSeedFingerprint(); - auto i = mapUnifiedSpendingKeyMeta.find(std::make_pair(seedfp, accountId)); - if (i != mapUnifiedSpendingKeyMeta.end()) { - auto keyId = i->second.GetKeyID(); - return CCryptoKeyStore::GetUnifiedFullViewingKey(keyId); + auto entry = mapUnifiedAccountKeys.find(std::make_pair(seedfp, accountId)); + if (entry != mapUnifiedAccountKeys.end()) { + return CCryptoKeyStore::GetUnifiedFullViewingKey(entry->second); } else { return std::nullopt; } @@ -571,37 +570,42 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( } } - auto identifiedKey = GetUnifiedFullViewingKeyByAccount(accountId); - if (identifiedKey.has_value()) { - auto ufvk = identifiedKey.value(); + auto ufvk = GetUnifiedFullViewingKeyByAccount(accountId); + if (ufvk.has_value()) { + auto ufvkid = ufvk.value().GetKeyID(); // Check whether an address has already been generated for this // diversifier index. If so, ensure that the set of receiver types // being requested is the same as the set of receiver types that was - // previously generated; if so, return the previously generated address, + // previously generated & return the previously generated address, // otherwise return an error. - auto metadata = mapUnifiedFullViewingKeyMeta.find(ufvk.GetKeyID()); - if (metadata != mapUnifiedFullViewingKeyMeta.end()) { + auto metadata = mapUfvkAddressMetadata.find(ufvkid); + if (metadata != mapUfvkAddressMetadata.end()) { auto receivers = metadata->second.GetReceivers(j); if (receivers.has_value()) { if (receivers.value() == receiverTypes) { - ZcashdUnifiedAddressMetadata addrmeta(ufvk.GetKeyID(), j, receiverTypes); - auto addr = ufvk.Address(j, receiverTypes); - return std::make_pair(addr.value(), addrmeta); + return std::make_pair(ufvk.value().Address(j, receiverTypes).value(), j); } else { return AddressGenerationError::ExistingAddressMismatch; } } - } else { - mapUnifiedFullViewingKeyMeta[ufvk.GetKeyID()] = ZcashdUnifiedFullViewingKeyMetadata(accountId); } // Find a working diversifier and construct the associated address. - auto foundAddress = ufvk.FindAddress(j, receiverTypes); + auto foundAddress = ufvk.value().FindAddress(j, receiverTypes); auto diversifierIndex = foundAddress.second; // Persist the newly created address to the keystore - AddUnifiedAddress(ufvk.GetKeyID(), foundAddress); + mapUfvkAddressMetadata[ufvkid].SetReceivers(diversifierIndex, receiverTypes); + CCryptoKeyStore::AddUnifiedAddress(ufvkid, diversifierIndex, foundAddress.first); + + // Save the metadata for the generated address so that we can re-derive + // it in the future. + ZcashdUnifiedAddressMetadata addrmeta(ufvkid, diversifierIndex, receiverTypes); + if (fFileBacked && !CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta)) { + throw std::runtime_error( + "CWallet::AdddUnifiedAddress(): Writing unified address metadata failed"); + } if (hasTransparent) { // Regenerate the secret key for the transparent address and add it to @@ -612,65 +616,66 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( AddTransparentSecretKey(seed.Fingerprint(), key); } - // Save the metadata for the generated address so that we can re-derive - // it in the future. - ZcashdUnifiedAddressMetadata addrmeta(ufvk.GetKeyID(), diversifierIndex, receiverTypes); - - // we can safely ignore the return value here; we know that we're adding a new - // set of receivers given the receivers.has_value() check above. - mapUnifiedFullViewingKeyMeta[ufvk.GetKeyID()].SetReceivers(diversifierIndex, receiverTypes); - if (fFileBacked) { - CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta); - } - return std::make_pair(foundAddress.first, addrmeta); + return foundAddress; } else { return AddressGenerationError::NoSuchAccount; } } +// NOTE: There is an important relationship between `LoadUnifiedFullViewingKey` +// and `LoadUnifiedAddressMetadata`. In all cases, by the end of the +// wallet-loading process, `mapUfvkAddressMetadata` needs to be fully populated +// for each unified full viewing key. However, we cannot guarantee in what +// order UFVKs and unified address and account metadata are loaded from disk. +// Therefore: +// * When we load a unified full viewing key from disk, we repopulate +// the cache of generated addresses by regenerating the address for each +// (diversifierIndex, {receiverType...}) pair in `mapUfvkAddressMetadata` +// that we have loaded so far. +// * When we load a unified address metadata record from disk: +// - if we have not yet loaded the unified full viewing key, simply add +// an entry `mapUfvkAddressMetadata` so that we can restore the address +// once the UFVK has been loaded. +// - if we have already loaded the unified full viewing key from disk, +// regenerate the address from its metadata and add it to the keystore bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key) { auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key); - auto metadata = mapUnifiedFullViewingKeyMeta.find(zufvk.GetKeyID()); - if (metadata != mapUnifiedFullViewingKeyMeta.end()) { + auto metadata = mapUfvkAddressMetadata.find(zufvk.GetKeyID()); + if (metadata != mapUfvkAddressMetadata.end()) { // restore unified addresses that have been previously generated to the // keystore for (const auto &[j, receiverTypes] : metadata->second.GetAllReceivers()) { auto addr = zufvk.Address(j, receiverTypes).value(); - AddUnifiedAddress(zufvk.GetKeyID(), std::make_pair(addr, j)); + CCryptoKeyStore::AddUnifiedAddress(zufvk.GetKeyID(), j, addr); } } return CCryptoKeyStore::AddUnifiedFullViewingKey(zufvk); } -bool CWallet::LoadUnifiedAccount(const ZcashdUnifiedAccount &skmeta) +bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta) { - AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta + AssertLockHeld(cs_wallet); // mapUnifiedAccountKeys auto metaKey = std::make_pair(skmeta.GetSeedFingerprint(), skmeta.GetAccountId()); - mapUnifiedSpendingKeyMeta.insert({metaKey, skmeta}); - if (mapUnifiedFullViewingKeyMeta.count(skmeta.GetKeyID()) == 0) { - return mapUnifiedFullViewingKeyMeta[skmeta.GetKeyID()].SetAccountId(skmeta.GetAccountId()); - } - - return true; + mapUnifiedAccountKeys.insert({metaKey, skmeta.GetKeyID()}); + return mapUfvkAddressMetadata[skmeta.GetKeyID()].SetAccountId(skmeta.GetAccountId()); } bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta) { - AssertLockHeld(cs_wallet); // mapUnifiedSpendingKeyMeta + AssertLockHeld(cs_wallet); + mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers( + addrmeta.GetDiversifierIndex(), + addrmeta.GetReceiverTypes()); + auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID()); if (ufvk.has_value()) { - // restore unified addresses that have been previously generated + // Regenerate the unified address and add it to the keystore. auto j = addrmeta.GetDiversifierIndex(); - auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes()); - if (addr.has_value()) { - AddUnifiedAddress(addrmeta.GetKeyID(), std::make_pair(addr.value(), j)); - } else { - // an error has occurred; the ufvk is loaded but cannot reproduce the - // address identified by the address metadata. - return false; - } + auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes()).value(); + return CCryptoKeyStore::AddUnifiedAddress(addrmeta.GetKeyID(), j, addr); } + return true; } @@ -5811,8 +5816,8 @@ std::optional LookupUnifiedAddress::operator()(const l } diversifier_index_t j; - auto metadata = wallet.mapUnifiedFullViewingKeyMeta.find(ufvkid); - if (metadata != wallet.mapUnifiedFullViewingKeyMeta.end()) { + auto metadata = wallet.mapUfvkAddressMetadata.find(ufvkid); + if (metadata != wallet.mapUfvkAddressMetadata.end()) { librustzcash_sapling_diversifier_index( ufvk.value().GetSaplingKey().value().dk.begin(), saplingAddr.d.begin(), @@ -5851,8 +5856,8 @@ std::optional LookupUnifiedAddress::operator()(const C // Find the set of receivers at the diversifier index. If no metadata is available // for the ufvk, or we do not know the receiver types for the address produced // at this diversifier, we cannot reconstruct the address. - auto metadata = wallet.mapUnifiedFullViewingKeyMeta.find(ufvkid); - if (metadata != wallet.mapUnifiedFullViewingKeyMeta.end()) { + auto metadata = wallet.mapUfvkAddressMetadata.find(ufvkid); + if (metadata != wallet.mapUfvkAddressMetadata.end()) { auto receivers = metadata->second.GetReceivers(j); if (receivers.has_value()) { return ufvk.value().Address(j, receivers.value()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 5f5e440893c..cb8f825854f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -412,7 +412,7 @@ enum class AddressGenerationError { }; typedef std::variant< - std::pair, + std::pair, AddressGenerationError> UAGenerationResult; /** @@ -681,14 +681,14 @@ class CWalletKey } }; -class ZcashdUnifiedFullViewingKeyMetadata +class UFVKAddressMetadata { private: std::optional accountId; std::map> addressReceivers; public: - ZcashdUnifiedFullViewingKeyMetadata() {} - ZcashdUnifiedFullViewingKeyMetadata(libzcash::AccountId accountId): accountId(accountId) {} + UFVKAddressMetadata() {} + UFVKAddressMetadata(libzcash::AccountId accountId): accountId(accountId) {} const std::map>& GetAllReceivers() const { return addressReceivers; @@ -704,10 +704,21 @@ class ZcashdUnifiedFullViewingKeyMetadata } } + /** + * Add the specified set of receivers at the provided diversifier index. + * + * Returns `true` if this is a new entry or if the operation would not + * alter the existing set of receiver types at this index, `false` + * otherwise. + */ bool SetReceivers( const libzcash::diversifier_index_t& j, const std::set& receivers) { - return addressReceivers.insert(std::make_pair(j, receivers)).second; + if (addressReceivers.count(j) > 0) { + return addressReceivers[j] == receivers; + } else { + return addressReceivers.insert(std::make_pair(j, receivers)).second; + } } bool SetAccountId(libzcash::AccountId accountIdIn) { @@ -888,8 +899,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapSproutZKeyMetadata; std::map mapSaplingZKeyMetadata; - std::map, ZcashdUnifiedAccount> mapUnifiedSpendingKeyMeta; - std::map mapUnifiedFullViewingKeyMeta; + std::map, libzcash::UFVKId> mapUnifiedAccountKeys; + std::map mapUfvkAddressMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -1164,12 +1175,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Generate the unified spending key from the wallet's mnemonic seed //! for the next unused account identifier. - std::pair + std::pair GenerateNewUnifiedSpendingKey(); //! Generate the next available unified spending key from the wallet's //! mnemonic seed. - std::optional> + std::optional GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); //! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account. @@ -1183,14 +1194,14 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface const libzcash::diversifier_index_t& j, const std::set& receivers); - std::optional GetUnifiedForReceiver(const libzcash::Receiver& receiver); - bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); - bool LoadUnifiedAccount(const ZcashdUnifiedAccount &skmeta); + bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); + bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta); bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta); + std::optional GetUnifiedForReceiver(const libzcash::Receiver& receiver); + /** * Increment the next transaction order id * @return next transaction order id diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 712227c47df..0044673728c 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -221,10 +221,9 @@ bool CWalletDB::EraseSaplingExtendedFullViewingKey( // Unified address & key storage // -bool CWalletDB::WriteUnifiedAccount(const ZcashdUnifiedAccount& keymeta) +bool CWalletDB::WriteUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata& keymeta) { nWalletDBUpdateCounter++; - auto ufvkId = keymeta.GetKeyID(); return Write(std::make_pair(std::string("unifiedaccount"), keymeta), 0x00); } @@ -238,8 +237,7 @@ bool CWalletDB::WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey bool CWalletDB::WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta) { nWalletDBUpdateCounter++; - auto ufvkId = addrmeta.GetKeyID(); - return Write(std::make_pair(std::string("unifiedaddrmeta"), ufvkId), addrmeta); + return Write(std::make_pair(std::string("unifiedaddrmeta"), addrmeta), 0x00); } // @@ -688,23 +686,31 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "unifiedaccount") { - auto acct = ZcashdUnifiedAccount::Read(ssKey); + auto acct = ZcashdUnifiedAccountMetadata::Read(ssKey); uint8_t value; ssValue >> value; if (value != 0x00) { - strErr = "Error reading wallet database: invalid unified account value."; + strErr = "Error reading wallet database: invalid unified account metadata."; return false; } - if (!pwallet->LoadUnifiedAccount(acct)) { + if (!pwallet->LoadUnifiedAccountMetadata(acct)) { strErr = "Error reading wallet database: account ID mismatch for unified spending key."; return false; }; } else if (strType == "unifiedaddrmeta") { - auto keymeta = ZcashdUnifiedAddressMetadata::Read(ssValue); + auto keymeta = ZcashdUnifiedAddressMetadata::Read(ssKey); + + uint8_t value; + ssValue >> value; + if (value != 0x00) { + strErr = "Error reading wallet database: invalid unified address metadata."; + return false; + } + if (!pwallet->LoadUnifiedAddressMetadata(keymeta)) { strErr = "Error reading wallet database: cannot reproduce previously generated unified address."; return false; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index e3928d79851..8fc72d01f9a 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -172,16 +172,16 @@ class CKeyMetadata } }; -class ZcashdUnifiedAccount { +class ZcashdUnifiedAccountMetadata { private: libzcash::SeedFingerprint seedFp; uint32_t bip44CoinType; libzcash::AccountId accountId; libzcash::UFVKId ufvkId; - ZcashdUnifiedAccount() {} + ZcashdUnifiedAccountMetadata() {} public: - ZcashdUnifiedAccount( + ZcashdUnifiedAccountMetadata( libzcash::SeedFingerprint seedFp, uint32_t bip44CoinType, libzcash::AccountId accountId, @@ -216,8 +216,8 @@ class ZcashdUnifiedAccount { } template - static ZcashdUnifiedAccount Read(Stream& stream) { - ZcashdUnifiedAccount meta; + static ZcashdUnifiedAccountMetadata Read(Stream& stream) { + ZcashdUnifiedAccountMetadata meta; stream >> meta; return meta; } @@ -386,7 +386,7 @@ class CWalletDB : public CDB /// Unified key support. - bool WriteUnifiedAccount(const ZcashdUnifiedAccount& keymeta); + bool WriteUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata& keymeta); bool WriteUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey& ufvk); bool WriteUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata& addrmeta); diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 23326059b2a..e690fdf2e62 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -6,6 +6,7 @@ #include "key_constants.h" #include "script/script.h" #include "uint256.h" +#include "util/match.h" #include "zcash/address/orchard.hpp" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" @@ -116,6 +117,26 @@ class UnifiedAddress { const std::vector& GetReceiversAsParsed() const { return receivers; } + std::set GetKnownReceiverTypes() const { + std::set result; + for (const auto& receiver : receivers) { + std::visit(match { + [&](const libzcash::SaplingPaymentAddress &zaddr) { + result.insert(ReceiverType::Sapling); + }, + [&](const CScriptID &zaddr) { + result.insert(ReceiverType::P2SH); + }, + [&](const CKeyID &zaddr) { + result.insert(ReceiverType::P2PKH); + }, + [&](const libzcash::UnknownReceiver &uaddr) { + } + }, receiver); + } + return result; + } + ReceiverIterator begin() const { return ReceiverIterator(GetSorted(), 0); } From bf1cc206c0a359c0ac2e94af34f689a68fb40285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Kj=C3=A6rstad?= Date: Mon, 27 Dec 2021 00:30:51 +0100 Subject: [PATCH 214/514] Update copyright year to 2022 --- COPYING | 6 +++--- configure.ac | 2 +- contrib/debian/copyright | 6 +++--- src/clientversion.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/COPYING b/COPYING index d7c52f033f3..e6e8c782da6 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ -Copyright (c) 2016-2021 The Zcash developers -Copyright (c) 2009-2021 The Bitcoin Core developers -Copyright (c) 2009-2021 Bitcoin Developers +Copyright (c) 2016-2022 The Zcash developers +Copyright (c) 2009-2022 The Bitcoin Core developers +Copyright (c) 2009-2022 Bitcoin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/configure.ac b/configure.ac index d2dbb6ae7e5..25a7a45a704 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ 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))) define(_CLIENT_VERSION_IS_RELEASE, true) -define(_COPYRIGHT_YEAR, 2021) +define(_COPYRIGHT_YEAR, 2022) AC_INIT([Zcash],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://github.com/zcash/zcash/issues],[zcash]) AC_CONFIG_SRCDIR([src/main.cpp]) AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index dda7a253469..8e1b2d83659 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -4,9 +4,9 @@ Upstream-Contact: Electric Coin Company Source: https://github.com/zcash/zcash Files: * -Copyright: 2016-2021, The Zcash developers - 2009-2021, Bitcoin Core developers - 2009-2021, Bitcoin Developers +Copyright: 2016-2022, The Zcash developers + 2009-2022, Bitcoin Core developers + 2009-2022, Bitcoin Developers License: Expat Comment: The Bitcoin Core developers encompasses the current developers listed on bitcoin.org, as well as the numerous contributors to the project. diff --git a/src/clientversion.h b/src/clientversion.h index 8bff582b679..eb2a72f6680 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2016-2021 The Zcash developers +// Copyright (c) 2016-2022 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -27,7 +27,7 @@ * Copyright year (2009-this) * Todo: update this when changing our copyright comments in the source */ -#define COPYRIGHT_YEAR 2021 +#define COPYRIGHT_YEAR 2022 #endif //HAVE_CONFIG_H From 1f223ebed7a931dcd3ac30050c508987c2b4b3fc Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 28 Dec 2021 22:10:13 -0700 Subject: [PATCH 215/514] assert that the return value of submitblock is None --- qa/rpc-tests/getblocktemplate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index fff46296181..324e4e43169 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -98,7 +98,8 @@ def run_test(self): block = codecs.encode(block, 'hex_codec') print("Submitting block") - node.submitblock(block) + submitblock_reply = node.submitblock(block) + assert_equal(None, submitblock_reply) if __name__ == '__main__': From 56b706d5c739170848c2e06324ff9bb5a55ee5e6 Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Wed, 29 Dec 2021 21:22:19 -0800 Subject: [PATCH 216/514] Lock cs_main prior to calling blockToJSON --- src/gtest/test_rpc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gtest/test_rpc.cpp b/src/gtest/test_rpc.cpp index 405855c856b..bf2f0f09018 100644 --- a/src/gtest/test_rpc.cpp +++ b/src/gtest/test_rpc.cpp @@ -4,6 +4,7 @@ #include "chain.h" #include "chainparams.h" #include "clientversion.h" +#include "main.h" #include "primitives/block.h" #include "rpc/server.h" #include "streams.h" @@ -23,6 +24,7 @@ TEST(rpc, CheckBlockToJSONReturnsMinifiedSolution) { CBlockIndex index {block}; index.nHeight = 1391; + LOCK(cs_main); UniValue obj = blockToJSON(block, &index); EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", find_value(obj, "solution").get_str()); } From 5d07a8ae79065ccad46a1c7dd642a7a590b10952 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 29 Dec 2021 09:12:44 -0700 Subject: [PATCH 217/514] Apply suggestions from code review Co-authored-by: Daira Hopwood --- src/keystore.cpp | 2 +- src/rust/include/librustzcash.h | 2 +- src/wallet/gtest/test_wallet.cpp | 10 ++-------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index 53817a0b79b..487c1ec2aa3 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -306,7 +306,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( // We can't reasonably add the transparent component here, because // of the way that transparent addresses are generated from the - // P2PHK part of the unified address. Instead, whenever a new + // P2PKH part of the unified address. Instead, whenever a new // unified address is generated, the keys associated with the // transparent part of the address must be added to the keystore. diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index b10e3e38052..43b31031a87 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -355,7 +355,7 @@ extern "C" { /** * Decrypts a Sapling diversifier using the specified diversifier key - * to obtain the diversifier index `j` at which the diversivier was + * to obtain the diversifier index `j` at which the diversifier was * derived. * * Arguments: diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index f0e6dce65cd..5908584f8d4 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -184,7 +184,7 @@ TEST(WalletTests, SproutNoteDataSerialisation) { TEST(WalletTests, FindUnspentSproutNotes) { - auto consensusParams = RegtestActivateSapling(); + SelectParams(CBaseChainParams::TESTNET); CWallet wallet(Params()); LOCK2(cs_main, wallet.cs_wallet); @@ -379,8 +379,6 @@ TEST(WalletTests, SetSproutNoteAddrsInCWalletTx) { } TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) { - SelectParams(CBaseChainParams::REGTEST); - std::vector zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212}; const Consensus::Params& (*activations [])() = {RegtestActivateSapling, RegtestActivateCanopy}; void (*deactivations [])() = {RegtestDeactivateSapling, RegtestDeactivateCanopy}; @@ -659,8 +657,6 @@ TEST(WalletTests, GetConflictedSproutNotes) { // Generate note A and spend to create note B, from which we spend to create two conflicting transactions TEST(WalletTests, GetConflictedSaplingNotes) { - SelectParams(CBaseChainParams::REGTEST); - std::vector zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212}; const Consensus::Params& (*activations [])() = {RegtestActivateSapling, RegtestActivateCanopy}; void (*deactivations [])() = {RegtestDeactivateSapling, RegtestDeactivateCanopy}; @@ -1036,8 +1032,6 @@ TEST(WalletTests, SpentSproutNoteIsFromMe) { // Create note A, spend A to create note B, spend and verify note B is from me. TEST(WalletTests, SpentSaplingNoteIsFromMe) { - SelectParams(CBaseChainParams::REGTEST); - std::vector zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212}; const Consensus::Params& (*activations [])() = {RegtestActivateSapling, RegtestActivateCanopy}; void (*deactivations [])() = {RegtestDeactivateSapling, RegtestDeactivateCanopy}; @@ -2173,7 +2167,7 @@ TEST(WalletTests, SaplingNoteLocking) { } TEST(WalletTests, GenerateUnifiedAddress) { - SelectParams(CBaseChainParams::TESTNET); + (void) RegtestActivateSapling(); TestWallet wallet(Params()); UAGenerationResult uaResult = wallet.GenerateUnifiedAddress(0, diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); From 2006939d27390dfb4294b09f7dd0cf015e5fe397 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sun, 2 Jan 2022 23:16:01 +0200 Subject: [PATCH 218/514] Fix typos --- qa/rpc-tests/wallet_shieldcoinbase.py | 2 +- qa/zcash/smoke_tests.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/wallet_shieldcoinbase.py b/qa/rpc-tests/wallet_shieldcoinbase.py index ccb1e68a8e9..02b69a85523 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase.py +++ b/qa/rpc-tests/wallet_shieldcoinbase.py @@ -151,7 +151,7 @@ def verify_locking(first, second, limit): assert_equal(result["remainingUTXOs"], Decimal('0')) opid2 = result['opid'] - # wait for both aysnc operations to complete + # wait for both async operations to complete wait_and_assert_operationid_status(self.nodes[0], opid1) wait_and_assert_operationid_status(self.nodes[0], opid2) diff --git a/qa/zcash/smoke_tests.py b/qa/zcash/smoke_tests.py index 5d214b20849..6d3749ab403 100755 --- a/qa/zcash/smoke_tests.py +++ b/qa/zcash/smoke_tests.py @@ -172,7 +172,7 @@ def wait_for_balance(zcash, zaddr, expected=None): ttl = 300 def wait_and_check_balance(results, case, zcash, addr, expected): - #Wait for aysnc call to finish and persist completely to caller + #Wait for async call to finish and persist completely to caller time.sleep(5) balance = wait_for_balance(zcash, addr, expected) if balance != expected and results is not None and len(case) > 0: From 16ba83ab1e125cf6f36ed08bcd102280bb3a9862 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 30 Dec 2021 09:53:55 -0700 Subject: [PATCH 219/514] Make `FindAddress` correctly check for maximum transparent child index. ZcashdUnifiedFullViewingKey::FindAddress was previously not checking that the transparent child index did not overflow in unified address generation. GenerateUnifiedAddress has been modified to search from the last known diversifier index if no diversifier is specified, and boundary conditions for transparent addresses are now correctly handled. --- src/gtest/test_keystore.cpp | 4 +- src/wallet/gtest/test_wallet.cpp | 36 ++++++++++++--- src/wallet/wallet.cpp | 77 ++++++++++++++++++++++++-------- src/wallet/wallet.h | 30 +++++++++++-- src/zcash/Address.cpp | 1 + src/zcash/address/unified.cpp | 19 +++++--- src/zcash/address/unified.h | 18 ++++++-- src/zcash/address/zip32.cpp | 2 - src/zcash/address/zip32.h | 12 +++++ 9 files changed, 160 insertions(+), 39 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 21c4282626f..e8b4abb72c9 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -533,7 +533,7 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { EXPECT_TRUE(keyStore.AddUnifiedFullViewingKey(zufvk)); EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk); - auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}); + auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}).value(); EXPECT_TRUE(addrPair.first.GetSaplingReceiver().has_value()); auto saplingReceiver = addrPair.first.GetSaplingReceiver().value(); @@ -557,7 +557,7 @@ TEST(KeystoreTests, AddUnifiedAddress) { auto ufvk = usk.value().ToFullViewingKey(); auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), ufvk); auto ufvkid = zufvk.GetKeyID(); - auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); + auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}).value(); EXPECT_TRUE(addrPair.first.GetP2PKHReceiver().has_value()); keyStore.AddUnifiedAddress(ufvkid, addrPair.second, addrPair.first); diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 5908584f8d4..3f758550a36 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -2170,7 +2170,7 @@ TEST(WalletTests, GenerateUnifiedAddress) { (void) RegtestActivateSapling(); TestWallet wallet(Params()); - UAGenerationResult uaResult = wallet.GenerateUnifiedAddress(0, diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); + UAGenerationResult uaResult = wallet.GenerateUnifiedAddress(0, {ReceiverType::P2PKH, ReceiverType::Sapling}); // If the wallet does not have a mnemonic seed available, it is // treated as if the wallet is encrypted. @@ -2186,16 +2186,13 @@ TEST(WalletTests, GenerateUnifiedAddress) { // If the user has not generated a unified spending key, // we cannot create an address for the account corresponding // to that spending key. - uaResult = wallet.GenerateUnifiedAddress(0, diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}); + uaResult = wallet.GenerateUnifiedAddress(0, {ReceiverType::P2PKH, ReceiverType::Sapling}); expected = AddressGenerationError::NoSuchAccount; EXPECT_EQ(uaResult, expected); // Create an account, then generate an address for that account. auto skpair = wallet.GenerateNewUnifiedSpendingKey(); - uaResult = wallet.GenerateUnifiedAddress( - skpair.second, - diversifier_index_t(0), - {ReceiverType::P2PKH, ReceiverType::Sapling}); + uaResult = wallet.GenerateUnifiedAddress(skpair.second, {ReceiverType::P2PKH, ReceiverType::Sapling}); auto ua = std::get_if>(&uaResult); EXPECT_NE(ua, nullptr); @@ -2208,4 +2205,31 @@ TEST(WalletTests, GenerateUnifiedAddress) { auto u4r = wallet.GetUnifiedForReceiver(ua->first.GetSaplingReceiver().value()); EXPECT_EQ(u4r, ua->first); + + // Explicitly trigger the invalid transparent child index failure + uaResult = wallet.GenerateUnifiedAddress( + 0, + {ReceiverType::P2PKH, ReceiverType::Sapling}, + MAX_TRANSPARENT_CHILD_IDX.succ().value()); + expected = AddressGenerationError::InvalidTransparentChildIndex; + EXPECT_EQ(uaResult, expected); + + // Attempt to generate a UA at the maximum transparent child index. This might fail + // due to this index being invalid for Sapling; if so, it will return an error that + // the diversifier index is out of range. If it succeeds, we'll attempt to generate + // the next available diversifier, and this should always fail + uaResult = wallet.GenerateUnifiedAddress( + 0, + {ReceiverType::P2PKH, ReceiverType::Sapling}, + MAX_TRANSPARENT_CHILD_IDX); + ua = std::get_if>(&uaResult); + if (ua == nullptr) { + expected = AddressGenerationError::NoAddressForDiversifier; + EXPECT_EQ(uaResult, expected); + } else { + // the previous generation attempt succeeded, so this one should definitely fail. + uaResult = wallet.GenerateUnifiedAddress(0, {ReceiverType::P2PKH, ReceiverType::Sapling}); + expected = AddressGenerationError::DiversifierSpaceExhausted; + EXPECT_EQ(uaResult, expected); + } } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index eab370f2131..77fcee2c622 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -547,9 +547,10 @@ std::optional CWallet::GetUnifiedFullViewingKeyByAc UAGenerationResult CWallet::GenerateUnifiedAddress( const libzcash::AccountId& accountId, - const libzcash::diversifier_index_t& j, - const std::set& receiverTypes) + const std::set& receiverTypes, + std::optional j) { + bool searchDiversifiers = !j.has_value(); if (!libzcash::HasShielded(receiverTypes)) { return AddressGenerationError::InvalidReceiverTypes; } @@ -561,7 +562,11 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // ours rather than considering them as watch-only. bool hasTransparent = receiverTypes.find(ReceiverType::P2PKH) != receiverTypes.end(); if (hasTransparent) { - if (!j.ToTransparentChildIndex().has_value()) { + // A preemptive check to ensure that the user has not specified an + // invalid transparent child index. If we search from a valid transparent + // child index into invalid child index space, later checks will return + // this error as `AddressGenerationError::DiversifierSpaceExhausted` + if (j.has_value() && !j.value().ToTransparentChildIndex().has_value()) { return AddressGenerationError::InvalidTransparentChildIndex; } @@ -574,30 +579,66 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( if (ufvk.has_value()) { auto ufvkid = ufvk.value().GetKeyID(); - // Check whether an address has already been generated for this - // diversifier index. If so, ensure that the set of receiver types - // being requested is the same as the set of receiver types that was - // previously generated & return the previously generated address, - // otherwise return an error. + // Check whether an address has already been generated for any provided + // diversifier index. Return that address, or set the diversifier index + // at which we'll begin searching for the next available diversified + // address. auto metadata = mapUfvkAddressMetadata.find(ufvkid); if (metadata != mapUfvkAddressMetadata.end()) { - auto receivers = metadata->second.GetReceivers(j); - if (receivers.has_value()) { - if (receivers.value() == receiverTypes) { - return std::make_pair(ufvk.value().Address(j, receiverTypes).value(), j); - } else { - return AddressGenerationError::ExistingAddressMismatch; + if (j.has_value()) { + + auto receivers = metadata->second.GetReceivers(j.value()); + if (receivers.has_value()) { + // Ensure that the set of receiver types being requested is + // the same as the set of receiver types that was previously + // generated. If they match, simply return that address. + if (receivers.value() == receiverTypes) { + return std::make_pair(ufvk.value().Address(j.value(), receiverTypes).value(), j.value()); + } else { + return AddressGenerationError::ExistingAddressMismatch; + } + } + } else { + // Set the diversifier index to one greater than the last used + // diversifier + j = metadata->second.GetNextDiversifierIndex(); + if (!j.has_value()) { + return AddressGenerationError::DiversifierSpaceExhausted; } } + } else { + // Begin searching from the zero diversifier index if we haven't + // yet generated an address from the specified UFVK and no + // diversifier index has been specified. + if (!j.has_value()) { + j = libzcash::diversifier_index_t(0); + } } // Find a working diversifier and construct the associated address. - auto foundAddress = ufvk.value().FindAddress(j, receiverTypes); - auto diversifierIndex = foundAddress.second; + // At this point, we know that `j` will contain a value. Optimistically + // attempt to find an address at that diversifier; we'll search if we + // don't find one. + auto diversifierIndex = j.value(); + auto address = ufvk.value().Address(diversifierIndex, receiverTypes); + if (!address.has_value() && searchDiversifiers) { + auto found = ufvk.value().FindAddress(j.value(), receiverTypes); + if (found.has_value()) { + address = found.value().first; + diversifierIndex = found.value().second; + } else { + return AddressGenerationError::DiversifierSpaceExhausted; + } + } + + // Now that we're done searching, check that we actually got an address + if (!address.has_value()) { + return AddressGenerationError::NoAddressForDiversifier; + } // Persist the newly created address to the keystore mapUfvkAddressMetadata[ufvkid].SetReceivers(diversifierIndex, receiverTypes); - CCryptoKeyStore::AddUnifiedAddress(ufvkid, diversifierIndex, foundAddress.first); + CCryptoKeyStore::AddUnifiedAddress(ufvkid, diversifierIndex, address.value()); // Save the metadata for the generated address so that we can re-derive // it in the future. @@ -616,7 +657,7 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( AddTransparentSecretKey(seed.Fingerprint(), key); } - return foundAddress; + return std::make_pair(address.value(), diversifierIndex); } else { return AddressGenerationError::NoSuchAccount; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index cb8f825854f..e24b883b6ac 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -406,7 +406,8 @@ enum class AddressGenerationError { NoSuchAccount, InvalidReceiverTypes, ExistingAddressMismatch, - NoSaplingAddressForDiversifier, + NoAddressForDiversifier, + DiversifierSpaceExhausted, WalletEncrypted, InvalidTransparentChildIndex }; @@ -729,6 +730,26 @@ class UFVKAddressMetadata return true; } } + + /** + * Search the for the maximum diversifier that has already been used to + * generate a new address, and return the next diversifier. Returns the + * zero diversifier index if no addresses have yet been generated, + * and returns std::nullopt if the increment operation would cause an + * overflow. + */ + std::optional GetNextDiversifierIndex() { + if (addressReceivers.empty()) { + return libzcash::diversifier_index_t(0); + } else { + auto lastIndex = addressReceivers.rbegin()->first; + if (lastIndex.increment()) { + return lastIndex; + } else { + return std::nullopt; + } + } + } }; /** @@ -1189,10 +1210,13 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Generate a new unified address for the specified account, diversifier, and //! set of receiver types. + //! + //! If no diversifier index is provided, the next unused diversifier index + //! will be selected. UAGenerationResult GenerateUnifiedAddress( const libzcash::AccountId& accountId, - const libzcash::diversifier_index_t& j, - const std::set& receivers); + const std::set& receivers, + std::optional j = std::nullopt); bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk); diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 9b3f3bf711c..d30f83a95c4 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -95,6 +95,7 @@ std::pair AddressInfoFromViewingKey::operator()(con "unified", ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(keyConstants, ufvk) .FindAddress(diversifier_index_t(0)) + .value() //safe because we're searching from 0 .first ); } diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index a96b0a4e250..69d06443485 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -14,12 +14,20 @@ using namespace libzcash; bool libzcash::HasShielded(const std::set& receiverTypes) { auto has_shielded = [](ReceiverType r) { - // TODO: update this as support for new protocols is added. + // TODO: update this as support for new shielded protocols is added. return r == ReceiverType::Sapling; }; return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_shielded) != receiverTypes.end(); } +bool libzcash::HasTransparent(const std::set& receiverTypes) { + auto has_shielded = [](ReceiverType r) { + // TODO: update this as support for new transparent protocols is added. + return r == ReceiverType::P2PKH || r == ReceiverType::P2SH; + }; + return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_shielded) != receiverTypes.end(); +} + std::optional ZcashdUnifiedSpendingKey::ForAccount( const HDSeed& seed, uint32_t bip44CoinType, @@ -108,20 +116,21 @@ std::optional ZcashdUnifiedFullViewingKey::Address( return ua; } -std::pair ZcashdUnifiedFullViewingKey::FindAddress( +std::optional> ZcashdUnifiedFullViewingKey::FindAddress( const diversifier_index_t& j, const std::set& receiverTypes) const { diversifier_index_t j0(j); + bool hasTransparent = HasTransparent(receiverTypes); auto addr = Address(j0, receiverTypes); while (!addr.has_value()) { - if (!j0.increment()) - throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; + if (!j0.increment() || (hasTransparent && !j0.ToTransparentChildIndex().has_value())) + return std::nullopt; addr = Address(j0, receiverTypes); } return std::make_pair(addr.value(), j0); } -std::pair ZcashdUnifiedFullViewingKey::FindAddress( +std::optional> ZcashdUnifiedFullViewingKey::FindAddress( const diversifier_index_t& j) const { return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling, ReceiverType::Orchard}); } diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 0a0435cc5e0..754b134365c 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -24,6 +24,12 @@ enum class ReceiverType: uint32_t { */ bool HasShielded(const std::set& receiverTypes); +/** + * Test whether the specified list of receiver types contains a + * shielded receiver type + */ +bool HasTransparent(const std::set& receiverTypes); + class ZcashdUnifiedSpendingKey; // prototypes for the classes handling ZIP-316 encoding (in Address.hpp) @@ -94,15 +100,21 @@ class ZcashdUnifiedFullViewingKey { * Find the smallest diversifier index >= `j` such that it generates a valid * unified address according to the conditions specified in the documentation * for the `Address` method above, and returns the newly created address along - * with the diversifier index used to produce it. + * with the diversifier index used to produce it. Returns `std::nullopt` if the + * diversifier space is exhausted, or if the set of receiver types contains a + * transparent receiver and the diversifier exceeds the maximum transparent + * child index. * * This method will throw if `receiverTypes` does not include a shielded receiver type. */ - std::pair FindAddress( + std::optional> FindAddress( const diversifier_index_t& j, const std::set& receiverTypes) const; - std::pair FindAddress(const diversifier_index_t& j) const; + /** + * Find the next available address that contains all supported receiver types. + */ + std::optional> FindAddress(const diversifier_index_t& j) const; friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b) { diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index b22cfa045a2..aadd2cb99df 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -19,8 +19,6 @@ const unsigned char ZCASH_HD_SEED_FP_PERSONAL[BLAKE2bPersonalBytes] = const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = {'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'}; -const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x7FFFFFFF); - uint256 HDSeed::Fingerprint() const { CBLAKE2bWriter h(SER_GETHASH, 0, ZCASH_HD_SEED_FP_PERSONAL); diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index d2ba55d8dd1..d62fbb94e70 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -103,6 +103,15 @@ class diversifier_index_t : public base_blob<88> { return false; //overflow } + std::optional succ() const { + diversifier_index_t next(*this); + if (next.increment()) { + return next; + } else { + return std::nullopt; + } + } + std::optional ToTransparentChildIndex() const; friend bool operator<(const diversifier_index_t& a, const diversifier_index_t& b) { @@ -118,6 +127,9 @@ class diversifier_index_t : public base_blob<88> { } }; +// The maximum allowed transparent child index according to BIP-44 +const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x7FFFFFFF); + class SaplingDiversifiableFullViewingKey { public: libzcash::SaplingFullViewingKey fvk; From 8d6d178a47b6e2cf4e9ca72749d49a02f4359891 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 30 Dec 2021 11:24:26 -0700 Subject: [PATCH 220/514] Use Bip44TransparentAccountKeyPath() for Bip44AccountChains keypath construction. --- src/zcash/address/bip44.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/zcash/address/bip44.cpp b/src/zcash/address/bip44.cpp index 075c2491f2a..1dd5679bdcd 100644 --- a/src/zcash/address/bip44.cpp +++ b/src/zcash/address/bip44.cpp @@ -50,11 +50,7 @@ std::optional> libzcash::Bip44AccountChains::DeriveEx auto childKey = external.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; - auto hdKeypath = "m/44'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(accountId) + "'/" - + "0/" - + std::to_string(addrIndex); + auto hdKeypath = Bip44TransparentAccountKeyPath(bip44CoinType, accountId) + "/0/" + std::to_string(addrIndex); return std::make_pair(childKey.value().key, hdKeypath); } @@ -63,11 +59,7 @@ std::optional> libzcash::Bip44AccountChains::DeriveIn auto childKey = internal.Derive(addrIndex); if (!childKey.has_value()) return std::nullopt; - auto hdKeypath = "m/44'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(accountId) + "'/" - + "1/" - + std::to_string(addrIndex); + auto hdKeypath = Bip44TransparentAccountKeyPath(bip44CoinType, accountId) + "/1/" + std::to_string(addrIndex); return std::make_pair(childKey.value().key, hdKeypath); } From ad54591061153dd00b76d02d60fef0bc063d2953 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 30 Dec 2021 11:27:13 -0700 Subject: [PATCH 221/514] Improve documentation of UFVK/UA metadata storage semantics. --- src/wallet/wallet.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 77fcee2c622..a23bc677537 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -679,6 +679,11 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( // once the UFVK has been loaded. // - if we have already loaded the unified full viewing key from disk, // regenerate the address from its metadata and add it to the keystore +// +// While this is somewhat complicated, it has the benefit of making each +// piece of unified full viewing key and address metadata write-once in +// the wallet database; we never need to update ufvk or address metadata +// once written. bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key) { auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key); From eb53abbbafa4511a8b007d6bf3a6d635fdf3cd7d Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 3 Jan 2022 15:46:28 -0700 Subject: [PATCH 222/514] Apply suggestions from code review Co-authored-by: str4d --- src/gtest/test_keystore.cpp | 12 ++++-- src/keystore.cpp | 21 +++++---- src/keystore.h | 4 +- src/wallet/gtest/test_wallet.cpp | 7 ++- src/wallet/wallet.cpp | 74 ++++++++++++++++---------------- src/wallet/wallet.h | 33 ++++++++------ src/zcash/address/unified.cpp | 6 +-- src/zcash/address/unified.h | 2 +- 8 files changed, 85 insertions(+), 74 deletions(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index e8b4abb72c9..b2cb66fd91d 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -536,17 +536,19 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}).value(); EXPECT_TRUE(addrPair.first.GetSaplingReceiver().has_value()); auto saplingReceiver = addrPair.first.GetSaplingReceiver().value(); + auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver); + EXPECT_FALSE(ufvkmeta.has_value()); auto saplingIvk = zufvk.GetSaplingKey().value().fvk.in_viewing_key(); keyStore.AddSaplingIncomingViewingKey(saplingIvk, saplingReceiver); - auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver); + ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver); EXPECT_TRUE(ufvkmeta.has_value()); EXPECT_EQ(ufvkmeta.value().first, ufvkid); EXPECT_FALSE(ufvkmeta.value().second.has_value()); } -TEST(KeystoreTests, AddUnifiedAddress) { +TEST(KeystoreTests, AddTransparentReceiverForUnifiedAddress) { SelectParams(CBaseChainParams::TESTNET); CBasicKeyStore keyStore; @@ -559,10 +561,12 @@ TEST(KeystoreTests, AddUnifiedAddress) { auto ufvkid = zufvk.GetKeyID(); auto addrPair = zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::P2PKH, ReceiverType::Sapling}).value(); EXPECT_TRUE(addrPair.first.GetP2PKHReceiver().has_value()); + auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value()); + EXPECT_FALSE(ufvkmeta.has_value()); - keyStore.AddUnifiedAddress(ufvkid, addrPair.second, addrPair.first); + keyStore.AddTransparentReceiverForUnifiedAddress(ufvkid, addrPair.second, addrPair.first); - auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value()); + ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value()); EXPECT_TRUE(ufvkmeta.has_value()); EXPECT_EQ(ufvkmeta.value().first, ufvkid); } diff --git a/src/keystore.cpp b/src/keystore.cpp index 487c1ec2aa3..27806b7e33d 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -316,7 +316,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey( return true; } -bool CBasicKeyStore::AddUnifiedAddress( +bool CBasicKeyStore::AddTransparentReceiverForUnifiedAddress( const libzcash::UFVKId& keyId, const libzcash::diversifier_index_t& diversifierIndex, const libzcash::UnifiedAddress& ua) @@ -361,10 +361,11 @@ CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) c std::optional>> FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const { - if (keystore.mapSaplingIncomingViewingKeys.count(saplingAddr) > 0) { - const auto& saplingIvk = keystore.mapSaplingIncomingViewingKeys.at(saplingAddr); - if (keystore.mapSaplingKeyUnified.count(saplingIvk) > 0) { - return std::make_pair(keystore.mapSaplingKeyUnified.at(saplingIvk), std::nullopt); + const auto saplingIvk = keystore.mapSaplingIncomingViewingKeys.find(saplingAddr); + if (saplingIvk != keystore.mapSaplingIncomingViewingKeys.end()) { + const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second); + if (ufvkId != keystore.mapSaplingKeyUnified.end()) { + return std::make_pair(ufvkId->second, std::nullopt); } else { return std::nullopt; } @@ -374,16 +375,18 @@ FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const } std::optional>> FindUFVKId::operator()(const CScriptID& scriptId) const { - if (keystore.mapP2SHUnified.count(scriptId) > 0) { - return keystore.mapP2SHUnified.at(scriptId); + const auto metadata = keystore.mapP2SHUnified.find(scriptId); + if (metadata != keystore.mapP2SHUnified.end()) { + return metadata->second; } else { return std::nullopt; } } std::optional>> FindUFVKId::operator()(const CKeyID& keyId) const { - if (keystore.mapP2PKHUnified.count(keyId) > 0) { - return keystore.mapP2PKHUnified.at(keyId); + const auto metadata = keystore.mapP2PKHUnified.find(keyId); + if (metadata != keystore.mapP2PKHUnified.end()) { + return metadata->second; } else { return std::nullopt; } diff --git a/src/keystore.h b/src/keystore.h index d3e1c6c761c..c0da90c24db 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -117,7 +117,7 @@ class CKeyStore * viewing key upon discovery of the address as having received * funds. */ - virtual bool AddUnifiedAddress( + virtual bool AddTransparentReceiverForUnifiedAddress( const libzcash::UFVKId& keyId, const libzcash::diversifier_index_t& diversifierIndex, const libzcash::UnifiedAddress& ua) = 0; @@ -357,7 +357,7 @@ class CBasicKeyStore : public CKeyStore virtual bool AddUnifiedFullViewingKey( const libzcash::ZcashdUnifiedFullViewingKey &ufvk); - virtual bool AddUnifiedAddress( + virtual bool AddTransparentReceiverForUnifiedAddress( const libzcash::UFVKId& keyId, const libzcash::diversifier_index_t& diversifierIndex, const libzcash::UnifiedAddress& ua); diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 3f758550a36..707b1354483 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -182,7 +182,6 @@ TEST(WalletTests, SproutNoteDataSerialisation) { EXPECT_EQ(noteData[jsoutpt].witnesses, noteData2[jsoutpt].witnesses); } - TEST(WalletTests, FindUnspentSproutNotes) { SelectParams(CBaseChainParams::TESTNET); @@ -356,9 +355,6 @@ TEST(WalletTests, FindUnspentSproutNotes) { mapBlockIndex.erase(blockHash); mapBlockIndex.erase(blockHash2); mapBlockIndex.erase(blockHash3); - - // Revert to default - RegtestDeactivateSapling(); } @@ -2232,4 +2228,7 @@ TEST(WalletTests, GenerateUnifiedAddress) { expected = AddressGenerationError::DiversifierSpaceExhausted; EXPECT_EQ(uaResult, expected); } + + // Revert to default + RegtestDeactivateSapling(); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a23bc677537..9538a3beffd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -195,9 +195,7 @@ bool CWallet::AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &sk) return true; } -bool CWallet::AddSaplingFullViewingKey( - const libzcash::SaplingExtendedFullViewingKey &extfvk, - const std::optional& ufvkId) +bool CWallet::AddSaplingFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk) { AssertLockHeld(cs_wallet); @@ -636,16 +634,16 @@ UAGenerationResult CWallet::GenerateUnifiedAddress( return AddressGenerationError::NoAddressForDiversifier; } - // Persist the newly created address to the keystore - mapUfvkAddressMetadata[ufvkid].SetReceivers(diversifierIndex, receiverTypes); - CCryptoKeyStore::AddUnifiedAddress(ufvkid, diversifierIndex, address.value()); + assert(mapUfvkAddressMetadata[ufvkid].SetReceivers(diversifierIndex, receiverTypes)); + // Writing this data is handled by `CWalletDB::WriteUnifiedAddressMetadata` below. + assert(CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(ufvkid, diversifierIndex, address.value())); // Save the metadata for the generated address so that we can re-derive // it in the future. ZcashdUnifiedAddressMetadata addrmeta(ufvkid, diversifierIndex, receiverTypes); if (fFileBacked && !CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta)) { throw std::runtime_error( - "CWallet::AdddUnifiedAddress(): Writing unified address metadata failed"); + "CWallet::AddUnifiedAddress(): Writing unified address metadata failed"); } if (hasTransparent) { @@ -691,11 +689,14 @@ bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &k if (metadata != mapUfvkAddressMetadata.end()) { // restore unified addresses that have been previously generated to the // keystore - for (const auto &[j, receiverTypes] : metadata->second.GetAllReceivers()) { + for (const auto &[j, receiverTypes] : metadata->second.GetKnownReceiverSetsByDiversifierIndex()) { auto addr = zufvk.Address(j, receiverTypes).value(); - CCryptoKeyStore::AddUnifiedAddress(zufvk.GetKeyID(), j, addr); + if (!CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(zufvk.GetKeyID(), j, addr)) { + return false; + } } } + return CCryptoKeyStore::AddUnifiedFullViewingKey(zufvk); } @@ -710,16 +711,18 @@ bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skm bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta) { AssertLockHeld(cs_wallet); - mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers( + if (!mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers( addrmeta.GetDiversifierIndex(), - addrmeta.GetReceiverTypes()); + addrmeta.GetReceiverTypes())) { + return false; + } auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID()); if (ufvk.has_value()) { // Regenerate the unified address and add it to the keystore. auto j = addrmeta.GetDiversifierIndex(); auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes()).value(); - return CCryptoKeyStore::AddUnifiedAddress(addrmeta.GetKeyID(), j, addr); + return CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(addrmeta.GetKeyID(), j, addr); } return true; @@ -5862,21 +5865,19 @@ std::optional LookupUnifiedAddress::operator()(const l } diversifier_index_t j; - auto metadata = wallet.mapUfvkAddressMetadata.find(ufvkid); - if (metadata != wallet.mapUfvkAddressMetadata.end()) { - librustzcash_sapling_diversifier_index( - ufvk.value().GetSaplingKey().value().dk.begin(), - saplingAddr.d.begin(), - j.begin()); - auto receivers = metadata->second.GetReceivers(j); - if (receivers.has_value()) { - return ufvk.value().Address(j, receivers.value()); - } else { - // If we don't know the receiver types at which the address was originally - // generated, we can't reconstruct the address. - return std::nullopt; - } + // If the wallet is missing metadata at this UFVK id, it is probably + // corrupt and the node should shut down. + const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid); + librustzcash_sapling_diversifier_index( + ufvk.value().GetSaplingKey().value().dk.begin(), + saplingAddr.d.begin(), + j.begin()); + auto receivers = metadata.GetReceivers(j); + if (receivers.has_value()) { + return ufvk.value().Address(j, receivers.value()); } else { + // If we don't know the receiver types at which the address was originally + // generated, we can't reconstruct the address. return std::nullopt; } } else { @@ -5899,17 +5900,16 @@ std::optional LookupUnifiedAddress::operator()(const C throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no P2PKH key part."); } - // Find the set of receivers at the diversifier index. If no metadata is available - // for the ufvk, or we do not know the receiver types for the address produced - // at this diversifier, we cannot reconstruct the address. - auto metadata = wallet.mapUfvkAddressMetadata.find(ufvkid); - if (metadata != wallet.mapUfvkAddressMetadata.end()) { - auto receivers = metadata->second.GetReceivers(j); - if (receivers.has_value()) { - return ufvk.value().Address(j, receivers.value()); - } else { - return std::nullopt; - } + // If the wallet is missing metadata at this UFVK id, it is probably + // corrupt and the node should shut down. + const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid); + + // Find the set of receivers at the diversifier index. If we do not + // know the receiver types for the address produced at this + // diversifier, we cannot reconstruct the address. + auto receivers = metadata.GetReceivers(j); + if (receivers.has_value()) { + return ufvk.value().Address(j, receivers.value()); } else { return std::nullopt; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e24b883b6ac..9bc581f59cb 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -685,13 +685,21 @@ class CWalletKey class UFVKAddressMetadata { private: + // The account ID may be absent for imported UFVKs, and also may temporarily + // be absent when this data structure is in a partially-reconstructed state + // during the wallet load process. std::optional accountId; std::map> addressReceivers; public: UFVKAddressMetadata() {} UFVKAddressMetadata(libzcash::AccountId accountId): accountId(accountId) {} - const std::map>& GetAllReceivers() const { + /** + * Return all currently known diversifier indices for which addresses + * have been generated, each accompanied by the associated set of receiver + * types that were used when generating that address. + */ + const std::map>& GetKnownReceiverSetsByDiversifierIndex() const { return addressReceivers; } @@ -715,10 +723,11 @@ class UFVKAddressMetadata bool SetReceivers( const libzcash::diversifier_index_t& j, const std::set& receivers) { - if (addressReceivers.count(j) > 0) { - return addressReceivers[j] == receivers; + const auto [it, success] = addressReceivers.insert(std::make_pair(j, receivers)); + if (success) { + return true; } else { - return addressReceivers.insert(std::make_pair(j, receivers)).second; + return it->second == receivers; } } @@ -742,12 +751,7 @@ class UFVKAddressMetadata if (addressReceivers.empty()) { return libzcash::diversifier_index_t(0); } else { - auto lastIndex = addressReceivers.rbegin()->first; - if (lastIndex.increment()) { - return lastIndex; - } else { - return std::nullopt; - } + return addressReceivers.rbegin()->first.succ(); } } }; @@ -1167,8 +1171,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! CBasicKeyStore::AddSaplingFullViewingKey is called directly when adding a //! full viewing key to the keystore, to avoid this override. bool AddSaplingFullViewingKey( - const libzcash::SaplingExtendedFullViewingKey &extfvk, - const std::optional& ufvkId = std::nullopt); + const libzcash::SaplingExtendedFullViewingKey &extfvk); bool AddSaplingIncomingViewingKey( const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingPaymentAddress &addr); @@ -1199,8 +1202,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::pair GenerateNewUnifiedSpendingKey(); - //! Generate the next available unified spending key from the wallet's - //! mnemonic seed. + //! Generate the unified spending key for the specified ZIP-32/BIP-44 + //! account identifier from the wallet's mnemonic seed, or returns + //! std::nullopt if the account identifier does not produce a valid + //! spending key for all receiver types. std::optional GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId); diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index 69d06443485..555f7f931f5 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -21,11 +21,11 @@ bool libzcash::HasShielded(const std::set& receiverTypes) { } bool libzcash::HasTransparent(const std::set& receiverTypes) { - auto has_shielded = [](ReceiverType r) { + auto has_transparent = [](ReceiverType r) { // TODO: update this as support for new transparent protocols is added. return r == ReceiverType::P2PKH || r == ReceiverType::P2SH; }; - return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_shielded) != receiverTypes.end(); + return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_transparent) != receiverTypes.end(); } std::optional ZcashdUnifiedSpendingKey::ForAccount( @@ -132,5 +132,5 @@ std::optional> ZcashdUnifiedFullV std::optional> ZcashdUnifiedFullViewingKey::FindAddress( const diversifier_index_t& j) const { - return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling, ReceiverType::Orchard}); + return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling}); } diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h index 754b134365c..cddee7cc488 100644 --- a/src/zcash/address/unified.h +++ b/src/zcash/address/unified.h @@ -15,7 +15,7 @@ enum class ReceiverType: uint32_t { P2PKH = 0x00, P2SH = 0x01, Sapling = 0x02, - Orchard = 0x03 + //Orchard = 0x03 }; /** From dfefab2f5587b873b02c4ed22ee3441d63f8a111 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 4 Jan 2022 09:46:30 -0700 Subject: [PATCH 223/514] test: check getblocktemplate output before and after NU5 This test currently fails with submitblock returning the error "bad-heartwood-root-in-block". Added authdigest to GBT coinbasetxn field because we can't obtain this via getrawtransaction. Co-authored-by: Jack Grigg --- qa/rpc-tests/getblocktemplate.py | 141 ++++++++++++++++++------ qa/rpc-tests/test_framework/mininode.py | 24 ++++ src/rpc/mining.cpp | 1 + 3 files changed, 130 insertions(+), 36 deletions(-) diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index 324e4e43169..ab6803cd0f5 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -8,11 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - BLOSSOM_BRANCH_ID, CANOPY_BRANCH_ID, - HEARTWOOD_BRANCH_ID, NU5_BRANCH_ID, - get_coinbase_address, hex_str_to_bytes, nuparams, start_nodes, @@ -20,8 +17,9 @@ ) from test_framework.mininode import ( CTransaction, + uint256_from_str, ) -from test_framework.blocktools import( +from test_framework.blocktools import ( create_block ) from decimal import Decimal @@ -39,38 +37,36 @@ def __init__(self): def setup_network(self, split=False): args = [ - nuparams(BLOSSOM_BRANCH_ID, 1), - nuparams(HEARTWOOD_BRANCH_ID, 1), - nuparams(CANOPY_BRANCH_ID, 1), - nuparams(NU5_BRANCH_ID, 1), + nuparams(CANOPY_BRANCH_ID, 115), + nuparams(NU5_BRANCH_ID, 130), ] self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [args] * self.num_nodes) - self.is_network_split=False - self.sync_all() + self.is_network_split = False + self.node = self.nodes[0] + + def add_nu5_v4_tx_to_mempool(self): + node = self.node + # sprout to transparent (v4) + recipients = [{"address": self.transparent_addr, "amount": Decimal('0.1')}] + myopid = node.z_sendmany(self.sprout_addr, recipients) + wait_and_assert_operationid_status(node, myopid) + + def add_transparent_tx_to_mempool(self): + node = self.node + # transparent to transparent (v5 after nu5) + outputs = {self.transparent_addr: 0.1} + node.sendmany('', outputs) + + def gbt_submitblock(self): + node = self.node + mempool_tx_list = node.getrawmempool() - def run_test(self): - node = self.nodes[0] - print("Generating blocks") - node.generate(110) - - print("Add transactions to the mempool so they will be in the template") - # This part of the test should be improved, submit some V4 transactions - # and varying combinations of shielded and transparent - for _ in range(5): - # submit a tx with a shielded output - taddr0 = get_coinbase_address(node) - zaddr = node.z_getnewaddress('sapling') - recipients = [{"address": zaddr, "amount": Decimal('0.1')}] - myopid = node.z_sendmany(taddr0, recipients, 1, 0) - wait_and_assert_operationid_status(node, myopid) - - # submit a tx with a transparent output - outputs = {node.getnewaddress():0.2} - node.sendmany('', outputs) - - print("Getting block template") gbt = node.getblocktemplate() + # make sure no transactions were left out (or added) + assert_equal(len(mempool_tx_list), len(gbt['transactions'])) + assert_equal(set(mempool_tx_list), set([tx['hash'] for tx in gbt['transactions']])) + prevhash = int(gbt['previousblockhash'], 16) blockcommitmentshash = int(gbt['defaultroots']['blockcommitmentshash'], 16) nTime = gbt['mintime'] @@ -79,8 +75,10 @@ def run_test(self): f = BytesIO(hex_str_to_bytes(gbt['coinbasetxn']['data'])) coinbase = CTransaction() coinbase.deserialize(f) + coinbase.calc_sha256() + assert_equal(coinbase.hash, gbt['coinbasetxn']['hash']) + assert_equal(coinbase.auth_digest_hex, gbt['coinbasetxn']['authdigest']) - print("Creating_block from template (simulating a miner)") block = create_block(prevhash, coinbase, nTime, nBits, blockcommitmentshash) # copy the non-coinbase transactions from the block template to the block @@ -88,18 +86,89 @@ def run_test(self): f = BytesIO(hex_str_to_bytes(gbt_tx['data'])) tx = CTransaction() tx.deserialize(f) + tx.calc_sha256() + assert_equal(tx.auth_digest_hex, node.getrawtransaction(tx.hash, 1)['authdigest']) block.vtx.append(tx) block.hashMerkleRoot = int(gbt['defaultroots']['merkleroot'], 16) assert_equal(block.hashMerkleRoot, block.calc_merkle_root(), "merkleroot") assert_equal(len(block.vtx), len(gbt['transactions']) + 1, "number of transactions") assert_equal(block.hashPrevBlock, int(gbt['previousblockhash'], 16), "prevhash") + assert_equal(uint256_from_str(block.calc_auth_data_root()), int(gbt['defaultroots']['authdataroot'], 16)) block.solve() - block = block.serialize() - block = codecs.encode(block, 'hex_codec') + block.calc_sha256() - print("Submitting block") - submitblock_reply = node.submitblock(block) + submitblock_reply = node.submitblock(codecs.encode(block.serialize(), 'hex_codec')) assert_equal(None, submitblock_reply) + assert_equal(block.hash, node.getbestblockhash()) + # Wait until the wallet has been notified of all blocks, so that it doesn't try to + # double-spend transparent coins in subsequent test phases. + self.sync_all() + + def run_test(self): + node = self.node + + # Generate Sprout funds before Canopy activates; using the Sprout address will + # force the generation of v4 transactions from NU5. + print("Generating pre-Canopy blocks to create sprout funds") + # coinbase only becomes mature after 100 blocks, so make one mature. + node.generate(105) + + self.sprout_addr = node.z_getnewaddress('sprout') + myopid = node.z_shieldcoinbase('*', self.sprout_addr)['opid'] + wait_and_assert_operationid_status(node, myopid) + + self.transparent_addr = node.getnewaddress() + node.generate(15) + + # at height 120, NU5 is not active + assert_equal(node.getblockchaininfo()['upgrades']['37519621']['status'], 'pending') + + print("Testing getblocktemplate for pre-NU5") + + # Only the coinbase; this covers the case where the Merkle root + # is equal to the coinbase txid. + print("- only coinbase") + self.gbt_submitblock() + + # Adding one transaction triggering a single Merkle digest. + print("- one transaction (plus coinbase)") + self.add_transparent_tx_to_mempool() + self.gbt_submitblock() + + # Adding two transactions to trigger hash Merkle root edge case. + print("- two transactions (plus coinbase)") + self.add_transparent_tx_to_mempool() + self.add_transparent_tx_to_mempool() + self.gbt_submitblock() + + # Activate NU5, repeat the above cases + node.generate(7) + assert_equal(node.getblockchaininfo()['upgrades']['37519621']['status'], 'active') + + print("Testing getblocktemplate for post-NU5") + + # Only the coinbase; this covers the case where the block authdata root + # is equal to the coinbase authdata + print("- only coinbase") + self.gbt_submitblock() + + # Adding one transaction triggering a single Merkle digest. + print("- one transaction (plus coinbase)") + self.add_transparent_tx_to_mempool() + self.gbt_submitblock() + + # Adding two transactions to trigger hash Merkle root edge case. + print("- two transactions (plus coinbase)") + self.add_transparent_tx_to_mempool() + self.add_transparent_tx_to_mempool() + self.gbt_submitblock() + + # Adding both v4 and v5 to cover legacy auth digest. + print("- both v4 and v5 transactions (plus coinbase)") + self.add_nu5_v4_tx_to_mempool() + self.add_transparent_tx_to_mempool() + self.add_transparent_tx_to_mempool() + self.gbt_submitblock() if __name__ == '__main__': diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 11635f838d1..2c51f4421f3 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -1126,11 +1126,14 @@ def calc_sha256(self): if self.nVersion >= 5: from . import zip244 txid = zip244.txid_digest(self) + self.auth_digest = zip244.auth_digest(self) else: txid = hash256(self.serialize()) + self.auth_digest = b'\xFF'*32 if self.sha256 is None: self.sha256 = uint256_from_str(txid) self.hash = encode(txid[::-1], 'hex_codec').decode('ascii') + self.auth_digest_hex = encode(self.auth_digest[::-1], 'hex_codec').decode('ascii') def is_valid(self): self.calc_sha256() @@ -1263,6 +1266,27 @@ def calc_merkle_root(self): hashes = newhashes return uint256_from_str(hashes[0]) + def calc_auth_data_root(self): + hashes = [] + nleaves = 0 + for tx in self.vtx: + tx.calc_sha256() + hashes.append(tx.auth_digest) + nleaves += 1 + # Continue adding leaves (of zeros) until reaching a power of 2 + while nleaves & (nleaves-1) > 0: + hashes.append(b'\x00'*32) + nleaves += 1 + while len(hashes) > 1: + newhashes = [] + for i in range(0, len(hashes), 2): + digest = blake2b(digest_size=32, person=b'ZcashAuthDatHash') + digest.update(hashes[i]) + digest.update(hashes[i+1]) + newhashes.append(digest.digest()) + hashes = newhashes + return hashes[0] + def is_valid(self, n=48, k=5): # H(I||... digest = blake2b(digest_size=(512//n)*n//8, person=zcash_person(n, k)) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 48e0d69326e..35c746224d1 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -723,6 +723,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) entry.pushKV("data", EncodeHexTx(tx)); entry.pushKV("hash", txHash.GetHex()); + entry.pushKV("authdigest", tx.GetAuthDigest().GetHex()); UniValue deps(UniValue::VARR); for (const CTxIn &in : tx.vin) From 97a76f1b61c4288757f34c00332be510c1cc5e1a Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 4 Jan 2022 17:42:47 -0700 Subject: [PATCH 224/514] test: Fix ZIP 244 implementation Script codes must be serialized in their field encoding, which includes the CompactSize length prefix. Co-authored-by: Jack Grigg --- qa/rpc-tests/test_framework/zip244.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/test_framework/zip244.py b/qa/rpc-tests/test_framework/zip244.py index adbfbda59e6..f9cd5ceee25 100644 --- a/qa/rpc-tests/test_framework/zip244.py +++ b/qa/rpc-tests/test_framework/zip244.py @@ -15,7 +15,7 @@ from pyblake2 import blake2b -from .mininode import ser_uint256 +from .mininode import ser_string, ser_uint256 from .script import ( SIGHASH_ANYONECANPAY, SIGHASH_NONE, @@ -41,7 +41,7 @@ def transparent_digest(tx): def transparent_scripts_digest(tx): digest = blake2b(digest_size=32, person=b'ZTxAuthTransHash') for x in tx.vin: - digest.update(bytes(x.scriptSig)) + digest.update(ser_string(x.scriptSig)) return digest.digest() # Sapling @@ -288,7 +288,7 @@ def outputs_sig_digest(tx, nHashType, txin): def txin_sig_digest(tx, txin): digest = blake2b(digest_size=32, person=b'Zcash___TxInHash') digest.update(bytes(tx.vin[txin.nIn].prevout)) - digest.update(bytes(txin.scriptCode)) + digest.update(ser_string(txin.scriptCode)) digest.update(struct.pack(' Date: Wed, 5 Jan 2022 02:13:14 +0000 Subject: [PATCH 225/514] rpc: Fix regression in getblocktemplate output We added support for the NU5 consensus rules in v4.5.0, which alters the block header to contain a `hashBlockCommitments` value instead of the chain history root. However, the output of `getblocktemplate` wasn't returning this value; once NU5 activated, the `blockcommitmentshash` field was being set to "null" (all-zeroes). In v4.6.0 we added full NU5 support to `getblocktemplate`, by adding a `defaultroots` field that gave default values for `hashBlockCommitments` and the components required to derive it. However, in doing so we introduced a regression in the (now-deprecated) legacy fields, where prior to NU5 activation they contained nonsense. This commit fixes the output of `getblocktemplate` to have the intended semantics for all fields: - The `blockcommitmentshash` and `authdataroot` fields in `defaultroots` are now omitted from block templates for heights before NU5 activation. - The legacy fields now always contain the default value to be placed into the block header (regaining their previous semantics). Co-authored-by: Larry Ruane --- qa/rpc-tests/getblocktemplate.py | 32 +++++++++++++++++++++---------- src/miner.cpp | 33 ++++++++++++++++++++++++++++---- src/miner.h | 4 ++++ src/rpc/mining.cpp | 20 +++++++++---------- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index ab6803cd0f5..da3f638f991 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -57,7 +57,7 @@ def add_transparent_tx_to_mempool(self): outputs = {self.transparent_addr: 0.1} node.sendmany('', outputs) - def gbt_submitblock(self): + def gbt_submitblock(self, nu5_active): node = self.node mempool_tx_list = node.getrawmempool() @@ -68,10 +68,19 @@ def gbt_submitblock(self): assert_equal(set(mempool_tx_list), set([tx['hash'] for tx in gbt['transactions']])) prevhash = int(gbt['previousblockhash'], 16) - blockcommitmentshash = int(gbt['defaultroots']['blockcommitmentshash'], 16) nTime = gbt['mintime'] nBits = int(gbt['bits'], 16) + if nu5_active: + blockcommitmentshash = int(gbt['defaultroots']['blockcommitmentshash'], 16) + else: + blockcommitmentshash = int(gbt['defaultroots']['chainhistoryroot'], 16) + assert 'blockcommitmentshash' not in gbt['defaultroots'] + # Confirm that the legacy fields match this default value. + assert_equal(blockcommitmentshash, int(gbt['blockcommitmentshash'], 16)) + assert_equal(blockcommitmentshash, int(gbt['lightclientroothash'], 16)) + assert_equal(blockcommitmentshash, int(gbt['finalsaplingroothash'], 16)) + f = BytesIO(hex_str_to_bytes(gbt['coinbasetxn']['data'])) coinbase = CTransaction() coinbase.deserialize(f) @@ -93,7 +102,10 @@ def gbt_submitblock(self): assert_equal(block.hashMerkleRoot, block.calc_merkle_root(), "merkleroot") assert_equal(len(block.vtx), len(gbt['transactions']) + 1, "number of transactions") assert_equal(block.hashPrevBlock, int(gbt['previousblockhash'], 16), "prevhash") - assert_equal(uint256_from_str(block.calc_auth_data_root()), int(gbt['defaultroots']['authdataroot'], 16)) + if nu5_active: + assert_equal(uint256_from_str(block.calc_auth_data_root()), int(gbt['defaultroots']['authdataroot'], 16)) + else: + assert 'authdataroot' not in gbt['defaultroots'] block.solve() block.calc_sha256() @@ -128,18 +140,18 @@ def run_test(self): # Only the coinbase; this covers the case where the Merkle root # is equal to the coinbase txid. print("- only coinbase") - self.gbt_submitblock() + self.gbt_submitblock(False) # Adding one transaction triggering a single Merkle digest. print("- one transaction (plus coinbase)") self.add_transparent_tx_to_mempool() - self.gbt_submitblock() + self.gbt_submitblock(False) # Adding two transactions to trigger hash Merkle root edge case. print("- two transactions (plus coinbase)") self.add_transparent_tx_to_mempool() self.add_transparent_tx_to_mempool() - self.gbt_submitblock() + self.gbt_submitblock(False) # Activate NU5, repeat the above cases node.generate(7) @@ -150,25 +162,25 @@ def run_test(self): # Only the coinbase; this covers the case where the block authdata root # is equal to the coinbase authdata print("- only coinbase") - self.gbt_submitblock() + self.gbt_submitblock(True) # Adding one transaction triggering a single Merkle digest. print("- one transaction (plus coinbase)") self.add_transparent_tx_to_mempool() - self.gbt_submitblock() + self.gbt_submitblock(True) # Adding two transactions to trigger hash Merkle root edge case. print("- two transactions (plus coinbase)") self.add_transparent_tx_to_mempool() self.add_transparent_tx_to_mempool() - self.gbt_submitblock() + self.gbt_submitblock(True) # Adding both v4 and v5 to cover legacy auth digest. print("- both v4 and v5 transactions (plus coinbase)") self.add_nu5_v4_tx_to_mempool() self.add_transparent_tx_to_mempool() self.add_transparent_tx_to_mempool() - self.gbt_submitblock() + self.gbt_submitblock(True) if __name__ == '__main__': diff --git a/src/miner.cpp b/src/miner.cpp index cb2c10c3bb6..0ed57273e58 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -642,14 +642,38 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre pblock->hashPrevBlock = pindexPrev->GetBlockHash(); if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5)) { // hashBlockCommitments depends on the block transactions, so we have to - // update it whenever the coinbase transaction changes. Leave it unset here, - // like hashMerkleRoot, and instead cache what we will need to calculate it. + // update it whenever the coinbase transaction changes. + // + // - For the internal miner (either directly or via the `generate` RPC), this + // will occur in `IncrementExtraNonce()`, like for `hashMerkleRoot`. + // - For `getblocktemplate`, we have two sets of fields to handle: + // - The `defaultroots` fields, which contain both the default value (if + // nothing in the template is altered), and the roots that can be used to + // recalculate it (if some or all of the template is altered). + // - The legacy `finalsaplingroothash`, `lightclientroothash`, and + // `blockcommitmentshash` fields, which had the semantics of "place this + // value into the block header and things will work" (except for in + // v4.6.0 where they were accidentally set to always be the NU5 value). + // + // To accommodate all use cases, we calculate the `hashBlockCommitments` + // default value here (unlike `hashMerkleRoot`), and additionally cache the + // values necessary to recalculate it. pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId); + pblocktemplate->hashAuthDataRoot = pblock->BuildAuthDataMerkleTree(); + pblock->hashBlockCommitments = DeriveBlockCommitmentsHash( + pblocktemplate->hashChainHistoryRoot, + pblocktemplate->hashAuthDataRoot); } else if (IsActivationHeight(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) { + pblocktemplate->hashChainHistoryRoot.SetNull(); + pblocktemplate->hashAuthDataRoot.SetNull(); pblock->hashBlockCommitments.SetNull(); } else if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) { - pblock->hashBlockCommitments = view.GetHistoryRoot(prevConsensusBranchId); + pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId); + pblocktemplate->hashAuthDataRoot.SetNull(); + pblock->hashBlockCommitments = pblocktemplate->hashChainHistoryRoot; } else { + pblocktemplate->hashChainHistoryRoot.SetNull(); + pblocktemplate->hashAuthDataRoot.SetNull(); pblock->hashBlockCommitments = sapling_tree.root(); } UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); @@ -727,9 +751,10 @@ void IncrementExtraNonce( pblock->vtx[0] = txCoinbase; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); if (consensusParams.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5)) { + pblocktemplate->hashAuthDataRoot = pblock->BuildAuthDataMerkleTree(); pblock->hashBlockCommitments = DeriveBlockCommitmentsHash( pblocktemplate->hashChainHistoryRoot, - pblock->BuildAuthDataMerkleTree()); + pblocktemplate->hashAuthDataRoot); } } diff --git a/src/miner.h b/src/miner.h index 9b76ec6ba91..52ac1a29d6b 100644 --- a/src/miner.h +++ b/src/miner.h @@ -110,6 +110,10 @@ struct CBlockTemplate // Cached whenever we update `block`, so we can update hashBlockCommitments // when we change the coinbase transaction. uint256 hashChainHistoryRoot; + // Cached whenever we update `block`, so we can return it from `getblocktemplate` + // (enabling the caller to update `hashBlockCommitments` when they change + // `hashPrevBlock`). + uint256 hashAuthDataRoot; std::vector vTxFees; std::vector vTxSigOps; }; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 35c746224d1..c9f6fc73dde 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -454,9 +454,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " \"finalsaplingroothash\" : \"xxxx\", (string) (DEPRECATED) The hash of the light client root field in the block header\n" " \"defaultroots\" : { (json object) root hashes that need to be recomputed if the transaction set is modified\n" " \"merkleroot\" : \"xxxx\" (string) The hash of the transactions in the block header\n" - " \"authdataroot\" : \"xxxx\" (string) The hash of the authorizing data merkel tree\n" " \"chainhistoryroot\" : \"xxxx\" (string) The hash of the chain history\n" - " \"blockcommitmentshash\" : \"xxxx\" (string) The hash of the block commitments field in the block header\n" + " \"authdataroot\" : \"xxxx\" (string) (From NU5) The hash of the authorizing data merkel tree\n" + " \"blockcommitmentshash\" : \"xxxx\" (string) (From NU5) The hash of the block commitments field in the block header\n" " }\n" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" @@ -765,27 +765,25 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) aMutable.push_back("prevblock"); } - auto hashAuthDataRoot = pblock->BuildAuthDataMerkleTree(); - std::string hashBlockCommitments_hex = DeriveBlockCommitmentsHash( - pblocktemplate->hashChainHistoryRoot, - hashAuthDataRoot).GetHex(); UniValue result(UniValue::VOBJ); result.pushKV("capabilities", aCaps); result.pushKV("version", pblock->nVersion); result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); // The following 3 are deprecated; remove in a future release. - result.pushKV("blockcommitmentshash", hashBlockCommitments_hex); - result.pushKV("lightclientroothash", hashBlockCommitments_hex); - result.pushKV("finalsaplingroothash", hashBlockCommitments_hex); + result.pushKV("blockcommitmentshash", pblock->hashBlockCommitments.GetHex()); + result.pushKV("lightclientroothash", pblock->hashBlockCommitments.GetHex()); + result.pushKV("finalsaplingroothash", pblock->hashBlockCommitments.GetHex()); { // These are items in the result object that are valid only if the // block template returned by this RPC is used unmodified. Otherwise, // these values must be recomputed. UniValue defaults(UniValue::VOBJ); defaults.pushKV("merkleroot", pblock->BuildMerkleTree().GetHex()); - defaults.pushKV("authdataroot", hashAuthDataRoot.GetHex()); defaults.pushKV("chainhistoryroot", pblocktemplate->hashChainHistoryRoot.GetHex()); - defaults.pushKV("blockcommitmentshash", hashBlockCommitments_hex); + if (consensus.NetworkUpgradeActive(pindexPrev->nHeight+1, Consensus::UPGRADE_NU5)) { + defaults.pushKV("authdataroot", pblocktemplate->hashAuthDataRoot.GetHex()); + defaults.pushKV("blockcommitmentshash", pblock->hashBlockCommitments.GetHex()); + } result.pushKV("defaultroots", defaults); } result.pushKV("transactions", transactions); From 5bf937b42269f830dace7d4ee9926deb4e7a4ed4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 5 Jan 2022 20:16:10 +0000 Subject: [PATCH 226/514] make-release.py: Versioning changes for 4.6.0-1. --- README.md | 2 +- configure.ac | 2 +- contrib/gitian-descriptors/gitian-linux.yml | 2 +- src/clientversion.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c6a6abf2c23..5a276efa5be 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.6.0 +Zcash 4.6.0-1 =========== diff --git a/configure.ac b/configure.ac index d2dbb6ae7e5..4162fa3083c 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) define(_CLIENT_VERSION_MINOR, 6) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 50) +define(_CLIENT_VERSION_BUILD, 51) 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))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 9f72e1d22d4..69db0cca2d2 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.6.0" +name: "zcash-4.6.0-1" enable_cache: true distro: "debian" suites: diff --git a/src/clientversion.h b/src/clientversion.h index 8bff582b679..99f24a62df8 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -18,7 +18,7 @@ #define CLIENT_VERSION_MAJOR 4 #define CLIENT_VERSION_MINOR 6 #define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 50 +#define CLIENT_VERSION_BUILD 51 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true From b97133daafcd4154b3dfa533a8a4837913aad95f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 5 Jan 2022 20:18:43 +0000 Subject: [PATCH 227/514] make-release.py: Updated manpages for 4.6.0-1. --- doc/man/zcash-cli.1 | 8 ++++---- doc/man/zcash-tx.1 | 8 ++++---- doc/man/zcashd.1 | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 0290a4767a1..51ebcfcf104 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH ZCASH-CLI "1" "December 2021" "zcash-cli v4.6.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. +.TH ZCASH-CLI "1" "January 2022" "zcash-cli v4.6.0-1" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.6.0 +zcash-cli \- manual page for zcash-cli v4.6.0-1 .SH DESCRIPTION -Zcash RPC client version v4.6.0 +Zcash RPC client version v4.6.0\-1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 09873786354..ddb563fcfab 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH ZCASH-TX "1" "December 2021" "zcash-tx v4.6.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. +.TH ZCASH-TX "1" "January 2022" "zcash-tx v4.6.0-1" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.6.0 +zcash-tx \- manual page for zcash-tx v4.6.0-1 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.6.0 +Zcash zcash\-tx utility version v4.6.0\-1 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index ab62ba23e8f..cb512e41c7d 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH ZCASHD "1" "December 2021" "zcashd v4.6.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. +.TH ZCASHD "1" "January 2022" "zcashd v4.6.0-1" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.6.0 +zcashd \- manual page for zcashd v4.6.0-1 .SH DESCRIPTION -Zcash Daemon version v4.6.0 +Zcash Daemon version v4.6.0\-1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -84,7 +84,7 @@ Keep at most unconnectable transactions in memory (default: 100) .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-16\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR From cf22fa7fe3a16e4b1a56c0c7e4e9b519e94e4123 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 5 Jan 2022 20:18:43 +0000 Subject: [PATCH 228/514] make-release.py: Updated release notes and changelog for 4.6.0-1. --- contrib/debian/changelog | 6 ++++++ doc/authors.md | 4 ++-- doc/release-notes/release-notes-4.6.0-1.md | 13 +++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 doc/release-notes/release-notes-4.6.0-1.md diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 3caf05a2246..84754abf585 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.6.0+1) stable; urgency=medium + + * 4.6.0-1 release. + + -- Electric Coin Company Wed, 05 Jan 2022 20:18:43 +0000 + zcash (4.6.0) stable; urgency=medium * 4.6.0 release. diff --git a/doc/authors.md b/doc/authors.md index 9ca489b67c1..c7d8886ddd0 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,7 +1,7 @@ Zcash Contributors ================== -Jack Grigg (1124) +Jack Grigg (1127) Simon Liu (460) Sean Bowe (367) Daira Hopwood (270) @@ -16,7 +16,7 @@ Jonas Schnelli (89) Jay Graber (89) Marco Falke (82) Cory Fields (75) -Larry Ruane (69) +Larry Ruane (72) Ying Tong Lai (56) Nathan Wilcox (56) Matt Corallo (52) diff --git a/doc/release-notes/release-notes-4.6.0-1.md b/doc/release-notes/release-notes-4.6.0-1.md new file mode 100644 index 00000000000..2cae811ae7f --- /dev/null +++ b/doc/release-notes/release-notes-4.6.0-1.md @@ -0,0 +1,13 @@ +Changelog +========= + +Jack Grigg (3): + rpc: Fix regression in getblocktemplate output + make-release.py: Versioning changes for 4.6.0-1. + make-release.py: Updated manpages for 4.6.0-1. + +Larry Ruane (3): + assert that the return value of submitblock is None + test: check getblocktemplate output before and after NU5 + test: Fix ZIP 244 implementation + From daef0041e037f442c6c6ab2fba052d54803a62ff Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 5 Jan 2022 20:24:29 +0000 Subject: [PATCH 229/514] Add release notes for v4.6.0-1 --- doc/release-notes/release-notes-4.6.0-1.md | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doc/release-notes/release-notes-4.6.0-1.md b/doc/release-notes/release-notes-4.6.0-1.md index 2cae811ae7f..975376aa164 100644 --- a/doc/release-notes/release-notes-4.6.0-1.md +++ b/doc/release-notes/release-notes-4.6.0-1.md @@ -1,3 +1,27 @@ +`getblocktemplate` regression fix +================================= + +We added support for the NU5 consensus rules in v4.5.0, which alters the +block header to contain a `hashBlockCommitments` value instead of the +chain history root. However, the output of `getblocktemplate` wasn't +returning this value; once NU5 activated, the `blockcommitmentshash` +field was being set to "null" (all-zeroes). + +In v4.6.0 we added full NU5 support to `getblocktemplate`, by adding a +`defaultroots` field that gave default values for `hashBlockCommitments` +and the components required to derive it. However, in doing so we +introduced a regression in the (now-deprecated) legacy fields, where +prior to NU5 activation they contained nonsense. + +This release fixes the output of `getblocktemplate` to have the intended +semantics for all fields: + +- The `blockcommitmentshash` and `authdataroot` fields in `defaultroots` + are now omitted from block templates for heights before NU5 activation. + +- The legacy fields now always contain the default value to be placed + into the block header (regaining their previous semantics). + Changelog ========= From fe777c5624e6035b0387fca8c3014407d57e228b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 4 Jan 2022 20:54:04 -0700 Subject: [PATCH 230/514] Fix encoding order of unified addresses. --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 10 +++++----- src/rust/src/address_ffi.rs | 2 +- src/test/data/unified_addrs.json | 14 +++++++------- src/test/data/unified_full_viewing_keys.json | 18 +++++++++--------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3398f845f82..9ad1950c4ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,7 +466,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "blake2b_simd 1.0.0", "byteorder", @@ -475,7 +475,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "blake2b_simd 1.0.0", ] @@ -1816,7 +1816,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "bech32", "blake2b_simd 1.0.0", @@ -1828,7 +1828,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "byteorder", "nonempty", @@ -1837,7 +1837,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "bigint", "blake2b_simd 1.0.0", @@ -1847,7 +1847,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "chacha20", "chacha20poly1305", @@ -1858,7 +1858,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "aes", "bip0039", @@ -1892,7 +1892,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=5622b060b1f57de7afc3d0b4e425b9b4b22482a0#5622b060b1f57de7afc3d0b4e425b9b4b22482a0" +source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531" dependencies = [ "bellman", "blake2b_simd 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index ff661db0558..36d98c96128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,8 +70,8 @@ panic = 'abort' codegen-units = 1 [patch.crates-io] -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } -zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" } +zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" } diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index 0344ec4161a..e56e3892368 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -212,7 +212,7 @@ pub extern "C" fn zcash_address_serialize_unified( } }; - let ua = match unified::Address::try_from_items_preserving_order(receivers) { + let ua = match unified::Address::try_from_items(receivers) { Ok(ua) => ua, Err(e) => { tracing::error!("{}", e); diff --git a/src/test/data/unified_addrs.json b/src/test/data/unified_addrs.json index 899a162a052..08f3c651728 100644 --- a/src/test/data/unified_addrs.json +++ b/src/test/data/unified_addrs.json @@ -1,14 +1,14 @@ [ ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_address.py"], ["p2pkh_bytes, p2sh_bytes, sapling_raw_addr, orchard_raw_addr, unified_addr"], - [null, "7a8f739a2d9e945b0ce152a8049e294c4d6e66b1", null, "dcb1d2a37762148db4cee3bbf19fb1ec05891894b13801c622ba6a90faf1119f8224ae3985c6abd3b7bbae", "7531743779336e647634367476636465377834363835616a6d676b353970643039326c68733477616e776c646834666d3775636a723074343468667a356a6375667a75616b65667a7571767a376a30617974736e6677347065796478387538746a7371347738756b7336727973633635356d786b6a367778786870676b6e79733533657978706b383978797363"], - ["b3534201cfb1cd8dbf69b8250c18ef41294ca979", null, "902b6565a1c44e7e7a080571af1dd774697cc126f1fc0435d3cdbf868783e9fb4620df4bf175cbf2c3e36f", "05f61273a7201295332fee4579474534809a0aeb817a2bc0594166ad7a462067712533b6eec0fa2d1be99f", "75316665686b656a646d3630613871353567736576716c78676632396a38676c377364393436756e6a73346e6c343473633434307274657435756771373264637839706a7468396c323372706a7566757a63776837357971657a753235617532383970717274616c713637776e343066787177306c6e377a78777a397973787575307a39337772676e6d6671376b33666e36747a63307637727a396733706a67777571746c6b7a786873783839617a39656868336c356a357175757572776e376e33746a786c616e3235726e7134676a7a61387436"], - [null, "e8c7203d996af7d477083756d59af80d06a745f4", null, "4ea7d6b3dfa338192af06cbbf47ad405715bc7832bedb1466217dc0d93314de9f3c25eec89f9a21bfe0e93", "7531646a686778773579747a7463356d75323272346d733567796d3864776867383432326667757879766c7465787638787572677338637230756b7264797337667668716d6d6173616667666530677435776e306d756a6e63333467746d637734397565646b68707a79787a34397a7678356339757130686a64356e37747267736e656a686a716d7376723663"], + [null, "7a8f739a2d9e945b0ce152a8049e294c4d6e66b1", null, "dcb1d2a37762148db4cee3bbf19fb1ec05891894b13801c622ba6a90faf1119f8224ae3985c6abd3b7bbae", "753136757437336b36346a757a75366a753077617336717537676437717a30337261777368776e746d3030783937306b326374356d776a3536726478737863383871706e356b76716e35337a63686c6e74786b3834786b6b686e34666a74336466796e6c3576796d64666568306d3978653077383066766d6a6d7078346d65666a74776a3878676e6770687770"], + ["b3534201cfb1cd8dbf69b8250c18ef41294ca979", null, "902b6565a1c44e7e7a080571af1dd774697cc126f1fc0435d3cdbf868783e9fb4620df4bf175cbf2c3e36f", "05f61273a7201295332fee4579474534809a0aeb817a2bc0594166ad7a462067712533b6eec0fa2d1be99f", "7531677235333765706b3274786b787a746172377272763435706b706370656c38396e6137326e3867703572653439616c6d7a713438356e3672376133656134306a7132333278373975373765646b6c7a6e73356566383630756e78323371396773777276336d337a78326a7666646166367675703535757a7333347a37617563753830376730796c6b7563767976777635746379386834386b656767706e327238797035636c7036663032343936617973386a6c64386a353875677668653272786372737779726a6b6635726d376d3674777973"], + [null, "e8c7203d996af7d477083756d59af80d06a745f4", null, "4ea7d6b3dfa338192af06cbbf47ad405715bc7832bedb1466217dc0d93314de9f3c25eec89f9a21bfe0e93", "75316e6b35373061616d79347a6d68656c7a366b613033667a79347337663972346b6576666d67636533793536716e6a717a6d687436687937723338746b776474673961346363786533647874346b746a34616a6e396b79346c73366a656a633972676664756e737567756878646d706a303575723664756364776c686a6d39326a786c6a727937746732646b"], [null, null, "02f1536b622c01346742d8f90e9d4ff39137f1bebe6e23ad9971776b3372702494cc08951eef032b35350f", null, "75313363366d36716e657a72337966753468757630356e687961356372786e35347861786a78376d6b6674393861796e7a336b68636e6176647961306c746a797565717a3577706d306d7a6a357a646c343464323076657a6768753272743861737635636c6133746463"], - [null, "183e31d49f25c9a138f49b1a537edcf04be34a98", "3246b59a5b492dab1855cc176bddfa28418f11f97f7b361cc3e8834b2c30d2a1717df323ef98ea7de71d2e", "ab6d26252c521547049de208283d96278bb221a6874cb5a86af1d3f8b3db3fbee3dbefedcb2c71e3ca1ead", "75313565326e323536746b636837396677797a766c66337378666c66727772666d3664336d6539306e763932666b6575776e653477737975657a656e71356a7368716e6730396e776c7930353735717370373668687878776b6a6373736b6365666c38356c307465793338773236746c6c6575787a66676d38703732727435373033637238787579307577376c7263746e6767396b377a75387a75667576397178633674796a7278337833676a76796b646d79616d34797633793471376a666c783070306d39347066737739687a6b376132387079"], - [null, null, "970dc3450d34554141d356cb548056279c57708fa73bd16ffe9a2e24ea694898a7b8af1b0ff92585d02623", "0414bb62b86149ee731851f27d532ac0361169da46e6d53d19d3dfd07a5bae22969922d8d0af7dc1e13bae", "75316b616c6b6d3276717267716d386c6c71657568353372777a6c657435676561346d6b647739376a3970613372636574767a76336c6b6b33653075333534337670746774326b3075616638383630386675337563656676776577797a3974717765616a3377756c6c30706437306d746e687977656b306c3576617a6361366577663472646d7778713866327334706d35687175366d6567397a6774677a76393872787a6832777835766576797330346573"], - [null, "098b79535e790fe53e29fef2b3766697ac32b4f4", "a8a8797c1ba69f78672affa65b943975026931ea628431f0991e744872ac9f36946f5dcd6851a0b5af29cf", "678ab0079bea28bf165c1ab976a2a58c18a7811ca2ad0ad649e876273d04325da6ca53cdb83c111e8e4394", "75316d30767a6e3738676830796d71356b717a346c7064343039357770756538686e687a7373357a34736c3338737a6e38767736683934787576636a763561686b6e706d6c72613637756563766e6b6d73397966717834733768736e737a7a6d63706776787232306c6834353970733375337766366d727a77653678396b646a3874703061366373656432656d7870367137716e7761746432786d3470736a683472346739706866663534747375786535373638746873787373617678726679747064706d37326b6a7372797061797a7767713978"], + [null, "183e31d49f25c9a138f49b1a537edcf04be34a98", "3246b59a5b492dab1855cc176bddfa28418f11f97f7b361cc3e8834b2c30d2a1717df323ef98ea7de71d2e", "ab6d26252c521547049de208283d96278bb221a6874cb5a86af1d3f8b3db3fbee3dbefedcb2c71e3ca1ead", "7531656a706e33676e343039737238333466637771326b683679346a6170663968717273363633786a3074796e787563336467766b786767777575306d646c7938386379633867767433306b76343635763976656d6b7232327771657861737277343976796c34686e616c6c6a64637536327532736134643261357463656872666c7579746a7a32707a6a396d61393863783330636b3271306d6b35306b64366d657670396870796d6d39703772737579387076307061666c666a686c306c707a6e32323833776b68646c6a68716a7a306e63756b"], + [null, null, "970dc3450d34554141d356cb548056279c57708fa73bd16ffe9a2e24ea694898a7b8af1b0ff92585d02623", "0414bb62b86149ee731851f27d532ac0361169da46e6d53d19d3dfd07a5bae22969922d8d0af7dc1e13bae", "75316a6d386d6563326c7372653366666565706d74747334376b3833336d337271653072686d7a6a393778726737376136666c6a7a613336666a687734646363766d396c326e61376c706166756a6661356b617477383979773636687330636135747436666570736a76367030757539737764617672633870786d6c343066773865766b763230766a61386e77786e37366e61306d376e6774326c30797336323537307761756a717374357137797435746e"], + [null, "098b79535e790fe53e29fef2b3766697ac32b4f4", "a8a8797c1ba69f78672affa65b943975026931ea628431f0991e744872ac9f36946f5dcd6851a0b5af29cf", "678ab0079bea28bf165c1ab976a2a58c18a7811ca2ad0ad649e876273d04325da6ca53cdb83c111e8e4394", "7531617673633374613838646863346a353774646570386a6833663273673363756e6670736d36766d636a61613735663066643937667137637030797134636b6d636c3576637778787777333273757375743076346c30393733763573766e3737757430307a617538366e39367967637776797a3279357479796c366e6479723638383236346e7263343273683833326c6c686170686a3933616c30336a6e64363670376e3434707037687138666e6b75796d6b796a353665357039677239786565783430386535326e37353565373863646a6c73"], [null, null, "3509c9e069e89fe501d97622c283ac98923da2d7e6eb346b4bafa67865e1e6dae7cf213b1ea3648dc09b48", null, "7531357676383834637a356436346e677232717634307835797171677a366a743368657a75396d6b7566323064756a61663871766b736c6e78793738663264707879323470767637797666637a6b6c307761653435613070683664377a37646574336a67347267677866"], - [null, "30d069896cff30eb414f727b89e001afa2fb8dc3", "55bc46aea6f60c1d61915640029b2af6334d7d27e1c47a248ab47c9fbe5d2d7bb5818739f062e37136654c", null, "753137766a736b6c7234756a6463386c307a686b646d6b71717778343833673861363432653937366c7466733073346a65643761673376336138726e677275366e653438687a7233336b6b7a70366d61677a336679656a777a6a6734336a6470657274667078656e326661747a356b3830636a6b6c6e39713772396d3033646e366736306332796c3336393474"], + [null, "30d069896cff30eb414f727b89e001afa2fb8dc3", "55bc46aea6f60c1d61915640029b2af6334d7d27e1c47a248ab47c9fbe5d2d7bb5818739f062e37136654c", null, "7531396767387371706568756d676d73787a676a796d6c393336786b3267786d736665356a6537377a6a61613067676e32723330733239343266766b61327563747536396d7074763063323976686a7034686172637530723273366e793730306c797a78716866383335786b716a7873776a6a7771303261646b71796a6b6b3963776a6e377032736864713379"], [null, null, "5c26a8117729334a957ca7941d47b2ce7040e844fa9882c25bfd2fcf51fa8ab21376f5300d0123f5703e9e", null, "75313976636e33726564706170687834326d6e307379633236793877397766676c657a396175736b61787267686d78383064756e61333663616763337973376d6e336a373639766367387275336b646e61713470683436303438646873766c35646d6473677879653833"] ] diff --git a/src/test/data/unified_full_viewing_keys.json b/src/test/data/unified_full_viewing_keys.json index 99c6990a0c4..9cf0f072712 100644 --- a/src/test/data/unified_full_viewing_keys.json +++ b/src/test/data/unified_full_viewing_keys.json @@ -2,13 +2,13 @@ ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_full_viewing_keys.py"], ["t_key_bytes, sapling_fvk_bytes, orchard_fvk_bytes, unknown_fvk_typecode, unknown_fvk_bytes, unified_fvk"], [null, "cfb835e7c05c80c2a15a58702bc529a44e1a815ef79124f23709214cf0167ac4e6340b493dca8e4bee114259dc35edc4c296ffd53869885531d1bdb27008bbcd6fec092ad5c4d1f68819f41ae447db96df4a5f110018f47060916ec54884f1cc27a0d4c0bca90984cdf39fb4cc61ceee78ddaa2a45af871f49f04e98b02fb16b", null, 65535, null, "757669657731747878783339707833736a676478796c6d6636666876706e6878667966717376756e3863737330723678717830726b3974767a3076727a74756a74683474716e7534367877657035367279396a643537687972726c36757467657a356a717232716466737a79787265686b64686774757964376d78756e6e6133327732356d396b7771387163687377673476686632796d736b376c6e7637786a3864356b347a7272343370756e746b6d666b396e346a636b66763237677063376e747765726c6c756439307a346c356c71786a68366333356b376135786c6d663563726467677537366c307572657475787333386839"], - [null, "04da0d94cb0a6397067a81a88ef422e56678e0ba232eb4dd6b05b98bc5e3461cd4a52b366a7df1f3a871854bfe1492711dc5130b35441748caa2742959279ce31e2b3604995d4ccedc4618ad16cdd2c0d42a6d36fb3a0610054cedef30beca20d187f32ce02f8ca357b575e705cda7ef8e1c68b9110381bd3958e0659a084205", "9bf5e479a60cfa3893a2a7693488b3fa016402851263e63c48540b3daff13814cd71a2ab1f78c561c768a3641dcf7a7d6b473393a629841c81f93afa87689035a119c9f7dac6fb9e7cf8fd6aa40f5f7f9e75b161d5f7269d28a4211ab84a8408", 65532, null, "75766965773135797678377477766a376a726a67766d6e6c797474683632396e7861746c7837686e79727a707a3468337a74776a79786c7733793770657a737671397276643470726779326474726a6c736d6c326c76366c6a7575306a7274766b6d6d366c6a32793768706c70686b383978656b66636565687a637a6777773237756538763272376e3077727339636b78376134756c6365766b777830363077667139613379357932746d337132777163756c796d3232396668387474386a6d376c683863367639707a67613274396e6c78673630377478353673303775757177337673336d78663676716b68353071636a33677678347664367a367379356e3039387565636c396a707968737178333934677337323539786b33796c376764326e333370767371707979377064393734347271786e73343073327a323963796b3435766a763765756b713976777a3879306163383539707a7166386b6d6c6b767678723930716b326e30376c6e7264393061363467376b6472336366356a637665726c7173667134366e6e71777137386b68"], - ["546c1fe01f7e9c8e36d6a5e29d4e30a73594bf5098421c69378af1e40f64e12503f4e4887b87079d7b7647f01a93a73c7a7d517840575c3246710adbdd752ed2d6", "fc02759ff2853b5e9e15842bef22c76023d43db7c265f120ed175713195f1240f63aee02ec23f146e9e25d25605fbae5472cdedc3b4c31c66b76fe9e6e47eeca79d5fb6a84d152820daaf89e99551d068d99cdf9be065007bc25f245ea62631d17b3bc77f62f35bd4205e6f682b1f9e824ecea53e271b80ff6bc79ef68a20ab5", "c36559ed79d1fecbe1de93d57698e915802f0ccd097adb4e448caef8c48d6c1e439482a1e5636131cba024f891b3bb1dfe333773415eeecf7a42e37dbd934e3a97d92dc90afa0ec3e96eeed9238dfcb6da23799cff14d477368d9ddc330afe39", 65532, null, "757669657731796e727935646c356c676b6733656a61326b386d777779646661357a363465726674657936383365776561326e3068333436636134326e6d7561347170636b63787565707073366e326d656a6c6b64363930333772766d7a61343677306c6d3035676a7070686566756a7a6d6734663876366d7130746b676766376667727138307a6d6d6b756467737739397465687363396d6a65773966367238366b6463646a7a666e66773065326b6d713567646b6866617175797167676b64616d68727a726e6a756663777739386e6577647a7479726d353775757037673338383730703932763776787a727377336c6366797a37616e326473616637757733663672716b32747a6a396c66356371376378346a77767667776a3078787064723433786a6d63646b366b6c6668773379663672617168383676676c61706a636d6b6879776a67366b687161777561667077716a6b776a75756c6a646e756b68646c6b6e726c3878307674727075727335636b7a357734683030397674657165646478786e63666e736c6e6830687074723665766566366e7a393268723838766c616a79366b6378366d306a67613267706b7773736b347277773373766d6e61683437656b64687430713964647467666e7a6736736d6b656c66336a723834707473706c376e37646575397a306d65616b756b336b3867707768703532"], - [null, null, "13a3e9649004641d3fc4da1cdefc4c5c6c457b0aee6e668d1d57a58bc48b0e1501a9e2572d16a3cb72c7b539582a46d8f67823da02235d41d49c12788bceeb334500fef8adfc1bcbaa0eede5b48ea7d3e9ec2a774986aac43050c50aa24f1d0f", 65533, null, "757669657731686d3672366e6434747463707137786e326a307367796a336d63726e6633336b796b3574797271666c70663774327266336a6c6530713839616a6870327764746164397533646e76356b6c746171767576647a6c7770397679726378666535676b763078646a6578326e68707839336e65367876307770386130676c6e36746a6b76796b7174686166726e686e6b6633676138656a766676717636386735633974386a75656e7a6833396766776a64637238786d7476677937656d7465"], - [null, null, "33ca395730a107b148032a999349f6dc9983daab9d01ffb1180b56ba7038221d1bf174c534ae1adb7a61a30ad0e3b36c52cf90957491ed84f90b5bd42c76833ef0cf897fe579461bc8db0b37e2da35766f2ee00712416c87f0a679fc55d4791f", 65531, null, "7576696577316478386a3774756b7473686678737930336167706a68633878326d716137343338353861343274736c327335786b78647a706534617664677363647978366163676c35736c656a3978616e33307479616735707333706a6a7375677075307276343237656b6c336c78616c6738637378726568667670717635306672617772687868716b38766a387975353270336875333872786c3772726e6c766a663374776b7467336b67323667656765636d30786873726d673671667036327563"], - ["9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f540215761b6d5155ba66df44f224e914e4a8bb6f2fee6820a865ac9fbfb327c11507", null, "a71cccc7d3f4fd389ba45bc4dc4e9b3d91d1fdc70eb15ab92aced578409e272f15181f6ae4b8412caf1044dab7749111cdba8c43a4ffad1de39bb22d2dd150077e9343b886179115512836623a664e3125d2faf44a1308e3dbd525da73e46523", 65534, null, "75766965773166326d6d6a783867786e376a716a796a397872776c6b32656d61617770393561326e716a3366373574766b37737a766d6430686e6d7a6d79637732336773343870646a37676d326c36637573763464733935666d656d723972723966787838333370776a63336c7a353330376c787a7632366630757132756b70776167717730786c7a6a746838386476776536616a3339686d6e7661307561716b6a656c73373268386563757539753273636c30756c373239687a397437793537656b617978777375326638366164306c3536727237653374353378736333787333347075766167667564787734766a377039336c346377303232747238706b746567716d386e32737835687061747a3776357130636d3865767032356d77776b6767657568677366737a357078"], - ["1ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671021c3e1aa400191c4609ba93df32612f46dfa68aebcfb80778b7fb59bb563e0a7a", null, "3d301cbf5f38ad466992b6466c3377d9f314e6e0e456cd8879f9f9a2db838232c3c801b93141a1ddc29f1079a9b3b46126ed23aeabc4007912798cadf9cfc00e800540cf11186e253036ae5a53f875c50a4a7940021523a92d8b359c140acb31", 65534, null, "7576696577317237386b6434393237373270396371646165336a396a6a647572637a76357a75633632706b366b3371756674776339396735666c7a6366356470737a676b6d6574307a6533726d38746c33356d63647a686564616a74386e7a397877377676656c673275397137756a373876736372666d383466703937633770753867326e78376a3476346c3966633530637671756b396d37673536306d386e3468707972726e6c6a39726b377537726d657578303265723876727a727167326136767535766b786d6b66753368326834666b30777a336c337735657173676c393374777037616d6d3936726b6b30356d6e326b7970677a3863636e6c3778786e3973616b35787a6c65617072736c3630653573323870346e796875356b6a32776b35726c6676766e3434396d35"], - ["26c1039586a7afcf4a0d9c731e985d99589c8bb838e8aaf745533ed9e8ae3a1c02ca6a53e13c134c1fb70013b1c3c7f59788b28df32fac5c220f565bd926dca633", null, "ffdffc7112cf550135fa5476272b24289fcabc5401b16db739d1df8f4493f1143c3cc90b837152144de475182209b0c169559f4a91fb788403b3b7642cc99c0d519abc658bfae98e12c9878d9e16c22937cc8182ebac54f15e307a2e639da239", 65535, null, "75766965773139706d666b617678647639777a6867766670653070753678727366373967616e613832396c3277667537713833336e63667a71657732656c637579643674757139683770656472397871763068713461637768396e647332776436677474357633776530777964336374736463336866676b7968637779776d753733323076666b30636b6e70326e65706a64686d756b3375746365613267796d78306630327a6d6c61386a706137656c6d7768796b7434637371333739717a356133386579346b79353077373064353330797a6a6d686c7473637a32673832766a7479616b65737366323776397177736b79713734786c6a63333530663767793036306a686b6c673871727671746535656c6d637374336e6b30766b6d6c6e6d6d7437367337657363656a707263"], - [null, "deb352b3dfeb09d42a96e77ce38e8a6bd00a1052e565215e1325e0712703368c8dbc0cd5eb82701bbd5903e60d488ef8f8944940ca7d87efbbd6c1eacf9ede8019a3b8fde82c151e11321e54a49aaf6de8b9d6515816a5104fb6ea54ed33873dc4a6ba5a80c1ee0f78378db0052388372aecdde7f53fb35320bec33763ddaa51", "37e5d0367502f509f94f077449cbd07473b9d2b630a5b4f8cefa689543d27028b01e4b736cc5620dc82093b4077b12fab582f225f3a5e58a76921cdad17dd70d458aee1ef78af37e9aadfffbd95db1046ad5b9f29ea4297050cadf9825721632", 65533, "857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a", "757669657731756d6b78787175716e7377307a73717a6175327235376d3863706a75363576787865636d6437686c726d677367326c63653932396c6a3563776c636e65707973636577306371353577646d657072767476337a707665356465727976716d726c7035796770346c733264716d796764636738636d6132763878656e6e7a637575326132736c6c7938646c646479757a717573677064736e7a61717933747332736c70746b6d386a737567766a333938766e336b7472643975757167336b3376737a33687a7a6361736c3765646e676868366c78707477746e6468377961616665796c6372353870686b66636635307a6b64666d6b36736133366e36646a687536636e68347278663530766b79396a767663663075707a346479713876367465687732673661616b737730747873336530646b3973763336613371797874356d68636373366768387265336736757633327a3370737879767133617930776e7332616b756666376a333833677130757036706d6e3467366c763937666a646d73397a336735777261383338786668657a776361363574796174333078637933326a76357279356e37613363306d3036713763616e71736e72366133673330716b387265616b717a716d64366b656338667663336d7a6a6e6d39653564376564686a6c36357a67636c743272756e6a34337a6d6d73327665636b7176366e3338716477303863736d7378746b643068796666366a6e3266377732376434726a7168326c7a637638386d30383763393839"], - ["d715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f803ff3f19851764670df1660c9cb56918abc2a0691eb8b89e705eb8e65b0f4e0dcb", "068d407022db8e5dd0730882bd54851ca0797a00dff60e358d28a04df20793838ba1618a6871da256cdbf7d8ad2ce81d9ce01df64765bc7c14ac74e7ed60129090fcb061b3e672a742aa0f5db728265947355b6375f3d2226b25129dced2e0991d02f739d2d822df5d41edc122b2330916ba36ca09e80cf07f99be4a45fce8e7", "d0e5967a78a4ba305dc8c47ad6ea668d2e88e11575d2ec1f082c740b5fdc050ddb57fa080007c5d868dd8670904b76a82959e8482795ab3ba4cbdc7bb37b1822d2f3ccda83c005fe5c571dbec5074f159f345ad2f60edf8364e4ddf983f91235", 65533, "ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e95", "757669657731666b6e393734747468686363306b71796d726a7467637370646c7170613768346e356e7439386b76773761326a676a686174706a713478656a76673879646e747074706e327a6c64707471386371716e646a6c67336c6b786b66776a7635726e39767638636a3666783567307a387a66746e6470726b7073387937656d34737a79327679386a70336832653678386b64717170656e356865387272326466797072767763713563756e7639396c67306a6b397233796c777064617a723779757a357938756a773071366e333939756d643236366a37396a7371776d6c7967767533776e666b797a3335647964687861706c63327578713572613237396e77396e73776379326837676873673930376d70733279686578666876323576776c343461383833376a63777336766365776a7434347a357333336e71756c396d35737a6d6e6179346476763364777568377a796871687732376d753777376a736b30756e6b73787363617a3372616d7973386579667a7a6475636e6765657764357738707433687478666a66786b657a65757130326d6c6333646b636163716b736c716761356b33656c3064746775796771686a3976793572646b70746d7274633432776c3336667676346b6d6b66793735677377776b72796e3835656e6e32396d75396a6c35326e7730717a7967747a366879347a653535756e707071647133346371767237766374777132657a676c716432666b79676161657072746c66376b65707277763871307571383865657771746a756b6c6a3973713933323675717a76786b727837773772716e7a68633361777138367938796a3371386868633472356a6e3864637a7766613934763367307572716173706334727132636878767a3772776e6872683332386168326c6168707776656134747771"] + ["18d9614fc820905d042bb1ef9ca3f24988c7b3534201cfb1cd8dbf69b8250c1802820346fcec8ea1276db2e615b8ced2fe4cf7468c1ef453146195b4d7bc13a4dc", "04da0d94cb0a6397067a81a88ef422e56678e0ba232eb4dd6b05b98bc5e3461cd4a52b366a7df1f3a871854bfe1492711dc5130b35441748caa2742959279ce31e2b3604995d4ccedc4618ad16cdd2c0d42a6d36fb3a0610054cedef30beca20d187f32ce02f8ca357b575e705cda7ef8e1c68b9110381bd3958e0659a084205", null, 65530, null, "75766965773171686334726b616c78373730716e66746d656b3578383770356c74756570336e6d6e6e766a716439736b656139637039333668387264706332736468737876633036636b376e7466746e78703970766564706b67393765356a6377727170726364686a6e3771357774333467656c7561356166636632666b737a33356b7333636c3764703472306330786a6364756a357073336c707435346c7177707036726c676e347278383878376e643034786470326461756a706765676a6a397436666c34326464386c3667683730746b70756a366b3276376e786b686c7568356a736b356c3461746d35726577646771646d796e326568763232766e73353665656468756167787171736d356833303764326a767832396466787530396a3433666a687a646637707664373277706e356e666d74767a7376726e75387079796166713478376e716467636372726d656675713664667a3535753078367a6e"], + ["25946f62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d62f5a8d7551db503062caf06c89638774b698aaafc8c834fb2696f70c3cdc70f4528f4ddae53f9e1", "fc02759ff2853b5e9e15842bef22c76023d43db7c265f120ed175713195f1240f63aee02ec23f146e9e25d25605fbae5472cdedc3b4c31c66b76fe9e6e47eeca79d5fb6a84d152820daaf89e99551d068d99cdf9be065007bc25f245ea62631d17b3bc77f62f35bd4205e6f682b1f9e824ecea53e271b80ff6bc79ef68a20ab5", "5aefc74861debd6c707786cd82c4a5d1ea088bba9ce4ea19851af7fabb90cd31055096c89f206607d6542b4f75856cecdcfcf65450a32cba628a1d0cb899ea3e9cc926110769fd9c685c6e087aa2e49b8d7646b04611e19be1da51c4953cf02e", 65532, "1a035587d5fb1a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a537edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f73476", "7576696577316436307463727173657a32757570773473303679667278717534716b30746d37716b6768336c36766d6b6d717676706567386777367478726b736a3265366d7175636a3776396a3274796a71726a72656d666535736d6e797877303035663570617164617474397073783270746d686b71797563726538346d7868337636637763703072363470357473346e71726b6678367a377a746c7473773378716b6e646d6c7261326767726a673571613861666e32387779756630346c6637617a636a6d63713771797872703634763072306e786e66306679796e70687a3361757965727473356e306d7137397176383263326a3732796d32616d366b356130767a37616a67676a39367870716b6c7277716a64743632707076796c397578737a7730363532793538776c63346d6a6475323464303775376e733463656567736568307a633633327932646b79376764616b366678363278776c6367307963676d3639616b3078657077333264716d6d357274386b6d6d6a7a78706a36676b687566766b3635773073676d6c346d617a7566773664737377786c34346a6b7535796a72306b33367176686467787072757266766c3370676d7133353832707963303438387677797138767232777966716d376432793277356d636c306138366765357178703832396178613971736776666e3839373032336d70716376347867726861336c366d6a323067783070796c6a753366747264667666706a78666b677273373967397478643237617373777035656a367735376c736467797067776a656a776a6a7867386a63343367676a7765646c7778666664687179717061687576336a367264687361736b6a77753566376d706a3371746467717a397767346468797276326172746c746378753375617633346d303630666d35383739677267637763376372326866346679367239367030347a647a75723766676433367135747268676a66687074383472327a7574756e38747576386d786d666538336e7a756a3572646b78356e78793471663075617274666e376633646a7971796870337a6e34"], + [null, null, "b4da1de64ff7ae804cd823f0d7b9ebd420beadef674b68399c7a31a3ff8b1b1edebd757e754423ddc889be2562618c1bb94921c987e09de96a0e6c591a453f0cc61bf8d4d70bf1825a1210499e2f7cf030be723958d6e939026434aa7aa68039", 65535, null, "75766965773165773575746379656872706c306c38683534793368757366676770796d3435347a727136747539777439797a7139356a6b6c38736a336d6a6879726b353778356a7975767837746666326c677a6a6779707472786a7979346833786336386639686a796d75746e3071306d64396737787872667a3772727a39746132756c736732636b74726464763338306b33767935777768766c7135657866796d366564736d67717467617537327338733461796873367434746d71756130713236"], + [null, null, "e72af55323ac5bc329a0c75bbb72c89ff5467b801d5783b66b39d02f87d03a117765db7fb65b6a4719958328d949adcbbe6b5bf92ad2539b6cc31948f3884a2811d3ae941ba3d218d7463f2b7dc158aa97d91fe06c2595a572c6c015c8647224", 65530, "596555ed9494c6ac893c49723833ec8926c1039586a7afcf4a0d9c731e985d9958", "75766965773174657a306b366a776e703661717a716166366670366a3865366c7771746d637a3868746e333536686b67356e756b373033666173337266777761743333653361616b3039306173616a6c737972756b366d6c7135646a713932776571743339677a37763577783337383873676533343571766a346e6d653335677276336477733466743461797371767132716a7a7a63767965793072776767797677366d79727136723076306370367870387a71386a63653677643664666768667175726435687977377970753379347565726a3737776873383071666c3739366370386a3339766836773561616171386a7638396e63756b67346d3537"], + [null, "1bec3da863b3274f4a93458559355066d32fce9d9cb051b2f42ef0ca425361b71ae8f4a40e143cb905b0471ae24781549de69ceeb582db050392e05c6e794685c225a75133434ad8f7c194576e65c0fe25bd7b1000ad07cf3dc63d6c6b7740ff8358a5cd3634092fc31611df64a2206eab0c5b8bcf056738f00bccbc615ffc87", null, 65533, null, "7576696577316771686c6c39657a68616632786177616a7677326c6e783273786d766b6135636d7677766d3879653065677064356a6e6a7132377574306d777861767a3067676465736d666d6b6a387633647167786d637878736667636a7865737a61797535717761333738743966743436763961386837726d37657466757864686d786661783674766468737161786c6c6a613876677633716133303768747270747674386d666a7270396c72396a6a7435386530393466763967653577327777646d326177797476727038653877703333736e6e726e753432336a6863687a776b386630686c363865616e7375716d6c61336e74"], + [null, "9d7e04ae88417ec87f82f4df18f35785b49ec1910e340d6b42e6248c88e2343691b2f0885065aaead6820bdc9dbc52a01bffad9b09298c7feb3002d9a863890ab681421ab447005e6cd49233d5ec66b92b63c78340f0cc23823f775acb1ef11f0d393f2be9b02bfd366cf1aaa0bb9328b2ad02f9adddef2e9aebed718c0e7006", "c4d6f75acbd5a42b053c2d2db17ed40d90718db0959cabffda2fcfe3dde734086ebfafd77b3ebf3648ee0f4ba5451356b18194bbfdb5fb4c4fe5a1fff11f3e2ef708c094983000425633d46329ffab857376718606fdf81b9224d0a6de700820", 65534, null, "75766965773176706a7a397664797973637579736e6b78756b6b6766757775726e36376b7a337a3776713670646a756a6a6c7670333971657a65343071766577307a397334656b64646634787368756b613635397863676137736b7276387263786667656c6d7a336135307663667276367130706d73373633616133306a386764376863376b726c326a726c7930303378783275677972377733727473666e38737661766b6c70727639766c66716e336335616c6e617164677475726d7677647a6d30686d6361326a636774717270677067343678756a78796d37336b613571673263723675783571646d7a3238366436796532653930736168717065726d686b636a6335646a6734373965786c727165766a767a356b673737767a6b306c6536673034756d787372717178336b70726d676c39656e713232717270397067327271733564327039357565777134327765643865646361756a7533676d6c33706666676e356868703936656e307563613834376463326c743261667933656437617777343964656575707a6767676468346566"], + [null, "625a20b874aef2dc146f22ad48c8f4516b32fbd4af3cfc94f6a995793114366a2d888601a6fa9d1293ba725f1ca94da2dddf92e7b6272069fe9599d540712e2f88ca41c967de424819a382a1d7f59e4d1e7452ee22a52884f52b0c71dd1eda58923e035e1adcb42846e5c3e29569e875b9d63f3c40b21445547d0a789a1c4030", "72c77b46af0a4173619e7e850b3e1ccbe137e3e83db05254699975bbd21cc82d28f537bb5c4574881ade4017bef217971791ba31998a37a99fe002c063d83c3cac9da4ed6d9191bfcf8a7e68590f65ec4e3efe827e7e8ea1e2a9b0680097b206", 65534, null, "75766965773134336c7577783479367970336565687763726e306b3479363736336c6c34747737356d703573617461343066357678636d3971753375706e39676365307378307875366a6a6336326b34707038746b7674743864753464396c38656371666e67667765336d6573717972347465666738383336357a3730706b63783067637a78647468356a3671306465383376366d637137366170687572787a333065347a6171326b7679397871716778776e3030647a6e63716538686539667567787968756a6434636d71363230633579736579326c3335326d746834376e35716c66646a3865796a6570797664706e7a326e37786a6863683374676e6463337463793866683564667235747771787a6b706e393238766c7467663036346a6b32796733397435666c716c78357473613538347a7764376774767779386c6736327968677139336c7632726b3474786668397a7833383433616a7265373339786a6878657338733536386167776438616b6339307a666a64393578726a307a6c71683234783970793977396366726b797579"], + [null, null, "226a052082409593988363063442bdd5907305bd83a40500233d7581c5058d26f69cc86e427bf98d16458b14b74fff5eb119d5ab0786e7a95356359b72a242354bdc50b58d380db7f9424b9f687098803e69a3b6dbee285dacde54b03e56981f", 65534, null, "757669657731786b706a397072336c373372637534306c796a38767a6e796a796439336d726872763664787570676868367a676d3333366678716d617a7776306e37637277686a3935726d79666b7272717a397a777579787434327477747371723537347a7a7479656a65336e776872336e7573397364716c366b7566687768687a32726a777239746b336d716d7176706173386a7732703230787267337773777170786d6377393067766b363665766b6a7a6d307a6e377a61373371767976733479"], + ["12b56da9c382857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984039b50a122f26ede2e13ff63b130c90f4c0f951ae8a81ddc1a4c987754f144c536", "068d407022db8e5dd0730882bd54851ca0797a00dff60e358d28a04df20793838ba1618a6871da256cdbf7d8ad2ce81d9ce01df64765bc7c14ac74e7ed60129090fcb061b3e672a742aa0f5db728265947355b6375f3d2226b25129dced2e0991d02f739d2d822df5d41edc122b2330916ba36ca09e80cf07f99be4a45fce8e7", "7ee622df8c71e3da6e509aae7ab0171e275a573b13bf7c2016515bd90c2e8c24524ff7f72de2274b130a87eec6a9b3558d31ef78b022230b2b41aeb553db940b77094cceda509601cad94dfac33cb5fe70938d47b3839a612a7048e912f5ef21", 65534, null, "7576696577313264777a6d7467323475397861787a36746e37763575366d6d73676e6e7765746a3476386d67356d3566306b75636d6b357a3375783878707573376663333468616733616b66753337373772717439747932386d3930356a3974766d6e716a6c6b6c6139653437373979326739767a703778713839397879716e37356d7830676b6b733675356d64736e356c66676461756866667066337874646d7138676c3078783964727773733863723933337774346a753861636e32327035796738656b71786c36706b673637746b7176356a7a6a7a6a757464703461706732336b6c75307a687835386d6c6b786a63616a6c6736616e7534746d34386a6b616d747234336e6b39737a35307678737a633477357732326836783739647371677733777868726e666d70717973686135646c73726e3332353667633066677339706e34726b6766657a353971387067367438667739663563786476616676763938613077707232726373703534706c756b7571376c70617068766c323571666677746b3477377a7a74703466656c733865383339326d66737966666377716e766630756b30727a32346636326d6577676d36736a6b6663337163326878383673706b75737433797664386665656b3276656171766168613975756472636c357475367461366672336a3032676a796875756e76386463686c6668356b"] ] From b305ad289255dbb737318910d89917a9041c0a3b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 16 Dec 2021 16:28:43 -0700 Subject: [PATCH 231/514] Remove the `InvalidEncoding` type from key & address variants. The presence of this variant results in a situation where more of the code than necessary needs to be aware of and handle decoding failures. This change moves all handling of decoding failures to the point of decoding. --- src/consensus/params.cpp | 7 +- src/gtest/test_keys.cpp | 30 +-- src/init.cpp | 7 +- src/key_io.cpp | 18 +- src/key_io.h | 6 +- src/miner.cpp | 15 +- src/miner.h | 24 +- src/rpc/misc.cpp | 6 +- src/test/key_tests.cpp | 24 +- src/util/match.h | 24 ++ .../asyncrpcoperation_mergetoaddress.cpp | 4 +- .../asyncrpcoperation_saplingmigration.cpp | 7 +- src/wallet/asyncrpcoperation_sendmany.cpp | 42 ++-- .../asyncrpcoperation_shieldcoinbase.cpp | 9 +- src/wallet/asyncrpcoperation_shieldcoinbase.h | 1 - src/wallet/gtest/test_wallet.cpp | 26 +- src/wallet/rpcdump.cpp | 24 +- src/wallet/rpcwallet.cpp | 224 ++++++++++-------- src/wallet/test/rpc_wallet_tests.cpp | 20 +- src/wallet/wallet.cpp | 50 +--- src/wallet/wallet.h | 11 +- src/zcash/Address.cpp | 32 +-- src/zcash/Address.hpp | 30 +-- 23 files changed, 292 insertions(+), 349 deletions(-) create mode 100644 src/util/match.h diff --git a/src/consensus/params.cpp b/src/consensus/params.cpp index 2ced730e82c..58528b7b498 100644 --- a/src/consensus/params.cpp +++ b/src/consensus/params.cpp @@ -169,10 +169,11 @@ namespace Consensus { addresses.push_back(GetScriptForDestination(taddr)); } else { auto zaddr = keyIO.DecodePaymentAddress(addr); - // If the string is not a valid transparent or Sapling address, we will - // throw here. + if (!(zaddr.has_value() && std::holds_alternative(zaddr.value()))) { + throw std::runtime_error("Funding stream address was not a valid transparent or Sapling address."); + } - addresses.push_back(std::get(zaddr)); + addresses.push_back(std::get(zaddr.value())); } } diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index b7aef27bbf2..a632f3deca5 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -27,11 +27,11 @@ TEST(Keys, EncodeAndDecodeSapling) Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string); - EXPECT_TRUE(IsValidSpendingKey(spendingkey2)); + EXPECT_TRUE(spendingkey2.has_value()); - ASSERT_TRUE(std::get_if(&spendingkey2) != nullptr); - auto sk2 = std::get(spendingkey2); - EXPECT_EQ(sk, sk2); + auto sk2 = std::get_if(&spendingkey2.value()); + EXPECT_NE(sk2, nullptr); + EXPECT_EQ(sk, *sk2); } { auto extfvk = sk.ToXFVK(); @@ -41,11 +41,11 @@ TEST(Keys, EncodeAndDecodeSapling) Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK)); auto viewingkey2 = keyIO.DecodeViewingKey(vk_string); - EXPECT_TRUE(IsValidViewingKey(viewingkey2)); + EXPECT_TRUE(viewingkey2.has_value()); - ASSERT_TRUE(std::get_if(&viewingkey2) != nullptr); - auto extfvk2 = std::get(viewingkey2); - EXPECT_EQ(extfvk, extfvk2); + auto extfvk2 = std::get_if(&viewingkey2.value()); + EXPECT_NE(extfvk2, nullptr); + EXPECT_EQ(extfvk, *extfvk2); } { auto addr = sk.DefaultAddress(); @@ -56,11 +56,11 @@ TEST(Keys, EncodeAndDecodeSapling) Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string); - EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2)); + EXPECT_TRUE(paymentaddr2.has_value()); - ASSERT_TRUE(std::get_if(&paymentaddr2) != nullptr); - auto addr2 = std::get(paymentaddr2); - EXPECT_EQ(addr, addr2); + auto addr2 = std::get_if(&paymentaddr2.value()); + EXPECT_NE(addr2, nullptr); + EXPECT_EQ(addr, *addr2); } } } @@ -152,8 +152,10 @@ TEST(Keys, EncodeAndDecodeUnified) std::string expected(expectedBytes.begin(), expectedBytes.end()); auto decoded = keyIO.DecodePaymentAddress(expected); - ASSERT_TRUE(std::holds_alternative(decoded)); - EXPECT_EQ(std::get(decoded), ua); + EXPECT_TRUE(decoded.has_value()); + auto ua_ptr = std::get_if(&decoded.value()); + EXPECT_NE(ua_ptr, nullptr); + EXPECT_EQ(*ua_ptr, ua); auto encoded = keyIO.EncodePaymentAddress(ua); EXPECT_EQ(encoded, expected); diff --git a/src/init.cpp b/src/init.cpp index b632d7e2b29..040ff0707d2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1102,7 +1102,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!IsValidDestination(addr)) { // Try a payment address auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); - if (!std::visit(IsValidMinerAddress(), std::visit(ExtractMinerAddress(), zaddr))) + if (!zaddr.has_value() || std::visit(ExtractMinerAddress(), zaddr.value()).has_value()) { return InitError(strprintf( _("Invalid address for -mineraddress=: '%s' (must be a Sapling or transparent address)"), @@ -1684,8 +1684,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) minerAddressInLocalWallet = pwalletMain->HaveKey(keyID); } else { auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); + if (!zaddr.has_value()) { + return InitError(_("-mineraddress is not a valid zcash address.")); + } minerAddressInLocalWallet = std::visit( - HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); + HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); } } if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) { diff --git a/src/key_io.cpp b/src/key_io.cpp index db83bea04fd..671cc8d3481 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -151,8 +151,6 @@ class PaymentAddressEncoder zcash_address_string_free(encoded); return res; } - - std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; class ViewingKeyEncoder @@ -189,8 +187,6 @@ class ViewingKeyEncoder memory_cleanse(data.data(), data.size()); return ret; } - - std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; class SpendingKeyEncoder @@ -227,8 +223,6 @@ class SpendingKeyEncoder memory_cleanse(data.data(), data.size()); return ret; } - - std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; // Sizes of SaplingPaymentAddress, SaplingExtendedFullViewingKey, and @@ -356,7 +350,7 @@ std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) } template -T1 DecodeAny( +std::optional DecodeAny( const KeyConstants& keyConstants, const std::string& str, std::pair sprout, @@ -393,7 +387,7 @@ T1 DecodeAny( } memory_cleanse(data.data(), data.size()); - return libzcash::InvalidEncoding(); + return std::nullopt; } /** @@ -447,7 +441,7 @@ static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* return reinterpret_cast(ua)->AddReceiver(receiver); } -libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str) +std::optional KeyIO::DecodePaymentAddress(const std::string& str) { // Try parsing as a Unified Address. libzcash::UnifiedAddress ua; @@ -475,7 +469,7 @@ libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str) } bool KeyIO::IsValidPaymentAddressString(const std::string& str) { - return IsValidPaymentAddress(DecodePaymentAddress(str)); + return DecodePaymentAddress(str).has_value(); } std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) @@ -483,7 +477,7 @@ std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) return std::visit(ViewingKeyEncoder(keyConstants), vk); } -libzcash::ViewingKey KeyIO::DecodeViewingKey(const std::string& str) +std::optional KeyIO::DecodeViewingKey(const std::string& str) { return DecodeAny KeyIO::DecodeSpendingKey(const std::string& str) { return DecodeAny DecodePaymentAddress(const std::string& str); bool IsValidPaymentAddressString(const std::string& str); std::string EncodeViewingKey(const libzcash::ViewingKey& vk); - libzcash::ViewingKey DecodeViewingKey(const std::string& str); + std::optional DecodeViewingKey(const std::string& str); std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey); - libzcash::SpendingKey DecodeSpendingKey(const std::string& str); + std::optional DecodeSpendingKey(const std::string& str); }; #endif // BITCOIN_KEY_IO_H diff --git a/src/miner.cpp b/src/miner.cpp index 0ed57273e58..d2a21a9870f 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -117,9 +117,7 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, } bool IsShieldedMinerAddress(const MinerAddress& minerAddr) { - return !( - std::holds_alternative(minerAddr) || - std::holds_alternative>(minerAddr)); + return !std::holds_alternative>(minerAddr); } class AddFundingStreamValueToTx @@ -241,8 +239,6 @@ class AddOutputsToCoinbaseTxAndSign } } - void operator()(const InvalidMinerAddress &invalid) const {} - // Create shielded output void operator()(const libzcash::SaplingPaymentAddress &pa) const { auto ctx = librustzcash_sapling_proving_ctx_init(); @@ -721,9 +717,12 @@ void GetMinerAddress(MinerAddress &minerAddress) minerAddress = mAddr; } else { // Try a payment address - auto zaddr = std::visit(ExtractMinerAddress(), keyIO.DecodePaymentAddress(mAddrArg)); - if (std::visit(IsValidMinerAddress(), zaddr)) { - minerAddress = zaddr; + auto zaddr0 = keyIO.DecodePaymentAddress(mAddrArg); + if (zaddr0.has_value()) { + auto zaddr = std::visit(ExtractMinerAddress(), zaddr0.value()); + if (zaddr.has_value()) { + minerAddress = zaddr.value(); + } } } } diff --git a/src/miner.h b/src/miner.h index 52ac1a29d6b..61dcc938b22 100644 --- a/src/miner.h +++ b/src/miner.h @@ -23,14 +23,7 @@ static const int DEFAULT_GENERATE_THREADS = 1; static const bool DEFAULT_PRINTPRIORITY = false; -class InvalidMinerAddress { -public: - friend bool operator==(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; } - friend bool operator<(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; } -}; - typedef std::variant< - InvalidMinerAddress, libzcash::SaplingPaymentAddress, boost::shared_ptr> MinerAddress; @@ -39,16 +32,13 @@ class ExtractMinerAddress public: ExtractMinerAddress() {} - MinerAddress operator()(const libzcash::InvalidEncoding &invalid) const { - return InvalidMinerAddress(); + std::optional operator()(const libzcash::SproutPaymentAddress &addr) const { + return std::nullopt; } - MinerAddress operator()(const libzcash::SproutPaymentAddress &addr) const { - return InvalidMinerAddress(); - } - MinerAddress operator()(const libzcash::SaplingPaymentAddress &addr) const { + std::optional operator()(const libzcash::SaplingPaymentAddress &addr) const { return addr; } - MinerAddress operator()(const libzcash::UnifiedAddress &addr) const { + std::optional operator()(const libzcash::UnifiedAddress &addr) const { auto recipient = RecipientForPaymentAddress()(addr); if (recipient) { // This looks like a recursive call, but we are actually calling @@ -66,7 +56,7 @@ class ExtractMinerAddress // Either the UA only contains unknown shielded receivers (unlikely that we // wouldn't know about them), or it only contains transparent receivers // (which are invalid). - return InvalidMinerAddress(); + return std::nullopt; } } }; @@ -76,7 +66,6 @@ class KeepMinerAddress public: KeepMinerAddress() {} - void operator()(const InvalidMinerAddress &invalid) const {} void operator()(const libzcash::SaplingPaymentAddress &pa) const {} void operator()(const boost::shared_ptr &coinbaseScript) const { coinbaseScript->KeepScript(); @@ -90,9 +79,6 @@ class IsValidMinerAddress public: IsValidMinerAddress() {} - bool operator()(const InvalidMinerAddress &invalid) const { - return false; - } bool operator()(const libzcash::SaplingPaymentAddress &pa) const { return true; } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index dffee0b8b5f..c09b694bfa5 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -217,8 +217,6 @@ UniValue validateaddress(const UniValue& params, bool fHelp) class DescribePaymentAddressVisitor { public: - UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); } - UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const { UniValue obj(UniValue::VOBJ); obj.pushKV("type", "sprout"); @@ -288,14 +286,14 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); string strAddress = params[0].get_str(); auto address = keyIO.DecodePaymentAddress(strAddress); - bool isValid = IsValidPaymentAddress(address); + bool isValid = address.has_value(); UniValue ret(UniValue::VOBJ); ret.pushKV("isvalid", isValid); if (isValid) { ret.pushKV("address", strAddress); - UniValue detail = std::visit(DescribePaymentAddressVisitor(), address); + UniValue detail = std::visit(DescribePaymentAddressVisitor(), address.value()); ret.pushKVs(detail); } return ret; diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 959bb25da4c..2de4e28f464 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -202,9 +202,9 @@ BOOST_AUTO_TEST_CASE(zc_address_test) BOOST_CHECK(sk_string[1] == 'K'); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string); - BOOST_CHECK(IsValidSpendingKey(spendingkey2)); - BOOST_ASSERT(std::get_if(&spendingkey2) != nullptr); - auto sk2 = std::get(spendingkey2); + BOOST_CHECK(spendingkey2.has_value()); + BOOST_ASSERT(std::get_if(&spendingkey2.value()) != nullptr); + auto sk2 = std::get(spendingkey2.value()); BOOST_CHECK(sk.inner() == sk2.inner()); } { @@ -216,10 +216,10 @@ BOOST_AUTO_TEST_CASE(zc_address_test) BOOST_CHECK(addr_string[1] == 'c'); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string); - BOOST_ASSERT(IsValidPaymentAddress(paymentaddr2)); + BOOST_ASSERT(paymentaddr2.has_value()); - BOOST_ASSERT(std::get_if(&paymentaddr2) != nullptr); - auto addr2 = std::get(paymentaddr2); + BOOST_ASSERT(std::get_if(&paymentaddr2.value()) != nullptr); + auto addr2 = std::get(paymentaddr2.value()); BOOST_CHECK(addr.a_pk == addr2.a_pk); BOOST_CHECK(addr.pk_enc == addr2.pk_enc); } @@ -240,10 +240,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test) BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string); - BOOST_CHECK(IsValidSpendingKey(spendingkey2)); + BOOST_CHECK(spendingkey2.has_value()); - BOOST_ASSERT(std::get_if(&spendingkey2) != nullptr); - auto sk2 = std::get(spendingkey2); + BOOST_ASSERT(std::get_if(&spendingkey2.value()) != nullptr); + auto sk2 = std::get(spendingkey2.value()); BOOST_CHECK(sk == sk2); } { @@ -253,10 +253,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test) BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string); - BOOST_CHECK(IsValidPaymentAddress(paymentaddr2)); + BOOST_CHECK(paymentaddr2.has_value()); - BOOST_ASSERT(std::get_if(&paymentaddr2) != nullptr); - auto addr2 = std::get(paymentaddr2); + BOOST_ASSERT(std::get_if(&paymentaddr2.value()) != nullptr); + auto addr2 = std::get(paymentaddr2.value()); BOOST_CHECK(addr == addr2); } } diff --git a/src/util/match.h b/src/util/match.h new file mode 100644 index 00000000000..d966ae4dac3 --- /dev/null +++ b/src/util/match.h @@ -0,0 +1,24 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_UTIL_MATCH_H +#define ZCASH_UTIL_MATCH_H + +// Helper for using `std::visit` with Rust-style match syntax. +// +// This can be used in place of defining an explicit visitor. `std::visit` requires an +// exhaustive match; to emulate Rust's catch-all binding, use an `(auto arg)` template +// operator(). +// +// Care must be taken that implicit conversions are handled correctly. For instance, a +// `(double arg)` operator() *will also* bind to `int` and `long`, if there isn't an +// earlier satisfying match. +// +// This corresponds to visitor example #4 in the `std::visit` documentation: +// https://en.cppreference.com/w/cpp/utility/variant/visit +template struct match : Ts... { using Ts::operator()...; }; +// explicit deduction guide (not needed as of C++20) +template match(Ts...) -> match; + +#endif // ZCASH_UTIL_MATCH_H diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index f6faaa473a8..b3ea22bbee3 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -102,9 +102,9 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( if (!isToTaddr_) { auto address = keyIO.DecodePaymentAddress(std::get<0>(recipient)); - if (IsValidPaymentAddress(address)) { + if (address.has_value()) { isToZaddr_ = true; - toPaymentAddress_ = address; + toPaymentAddress_ = address.value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address"); } diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 6671298c7d1..d7f48fc60bb 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -85,7 +85,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { // We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying // an anchor at height N-10 for each Sprout JoinSplit description // Consider, should notes be sorted? - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 11); } CAmount availableFunds = 0; for (const SproutNoteEntry& sproutEntry : sproutEntries) { @@ -197,8 +197,9 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration if (mapArgs.count("-migrationdestaddress")) { std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; auto address = keyIO.DecodePaymentAddress(migrationDestAddress); - auto saplingAddress = std::get_if(&address); - assert(saplingAddress != nullptr); // This is checked in init.cpp + assert(address.has_value()); // This is checked in init.cpp + auto saplingAddress = std::get_if(&address.value()); + assert(saplingAddress != nullptr); // This is also checked in init.cpp return *saplingAddress; } // Derive the address for Sapling account 0 diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 155afd70087..30b8992070c 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -98,15 +98,15 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( if (!isfromtaddr_) { auto address = keyIO.DecodePaymentAddress(fromAddress); - if (IsValidPaymentAddress(address)) { + if (address.has_value()) { // We don't need to lock on the wallet as spending key related methods are thread-safe - if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), address)) { + if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), address.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, no spending key found for zaddr"); } isfromzaddr_ = true; - frompaymentaddress_ = address; - spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address).value(); + frompaymentaddress_ = address.value(); + spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()).value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address"); } @@ -375,8 +375,8 @@ bool AsyncRPCOperation_sendmany::main_impl() { auto hexMemo = r.memo; auto addr = keyIO.DecodePaymentAddress(address); - assert(std::get_if(&addr) != nullptr); - auto to = std::get(addr); + assert(addr.has_value() && std::get_if(&addr.value()) != nullptr); + auto to = std::get(addr.value()); auto memo = get_memo_from_hex_string(hexMemo); @@ -533,12 +533,14 @@ bool AsyncRPCOperation_sendmany::main_impl() { std::string hexMemo = smr.memo; zOutputsDeque.pop_front(); - PaymentAddress pa = keyIO.DecodePaymentAddress(address); - JSOutput jso = JSOutput(std::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); + std::optional pa = keyIO.DecodePaymentAddress(address); + if (pa.has_value()) { + JSOutput jso = JSOutput(std::get(pa.value()), value); + if (hexMemo.size() > 0) { + jso.memo = get_memo_from_hex_string(hexMemo); + } + info.vjsout.push_back(jso); } - info.vjsout.push_back(jso); // Funds are removed from the value pool and enter the private pool info.vpub_old += value; @@ -800,13 +802,15 @@ bool AsyncRPCOperation_sendmany::main_impl() { assert(value==0); info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new } else { - PaymentAddress pa = keyIO.DecodePaymentAddress(address); - // If we are here, we know we have no Sapling outputs. - JSOutput jso = JSOutput(std::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); + std::optional pa = keyIO.DecodePaymentAddress(address); + if (pa.has_value()) { + // If we are here, we know we have no Sapling outputs. + JSOutput jso = JSOutput(std::get(pa.value()), value); + if (hexMemo.size() > 0) { + jso.memo = get_memo_from_hex_string(hexMemo); + } + info.vjsout.push_back(jso); } - info.vjsout.push_back(jso); } // create output for any change @@ -927,7 +931,9 @@ bool AsyncRPCOperation_sendmany::load_inputs(TxValues& txValues) { bool AsyncRPCOperation_sendmany::find_unspent_notes() { std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress_, mindepth_); + // TODO: move this to the caller + auto zaddr = KeyIO(Params()).DecodePaymentAddress(fromaddress_); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddr, mindepth_); // If using the TransactionBuilder, we only want Sapling notes. // If not using it, we only want Sprout notes. diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 31098c40418..0b1960b7647 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -81,8 +81,8 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( // Check the destination address is valid for this network i.e. not testnet being used on mainnet KeyIO keyIO(Params()); auto address = keyIO.DecodePaymentAddress(toAddress); - if (IsValidPaymentAddress(address)) { - tozaddr_ = address; + if (address.has_value()) { + tozaddr_ = address.value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid to address"); } @@ -264,11 +264,6 @@ bool ShieldToAddress::operator()(const libzcash::UnifiedAddress &uaddr) const { return false; } -bool ShieldToAddress::operator()(const libzcash::InvalidEncoding& no) const { - return false; -} - - UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) { uint32_t consensusBranchId; uint256 anchor; diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index badc7fa5a99..c1048c00a8f 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -106,7 +106,6 @@ class ShieldToAddress bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; - bool operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 33361a22bb3..142d8335793 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -206,11 +206,11 @@ TEST(WalletTests, FindUnspentSproutNotes) { // We currently have an unspent and unconfirmed note in the wallet (depth of -1) std::vector sproutEntries; std::vector saplingEntries; - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, -1); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); @@ -233,15 +233,15 @@ TEST(WalletTests, FindUnspentSproutNotes) { // We now have an unspent and confirmed note in the wallet (depth of 1) - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); @@ -271,22 +271,22 @@ TEST(WalletTests, FindUnspentSproutNotes) { EXPECT_TRUE(wallet.IsSproutSpent(nullifier)); // The note has been spent. By default, GetFilteredNotes() ignores spent notes. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // Let's include spent notes to retrieve it. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0, INT_MAX, false); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // The spent note has two confirmations. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, false); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // It does not have 3 confirmations. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 3, INT_MAX, false); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); @@ -329,22 +329,22 @@ TEST(WalletTests, FindUnspentSproutNotes) { wallet.AddToWallet(wtx3, true, NULL); // We now have an unspent note which has one confirmation, in addition to our spent note. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // Let's return the spent note too. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1, INT_MAX, false); EXPECT_EQ(2, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // Increasing number of confirmations will exclude our new unspent note. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, false); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // If we also ignore spent notes at this depth, we won't find any notes. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, true); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d6c3130b010..0ad8e24b16a 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -400,9 +400,9 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys) // Only include hdKeypath and seedFpStr if we have both std::optional hdKeypath = (vstr.size() > 3) ? std::optional(vstr[2]) : std::nullopt; std::optional seedFpStr = (vstr.size() > 3) ? std::optional(vstr[3]) : std::nullopt; - if (IsValidSpendingKey(spendingkey)) { + if (spendingkey.has_value()) { auto addResult = std::visit( - AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey); + AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey.value()); if (addResult == KeyAlreadyExists){ LogPrint("zrpc", "Skipping import of zaddr (key already present)\n"); } else if (addResult == KeyNotAdded) { @@ -751,17 +751,17 @@ UniValue z_importkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); string strSecret = params[0].get_str(); auto spendingkey = keyIO.DecodeSpendingKey(strSecret); - if (!IsValidSpendingKey(spendingkey)) { + if (!spendingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey); + auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey.value()); UniValue result(UniValue::VOBJ); result.pushKV("type", addrInfo.first); result.pushKV("address", keyIO.EncodePaymentAddress(addrInfo.second)); // Sapling support - auto addResult = std::visit(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey); + auto addResult = std::visit(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey.value()); if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { return result; } @@ -846,17 +846,17 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); string strVKey = params[0].get_str(); auto viewingkey = keyIO.DecodeViewingKey(strVKey); - if (!IsValidViewingKey(viewingkey)) { + if (!viewingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); } - auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey{}, viewingkey); + auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey{}, viewingkey.value()); UniValue result(UniValue::VOBJ); const string strAddress = keyIO.EncodePaymentAddress(addrInfo.second); result.pushKV("type", addrInfo.first); result.pushKV("address", strAddress); - auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain), viewingkey); + auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain), viewingkey.value()); if (addResult == SpendingKeyExists) { throw JSONRPCError( RPC_WALLET_ERROR, @@ -905,12 +905,12 @@ UniValue z_exportkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); auto address = keyIO.DecodePaymentAddress(strAddress); - if (!IsValidPaymentAddress(address)) { + if (!address.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } // Sapling support - auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address); + auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()); if (!sk) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr"); } @@ -944,11 +944,11 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); auto address = keyIO.DecodePaymentAddress(strAddress); - if (!IsValidPaymentAddress(address)) { + if (!address.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } - auto vk = std::visit(GetViewingKeyForPaymentAddress(pwalletMain), address); + auto vk = std::visit(GetViewingKeyForPaymentAddress(pwalletMain), address.value()); if (vk) { return keyIO.EncodeViewingKey(vk.value()); } else { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a23336794bc..83c63e554c0 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -18,6 +18,7 @@ #include "timedata.h" #include "transaction_builder.h" #include "util.h" +#include "util/match.h" #include "utilmoneystr.h" #include "wallet.h" #include "walletdb.h" @@ -2266,16 +2267,16 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) } string address = o.get_str(); auto zaddr = keyIO.DecodePaymentAddress(address); - if (!IsValidPaymentAddress(zaddr)) { + if (!zaddr.has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address); } - auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); + auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); if (!fIncludeWatchonly && !hasSpendingKey) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address); } // We want to return unspent notes corresponding to any receiver within a // Unified Address. - for (const auto ra : std::visit(GetRawAddresses(), zaddr)) { + for (const auto ra : std::visit(GetRawAddresses(), zaddr.value())) { zaddrs.insert(ra); } @@ -2597,13 +2598,13 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); auto spendingkey = keyIO.DecodeSpendingKey(params[0].get_str()); - if (!IsValidSpendingKey(spendingkey)) { + if (!spendingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - if (std::get_if(&spendingkey) == nullptr) { + if (std::get_if(&spendingkey.value()) == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout spending keys"); } - SproutSpendingKey k = std::get(spendingkey); + SproutSpendingKey k = std::get(spendingkey.value()); uint256 epk; unsigned char nonce; @@ -2723,13 +2724,13 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); for (const string& name_ : inputs.getKeys()) { auto spendingkey = keyIO.DecodeSpendingKey(inputs[name_].get_str()); - if (!IsValidSpendingKey(spendingkey)) { + if (!spendingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - if (std::get_if(&spendingkey) == nullptr) { + if (std::get_if(&spendingkey.value()) == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout spending keys"); } - SproutSpendingKey k = std::get(spendingkey); + SproutSpendingKey k = std::get(spendingkey.value()); keys.push_back(k); @@ -2770,11 +2771,13 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) } for (const string& name_ : outputs.getKeys()) { - auto addrTo = keyIO.DecodePaymentAddress(name_); - if (!IsValidPaymentAddress(addrTo)) { + auto addrToDecoded = keyIO.DecodePaymentAddress(name_); + if (!addrToDecoded.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address."); } - if (std::get_if(&addrTo) == nullptr) { + + libzcash::PaymentAddress addrTo(addrToDecoded.value()); + if (!std::holds_alternative(addrTo)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout payment addresses"); } CAmount nAmount = AmountFromValue(outputs[name_]); @@ -3123,70 +3126,77 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) auto fromaddress = params[0].get_str(); KeyIO keyIO(Params()); - auto zaddr = keyIO.DecodePaymentAddress(fromaddress); - if (!IsValidPaymentAddress(zaddr)) { + auto decoded = keyIO.DecodePaymentAddress(fromaddress); + if (!decoded.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); } // Visitor to support Sprout and Sapling addrs - if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), zaddr)) { + if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), decoded.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); } UniValue result(UniValue::VARR); std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, decoded, nMinDepth, false, false); std::set> nullifierSet; - auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); + auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), decoded.value()); if (hasSpendingKey) { - nullifierSet = pwalletMain->GetNullifiersForAddresses(std::visit(GetRawAddresses(), zaddr)); - } - - if (std::get_if(&zaddr) != nullptr) { - for (SproutNoteEntry & entry : sproutEntries) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("txid", entry.jsop.hash.ToString()); - obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); - obj.pushKV("amountZat", CAmount(entry.note.value())); - std::string data(entry.memo.begin(), entry.memo.end()); - obj.pushKV("memo", HexStr(data)); - obj.pushKV("jsindex", entry.jsop.js); - obj.pushKV("jsoutindex", entry.jsop.n); - obj.pushKV("confirmations", entry.confirmations); - - txblock BlockData(entry.jsop.hash); - obj.pushKV("blockheight", BlockData.height); - obj.pushKV("blockindex", BlockData.index); - obj.pushKV("blocktime", BlockData.time); - - if (hasSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); + nullifierSet = pwalletMain->GetNullifiersForAddresses(std::visit(GetRawAddresses(), decoded.value())); + } + + std::visit(match { + [&](libzcash::SproutPaymentAddress addr) { + for (SproutNoteEntry & entry : sproutEntries) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("txid", entry.jsop.hash.ToString()); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); + obj.pushKV("amountZat", CAmount(entry.note.value())); + std::string data(entry.memo.begin(), entry.memo.end()); + obj.pushKV("memo", HexStr(data)); + obj.pushKV("jsindex", entry.jsop.js); + obj.pushKV("jsoutindex", entry.jsop.n); + obj.pushKV("confirmations", entry.confirmations); + + txblock BlockData(entry.jsop.hash); + obj.pushKV("blockheight", BlockData.height); + obj.pushKV("blockindex", BlockData.index); + obj.pushKV("blocktime", BlockData.time); + + if (hasSpendingKey) { + obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); + } + result.push_back(obj); } - result.push_back(obj); - } - } else if (std::get_if(&zaddr) != nullptr) { - for (SaplingNoteEntry & entry : saplingEntries) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("txid", entry.op.hash.ToString()); - obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); - obj.pushKV("amountZat", CAmount(entry.note.value())); - obj.pushKV("memo", HexStr(entry.memo)); - obj.pushKV("outindex", (int)entry.op.n); - obj.pushKV("confirmations", entry.confirmations); - - txblock BlockData(entry.op.hash); - obj.pushKV("blockheight", BlockData.height); - obj.pushKV("blockindex", BlockData.index); - obj.pushKV("blocktime", BlockData.time); - - if (hasSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); + }, + [&](libzcash::SaplingPaymentAddress addr) { + for (SaplingNoteEntry & entry : saplingEntries) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("txid", entry.op.hash.ToString()); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); + obj.pushKV("amountZat", CAmount(entry.note.value())); + obj.pushKV("memo", HexStr(entry.memo)); + obj.pushKV("outindex", (int)entry.op.n); + obj.pushKV("confirmations", entry.confirmations); + + txblock BlockData(entry.op.hash); + obj.pushKV("blockheight", BlockData.height); + obj.pushKV("blockindex", BlockData.index); + obj.pushKV("blocktime", BlockData.time); + + if (hasSpendingKey) { + obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); + } + result.push_back(obj); } - result.push_back(obj); + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED } - } + }, decoded.value()); + return result; } @@ -3234,10 +3244,10 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) auto pa = keyIO.DecodePaymentAddress(fromaddress); fromTaddr = IsValidDestination(taddr); if (!fromTaddr) { - if (!IsValidPaymentAddress(pa)) { + if (!pa.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } - if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), pa)) { + if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), pa.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, spending key or viewing key not found."); } } @@ -3247,7 +3257,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) nBalance = getBalanceTaddr(fromaddress, nMinDepth, false); } else { // TODO: Return an error if a UA is provided (once we support UAs). - auto zaddr = std::visit(RecipientForPaymentAddress(), pa).value(); + auto zaddr = std::visit(RecipientForPaymentAddress(), pa.value()).value(); nBalance = getBalanceZaddr(zaddr, nMinDepth, INT_MAX, false); } @@ -3718,19 +3728,20 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) CTxDestination taddr = keyIO.DecodeDestination(fromaddress); fromTaddr = IsValidDestination(taddr); if (!fromTaddr) { - auto res = keyIO.DecodePaymentAddress(fromaddress); - if (!IsValidPaymentAddress(res)) { + auto decoded = keyIO.DecodePaymentAddress(fromaddress); + if (!decoded.has_value()) { // invalid throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } // Check that we have the spending key - if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), res)) { + libzcash::PaymentAddress addr(decoded.value()); + if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), addr)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); } // Remember whether this is a Sprout or Sapling address - fromSapling = std::get_if(&res) != nullptr; + fromSapling = std::holds_alternative(addr); } } // This logic will need to be updated if we add a new shielded pool @@ -3770,13 +3781,14 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) bool isZaddr = false; CTxDestination taddr = keyIO.DecodeDestination(address); if (!IsValidDestination(taddr)) { - auto res = keyIO.DecodePaymentAddress(address); - if (IsValidPaymentAddress(res)) { + auto decoded = keyIO.DecodePaymentAddress(address); + if (decoded.has_value()) { + libzcash::PaymentAddress addr(decoded.value()); isZaddr = true; - bool toSapling = std::get_if(&res) != nullptr; - bool toSprout = !toSapling; - noSproutAddrs = noSproutAddrs && toSapling; + bool toSapling = std::holds_alternative(addr); + bool toSprout = std::holds_alternative(addr); + noSproutAddrs = !toSprout && noSproutAddrs && toSapling; containsSproutOutput |= toSprout; containsSaplingOutput |= toSapling; @@ -3872,16 +3884,23 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) size_t txsize = 0; for (int i = 0; i < zaddrRecipients.size(); i++) { auto address = zaddrRecipients[i].address; - auto res = keyIO.DecodePaymentAddress(address); - bool toSapling = std::get_if(&res) != nullptr; - if (toSapling) { - mtx.vShieldedOutput.push_back(OutputDescription()); - } else { - JSDescription jsdesc; - if (mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)) { - jsdesc.proof = GrothProof(); - } - mtx.vJoinSplit.push_back(jsdesc); + auto decoded = keyIO.DecodePaymentAddress(address); + if (decoded.has_value()) { + std::visit(match { + [&](libzcash::SaplingPaymentAddress addr) { + mtx.vShieldedOutput.push_back(OutputDescription()); + }, + [&](libzcash::SproutPaymentAddress addr) { + JSDescription jsdesc; + if (mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)) { + jsdesc.proof = GrothProof(); + } + mtx.vJoinSplit.push_back(jsdesc); + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED + } + }, decoded.value()); } } CTransaction tx(mtx); @@ -4174,10 +4193,11 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) if (canopyActive) { auto decodeAddr = keyIO.DecodePaymentAddress(destaddress); - bool isToSproutZaddr = (std::get_if(&decodeAddr) != nullptr); - - if (isToSproutZaddr) { - throw JSONRPCError(RPC_VERIFY_REJECTED, "Sprout shielding is not supported after Canopy activation"); + if (decodeAddr.has_value()) { + libzcash::PaymentAddress addr(decodeAddr.value()); + if (std::holds_alternative(addr)) { + throw JSONRPCError(RPC_VERIFY_REJECTED, "Sprout shielding is not supported after Canopy activation"); + } } } @@ -4427,12 +4447,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) isFromNonSprout = true; } else { auto zaddr = keyIO.DecodePaymentAddress(address); - if (IsValidPaymentAddress(zaddr)) { + if (zaddr.has_value()) { // We want to merge notes corresponding to any receiver within a // Unified Address. - for (const auto ra : std::visit(GetRawAddresses(), zaddr)) { + for (const libzcash::RawAddress& ra : std::visit(GetRawAddresses(), zaddr.value())) { zaddrs.insert(ra); - if (std::get_if(&ra) != nullptr) { + if (std::holds_alternative(ra)) { isFromNonSprout = true; } } @@ -4465,17 +4485,23 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) bool isToSaplingZaddr = false; CTxDestination taddr = keyIO.DecodeDestination(destaddress); if (!IsValidDestination(taddr)) { - auto decodeAddr = keyIO.DecodePaymentAddress(destaddress); - if (IsValidPaymentAddress(decodeAddr)) { - if (std::get_if(&decodeAddr) != nullptr) { - isToSaplingZaddr = true; - // If Sapling is not active, do not allow sending to a sapling addresses. - if (!saplingActive) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); + auto zaddr = keyIO.DecodePaymentAddress(destaddress); + if (zaddr.has_value()) { + std::visit(match { + [&](libzcash::SaplingPaymentAddress addr) { + isToSaplingZaddr = true; + // If Sapling is not active, do not allow sending to a sapling addresses. + if (!saplingActive) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); + } + }, + [&](libzcash::SproutPaymentAddress addr) { + isToSproutZaddr = true; + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED } - } else { - isToSproutZaddr = true; - } + }, zaddr.value()); } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 2d411842f93..5947c444a8d 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -602,15 +602,16 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) BOOST_CHECK(addrs.size()==1); // check that we have the spending key for the address - auto address = keyIO.DecodePaymentAddress(testAddr); - BOOST_CHECK(IsValidPaymentAddress(address)); - BOOST_ASSERT(std::get_if(&address) != nullptr); - auto addr = std::get(address); - BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(addr)); + auto decoded = keyIO.DecodePaymentAddress(testAddr); + BOOST_CHECK(decoded.has_value()); + libzcash::PaymentAddress address(decoded.value()); + BOOST_ASSERT(std::holds_alternative(address)); + auto sprout_addr = std::get(address); + BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(sprout_addr)); // Verify the spending key is the same as the test data libzcash::SproutSpendingKey k; - BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k)); + BOOST_CHECK(pwalletMain->GetSproutSpendingKey(sprout_addr, k)); BOOST_CHECK_EQUAL(testKey, keyIO.EncodeSpendingKey(k)); } @@ -776,10 +777,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) // Check if address is of given type and spendable from our wallet. template -void CheckHaveAddr(const libzcash::PaymentAddress& addr) { - - BOOST_CHECK(IsValidPaymentAddress(addr)); - auto addr_of_type = std::get_if(&addr); +void CheckHaveAddr(const std::optional& addr) { + BOOST_CHECK(addr.has_value()); + auto addr_of_type = std::get_if(&(addr.value())); BOOST_ASSERT(addr_of_type != nullptr); HaveSpendingKeyForPaymentAddress test(pwalletMain); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index adc171603bf..c6fd173fd0b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4927,8 +4927,8 @@ bool CWallet::ParameterInteraction(const CChainParams& params) // Check Sapling migration address if set and is a valid Sapling address if (mapArgs.count("-migrationdestaddress")) { std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; - libzcash::PaymentAddress address = keyIO.DecodePaymentAddress(migrationDestAddress); - if (std::get_if(&address) == nullptr) { + std::optional address = keyIO.DecodePaymentAddress(migrationDestAddress); + if (!address.has_value() || std::get_if(&address.value()) == nullptr) { return UIError(_("-migrationdestaddress must be a valid Sapling address.")); } } @@ -5032,17 +5032,15 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) void CWallet::GetFilteredNotes( std::vector& sproutEntries, std::vector& saplingEntries, - std::string address, + std::optional address, int minDepth, bool ignoreSpent, bool requireSpendingKey) { std::set filterAddresses; - KeyIO keyIO(Params()); - if (address.length() > 0) { - auto addr = keyIO.DecodePaymentAddress(address); - for (const auto ra : std::visit(GetRawAddresses(), addr)) { + if (address.has_value()) { + for (const auto ra : std::visit(GetRawAddresses(), address.value())) { filterAddresses.insert(ra); } } @@ -5210,11 +5208,6 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::UnifiedAddress &u return false; } -bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const -{ - return false; -} - /// PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const @@ -5253,12 +5246,6 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Unif return AddressNotFound; } -PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const -{ - return AddressNotFound; -} - - /// std::optional GetViewingKeyForPaymentAddress::operator()( @@ -5297,13 +5284,6 @@ std::optional GetViewingKeyForPaymentAddress::operator()( return libzcash::ViewingKey(); } -std::optional GetViewingKeyForPaymentAddress::operator()( - const libzcash::InvalidEncoding& no) const -{ - // Defaults to InvalidEncoding - return libzcash::ViewingKey(); -} - bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { return m_wallet->HaveSproutSpendingKey(zaddr); @@ -5325,11 +5305,6 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress return false; } -bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const -{ - return false; -} - std::optional GetSpendingKeyForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -5359,13 +5334,6 @@ std::optional GetSpendingKeyForPaymentAddress::operator() return libzcash::SpendingKey(); } -std::optional GetSpendingKeyForPaymentAddress::operator()( - const libzcash::InvalidEncoding& no) const -{ - // Defaults to InvalidEncoding - return libzcash::SpendingKey(); -} - KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey &vkey) const { auto addr = vkey.address(); @@ -5392,10 +5360,6 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu } } -KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); -} - KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { auto addr = sk.address(); KeyIO keyIO(Params()); @@ -5447,7 +5411,3 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS } } } - -KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); -} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7045a1ea217..57fda636663 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1304,10 +1304,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); - /* Find notes filtered by payment address, min depth, ability to spend */ + /* Find notes filtered by (optional) payment address, min depth, ability to spend */ void GetFilteredNotes(std::vector& sproutEntries, std::vector& saplingEntries, - std::string address, + std::optional address, int minDepth=1, bool ignoreSpent=true, bool requireSpendingKey=true); @@ -1372,7 +1372,6 @@ class PaymentAddressBelongsToWallet bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; - bool operator()(const libzcash::InvalidEncoding& no) const; }; class GetViewingKeyForPaymentAddress @@ -1385,7 +1384,6 @@ class GetViewingKeyForPaymentAddress std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; - std::optional operator()(const libzcash::InvalidEncoding& no) const; }; class HaveSpendingKeyForPaymentAddress @@ -1398,7 +1396,6 @@ class HaveSpendingKeyForPaymentAddress bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; - bool operator()(const libzcash::InvalidEncoding& no) const; }; class GetSpendingKeyForPaymentAddress @@ -1411,7 +1408,6 @@ class GetSpendingKeyForPaymentAddress std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; - std::optional operator()(const libzcash::InvalidEncoding& no) const; }; enum PaymentAddressSource { @@ -1433,7 +1429,6 @@ class GetSourceForPaymentAddress PaymentAddressSource operator()(const libzcash::SproutPaymentAddress &zaddr) const; PaymentAddressSource operator()(const libzcash::SaplingPaymentAddress &zaddr) const; PaymentAddressSource operator()(const libzcash::UnifiedAddress &uaddr) const; - PaymentAddressSource operator()(const libzcash::InvalidEncoding& no) const; }; enum KeyAddResult { @@ -1452,7 +1447,6 @@ class AddViewingKeyToWallet KeyAddResult operator()(const libzcash::SproutViewingKey &sk) const; KeyAddResult operator()(const libzcash::SaplingExtendedFullViewingKey &sk) const; - KeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; class AddSpendingKeyToWallet @@ -1479,7 +1473,6 @@ class AddSpendingKeyToWallet KeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const; KeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const; - KeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index d827a395a25..dbd8e7c20ff 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -41,9 +41,6 @@ std::pair AddressInfoFromSpendingKey::operator()(co std::pair AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const { return std::make_pair("sapling", sk.DefaultAddress()); } -std::pair AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const { - throw std::invalid_argument("Cannot derive default address from invalid spending key"); -} std::pair AddressInfoFromViewingKey::operator()(const SproutViewingKey &sk) const { return std::make_pair("sprout", sk.address()); @@ -51,23 +48,8 @@ std::pair AddressInfoFromViewingKey::operator()(con std::pair AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &sk) const { return std::make_pair("sapling", sk.DefaultAddress()); } -std::pair AddressInfoFromViewingKey::operator()(const InvalidEncoding&) const { - throw std::invalid_argument("Cannot derive default address from invalid viewing key"); -} -} - -bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr) { - return !std::holds_alternative(zaddr); -} - -bool IsValidViewingKey(const libzcash::ViewingKey& vk) { - return !std::holds_alternative(vk); -} - -bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) { - return !std::holds_alternative(zkey); -} +} // namespace libzcash uint32_t TypecodeForReceiver::operator()( const libzcash::SaplingPaymentAddress &zaddr) const @@ -117,12 +99,6 @@ std::optional ReceiverToRawAddress::operator()( return std::nullopt; } -std::optional RecipientForPaymentAddress::operator()( - const libzcash::InvalidEncoding& no) const -{ - return std::nullopt; -} - std::optional RecipientForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -146,12 +122,6 @@ std::optional RecipientForPaymentAddress::operator()( return std::nullopt; } -std::set GetRawAddresses::operator()( - const libzcash::InvalidEncoding& no) const -{ - return {}; -} - std::set GetRawAddresses::operator()( const libzcash::SproutPaymentAddress &zaddr) const { diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 38d416ee2e9..8eec84ee536 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -24,12 +24,6 @@ class P2SHAddress: public uint160 { /** Protocol addresses that can receive funds in a transaction. */ typedef std::variant RawAddress; -class InvalidEncoding { -public: - friend bool operator==(const InvalidEncoding &a, const InvalidEncoding &b) { return true; } - friend bool operator<(const InvalidEncoding &a, const InvalidEncoding &b) { return true; } -}; - class UnknownReceiver { public: uint32_t typecode; @@ -137,38 +131,32 @@ class UnifiedAddress { /** Addresses that can appear in a string encoding. */ typedef std::variant< - InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress, UnifiedAddress> PaymentAddress; -typedef std::variant ViewingKey; -typedef std::variant SpendingKey; +/** Viewing keys that can have a string encoding. */ +typedef std::variant< + SproutViewingKey, + SaplingExtendedFullViewingKey> ViewingKey; +/** Spending keys that can have a string encoding. */ +typedef std::variant< + SproutSpendingKey, + SaplingExtendedSpendingKey> SpendingKey; class AddressInfoFromSpendingKey { public: std::pair operator()(const SproutSpendingKey&) const; std::pair operator()(const struct SaplingExtendedSpendingKey&) const; - std::pair operator()(const InvalidEncoding&) const; }; class AddressInfoFromViewingKey { public: std::pair operator()(const SproutViewingKey&) const; std::pair operator()(const struct SaplingExtendedFullViewingKey&) const; - std::pair operator()(const InvalidEncoding&) const; }; } -/** Check whether a PaymentAddress is not an InvalidEncoding. */ -bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr); - -/** Check whether a ViewingKey is not an InvalidEncoding. */ -bool IsValidViewingKey(const libzcash::ViewingKey& vk); - -/** Check whether a SpendingKey is not an InvalidEncoding. */ -bool IsValidSpendingKey(const libzcash::SpendingKey& zkey); - /** * Gets the typecode for the given UA receiver. */ @@ -202,7 +190,6 @@ class RecipientForPaymentAddress { public: RecipientForPaymentAddress() {} - std::optional operator()(const libzcash::InvalidEncoding& no) const; std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; @@ -215,7 +202,6 @@ class GetRawAddresses { public: GetRawAddresses() {} - std::set operator()(const libzcash::InvalidEncoding& no) const; std::set operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::set operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::set operator()(const libzcash::UnifiedAddress &uaddr) const; From 62b86d6afc468c42ba54f58ccefcc01c6d442124 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 19 Nov 2021 16:21:36 -0700 Subject: [PATCH 232/514] Use CKeyID and CScriptID instead of new P2PKH/P2SHAddress classes. --- src/gtest/test_keys.cpp | 8 ++++---- src/key_io.cpp | 12 ++++++------ src/pubkey.h | 1 + src/script/script.h | 10 ++++++++++ src/script/standard.h | 9 --------- src/zcash/Address.cpp | 12 ++++++------ src/zcash/Address.hpp | 25 ++++++++----------------- 7 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index a632f3deca5..2ad3ecfc214 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -78,11 +78,11 @@ namespace libzcash { return tfm::format("Sapling(%s)", HexStr(ss.begin(), ss.end())); } - std::string operator()(const P2SHAddress &p2sh) const { + std::string operator()(const CScriptID &p2sh) const { return tfm::format("P2SH(%s)", p2sh.GetHex()); } - std::string operator()(const P2PKHAddress &p2pkh) const { + std::string operator()(const CKeyID &p2pkh) const { return tfm::format("P2PKH(%s)", p2pkh.GetHex()); } @@ -140,11 +140,11 @@ TEST(Keys, EncodeAndDecodeUnified) ua.AddReceiver(r); } if (!test[1].isNull()) { - libzcash::P2SHAddress r(ParseHex(test[1].get_str())); + CScriptID r(ParseHex(test[1].get_str())); ua.AddReceiver(r); } if (!test[0].isNull()) { - libzcash::P2PKHAddress r(ParseHex(test[0].get_str())); + CKeyID r(ParseHex(test[0].get_str())); ua.AddReceiver(r); } diff --git a/src/key_io.cpp b/src/key_io.cpp index 671cc8d3481..c8f1802961b 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -56,8 +56,8 @@ class DataLenForReceiver { DataLenForReceiver() {} size_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const { return 43; } - size_t operator()(const libzcash::P2SHAddress &p2sh) const { return 20; } - size_t operator()(const libzcash::P2PKHAddress &p2pkh) const { return 20; } + size_t operator()(const CScriptID &p2sh) const { return 20; } + size_t operator()(const CKeyID &p2pkh) const { return 20; } size_t operator()(const libzcash::UnknownReceiver &unknown) const { return unknown.data.size(); } }; @@ -82,11 +82,11 @@ class CopyDataForReceiver { memcpy(data, ss.data(), ss.size()); } - void operator()(const libzcash::P2SHAddress &p2sh) const { + void operator()(const CScriptID &p2sh) const { memcpy(data, p2sh.begin(), p2sh.size()); } - void operator()(const libzcash::P2PKHAddress &p2pkh) const { + void operator()(const CKeyID &p2pkh) const { memcpy(data, p2pkh.begin(), p2pkh.size()); } @@ -415,7 +415,7 @@ static bool AddP2SHReceiver(void* ua, const unsigned char* raw) reinterpret_cast(raw + 20), SER_NETWORK, PROTOCOL_VERSION); - libzcash::P2SHAddress receiver; + CScriptID receiver; ss >> receiver; return reinterpret_cast(ua)->AddReceiver(receiver); } @@ -430,7 +430,7 @@ static bool AddP2PKHReceiver(void* ua, const unsigned char* raw) reinterpret_cast(raw + 20), SER_NETWORK, PROTOCOL_VERSION); - libzcash::P2PKHAddress receiver; + CKeyID receiver; ss >> receiver; return reinterpret_cast(ua)->AddReceiver(receiver); } diff --git a/src/pubkey.h b/src/pubkey.h index 4cdfbef3831..94ff547c4a6 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -22,6 +22,7 @@ class CKeyID : public uint160 public: CKeyID() : uint160() {} CKeyID(const uint160& in) : uint160(in) {} + explicit CKeyID(const std::vector& vch) : uint160(vch) {} }; typedef uint256 ChainCode; diff --git a/src/script/script.h b/src/script/script.h index e671191972d..b20227caf90 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -625,6 +625,16 @@ class CScript : public CScriptBase } }; +/** A reference to a CScript: the Hash160 of its serialization */ +class CScriptID : public uint160 +{ +public: + CScriptID() : uint160() {} + explicit CScriptID(const CScript& in); + CScriptID(const uint160& in) : uint160(in) {} + explicit CScriptID(const std::vector& vch) : uint160(vch) {} +}; + class CReserveScript { public: diff --git a/src/script/standard.h b/src/script/standard.h index 09a23368f47..920ac165d5e 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -18,15 +18,6 @@ static const bool DEFAULT_ACCEPT_DATACARRIER = true; class CKeyID; class CScript; -/** A reference to a CScript: the Hash160 of its serialization (see script.h) */ -class CScriptID : public uint160 -{ -public: - CScriptID() : uint160() {} - explicit CScriptID(const CScript& in); - CScriptID(const uint160& in) : uint160(in) {} -}; - /** * Default setting for nMaxDatacarrierBytes. 80 bytes of data, +1 for OP_RETURN, * +2 for the pushdata opcodes. diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index dbd8e7c20ff..fe048c1a9ea 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -24,8 +24,8 @@ bool UnifiedAddress::AddReceiver(Receiver receiver) { auto t = std::visit(TypecodeForReceiver(), r); if ( (t == typecode) || - (std::holds_alternative(r) && std::holds_alternative(receiver)) || - (std::holds_alternative(r) && std::holds_alternative(receiver)) + (std::holds_alternative(r) && std::holds_alternative(receiver)) || + (std::holds_alternative(r) && std::holds_alternative(receiver)) ) { return false; } @@ -58,13 +58,13 @@ uint32_t TypecodeForReceiver::operator()( } uint32_t TypecodeForReceiver::operator()( - const libzcash::P2SHAddress &p2sh) const + const CScriptID &p2sh) const { return ZCASH_UA_TYPECODE_P2SH; } uint32_t TypecodeForReceiver::operator()( - const libzcash::P2PKHAddress &p2sh) const + const CKeyID &p2sh) const { return ZCASH_UA_TYPECODE_P2PKH; } @@ -82,13 +82,13 @@ std::optional ReceiverToRawAddress::operator()( } std::optional ReceiverToRawAddress::operator()( - const libzcash::P2SHAddress &p2sh) const + const CScriptID &p2sh) const { return std::nullopt; } std::optional ReceiverToRawAddress::operator()( - const libzcash::P2PKHAddress &p2sh) const + const CKeyID &p2sh) const { return std::nullopt; } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 8eec84ee536..fd95418d4e9 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -2,6 +2,8 @@ #define ZC_ADDRESS_H_ #include "uint256.h" +#include "pubkey.h" +#include "script/script.h" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" #include "zcash/address/zip32.h" @@ -9,17 +11,6 @@ #include namespace libzcash { -// We use new classes here instead of CKeyID and CScriptID to prevent a cyclic dependency. -class P2PKHAddress: public uint160 { -public: - P2PKHAddress() {} - explicit P2PKHAddress(const std::vector& vch) : uint160(vch) {} -}; -class P2SHAddress: public uint160 { -public: - P2SHAddress() {} - explicit P2SHAddress(const std::vector& vch) : uint160(vch) {} -}; /** Protocol addresses that can receive funds in a transaction. */ typedef std::variant RawAddress; @@ -53,8 +44,8 @@ class UnknownReceiver { */ typedef std::variant< SaplingPaymentAddress, - P2SHAddress, - P2PKHAddress, + CScriptID, + CKeyID, UnknownReceiver> Receiver; struct ReceiverIterator { @@ -165,8 +156,8 @@ class TypecodeForReceiver { TypecodeForReceiver() {} uint32_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - uint32_t operator()(const libzcash::P2SHAddress &p2sh) const; - uint32_t operator()(const libzcash::P2PKHAddress &p2pkh) const; + uint32_t operator()(const CScriptID &p2sh) const; + uint32_t operator()(const CKeyID &p2pkh) const; uint32_t operator()(const libzcash::UnknownReceiver &p2pkh) const; }; @@ -178,8 +169,8 @@ class ReceiverToRawAddress { ReceiverToRawAddress() {} std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - std::optional operator()(const libzcash::P2SHAddress &p2sh) const; - std::optional operator()(const libzcash::P2PKHAddress &p2pkh) const; + std::optional operator()(const CScriptID &p2sh) const; + std::optional operator()(const CKeyID &p2pkh) const; std::optional operator()(const libzcash::UnknownReceiver &p2pkh) const; }; From c35e2b4438c449186c1dc4ee08e0600226445706 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 17 Dec 2021 12:59:30 -0700 Subject: [PATCH 233/514] Remove spurious uses of HaveSpendingKeyForPaymentAddress There's no need to dispatch through this visitor in cases where the payment address type is already statically known. --- src/keystore.cpp | 19 ++++++++-- src/keystore.h | 3 ++ src/rpc/misc.cpp | 4 +- src/wallet/rpcwallet.cpp | 80 ++++++++++++++++++++++------------------ src/wallet/wallet.cpp | 5 ++- src/zcash/Address.hpp | 2 +- 6 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index e39a0d6dbf5..645d6f4561b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -151,7 +151,7 @@ bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) return true; } -//! Sapling +//! Sapling bool CBasicKeyStore::AddSaplingSpendingKey( const libzcash::SaplingExtendedSpendingKey &sk) { @@ -187,7 +187,7 @@ bool CBasicKeyStore::AddSaplingFullViewingKey( return CBasicKeyStore::AddSaplingIncomingViewingKey(ivk, extfvk.DefaultAddress()); } -// This function updates the wallet's internal address->ivk map. +// This function updates the wallet's internal address->ivk map. // If we add an address that is already in the map, the map will // remain unchanged as each address only has one ivk. bool CBasicKeyStore::AddSaplingIncomingViewingKey( @@ -265,8 +265,9 @@ bool CBasicKeyStore::GetSaplingIncomingViewingKey(const libzcash::SaplingPayment return false; } -bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymentAddress &addr, - libzcash::SaplingExtendedSpendingKey &extskOut) const { +bool CBasicKeyStore::GetSaplingExtendedSpendingKey( + const libzcash::SaplingPaymentAddress &addr, + libzcash::SaplingExtendedSpendingKey &extskOut) const { libzcash::SaplingIncomingViewingKey ivk; libzcash::SaplingExtendedFullViewingKey extfvk; @@ -275,3 +276,13 @@ bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymen GetSaplingFullViewingKey(ivk, extfvk) && GetSaplingSpendingKey(extfvk, extskOut); } + +bool CBasicKeyStore::HaveSaplingSpendingKeyForAddress( + const libzcash::SaplingPaymentAddress &addr) const { + libzcash::SaplingIncomingViewingKey ivk; + libzcash::SaplingExtendedFullViewingKey extfvk; + + return GetSaplingIncomingViewingKey(addr, ivk) && + GetSaplingFullViewingKey(ivk, extfvk) && + HaveSaplingSpendingKey(extfvk); +} diff --git a/src/keystore.h b/src/keystore.h index 8246461aaed..7b021acf26e 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -66,6 +66,8 @@ class CKeyStore //! Check whether a Sapling spending key corresponding to a given Sapling viewing key is present in the store. virtual bool HaveSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk) const =0; + virtual bool HaveSaplingSpendingKeyForAddress( + const libzcash::SaplingPaymentAddress &addr) const =0; virtual bool GetSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, libzcash::SaplingExtendedSpendingKey& skOut) const =0; @@ -246,6 +248,7 @@ class CBasicKeyStore : public CKeyStore } return result; } + bool HaveSaplingSpendingKeyForAddress(const libzcash::SaplingPaymentAddress &addr) const; bool GetSaplingSpendingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk, libzcash::SaplingExtendedSpendingKey &skOut) const diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index c09b694bfa5..0f21145b77c 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -224,7 +224,7 @@ class DescribePaymentAddressVisitor obj.pushKV("transmissionkey", zaddr.pk_enc.GetHex()); #ifdef ENABLE_WALLET if (pwalletMain) { - obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr)); + obj.pushKV("ismine", pwalletMain->HaveSproutSpendingKey(zaddr)); } #endif return obj; @@ -237,7 +237,7 @@ class DescribePaymentAddressVisitor obj.pushKV("diversifiedtransmissionkey", zaddr.pk_d.GetHex()); #ifdef ENABLE_WALLET if (pwalletMain) { - obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr)); + obj.pushKV("ismine", pwalletMain->HaveSaplingSpendingKeyForAddress(zaddr)); } #endif return obj; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 83c63e554c0..984d02969a3 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -432,7 +432,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) if (!sproutAddresses.empty()) { UniValue random_sprout_addrs(UniValue::VARR); for (const SproutPaymentAddress& addr : sproutAddresses) { - if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + if (pwalletMain->HaveSproutSpendingKey(addr)) { random_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr)); } } @@ -2270,13 +2270,25 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) if (!zaddr.has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address); } - auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); - if (!fIncludeWatchonly && !hasSpendingKey) { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address); - } + // We want to return unspent notes corresponding to any receiver within a // Unified Address. for (const auto ra : std::visit(GetRawAddresses(), zaddr.value())) { + bool hasSpendingKey = std::visit(match { + [&](const SaplingPaymentAddress& addr) { + return pwalletMain->HaveSaplingSpendingKeyForAddress(addr); + }, + [&](const SproutPaymentAddress& addr) { + return pwalletMain->HaveSproutSpendingKey(addr); + } + }, ra); + + // If we don't include watchonly addresses, we must reject any address + // for which we do not have the spending key. + if (!fIncludeWatchonly && !hasSpendingKey) { + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address); + } + zaddrs.insert(ra); } @@ -2313,7 +2325,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) obj.pushKV("jsindex", (int)entry.jsop.js ); obj.pushKV("jsoutindex", (int)entry.jsop.n); obj.pushKV("confirmations", entry.confirmations); - bool hasSproutSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address); + bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(entry.address); obj.pushKV("spendable", hasSproutSpendingKey); obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); @@ -2330,7 +2342,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) obj.pushKV("txid", entry.op.hash.ToString()); obj.pushKV("outindex", (int)entry.op.n); obj.pushKV("confirmations", entry.confirmations); - bool hasSaplingSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address); + bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address); obj.pushKV("spendable", hasSaplingSpendingKey); // TODO: If we found this entry via a UA, show that instead. obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); @@ -2980,7 +2992,7 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) std::set addresses; pwalletMain->GetSproutPaymentAddresses(addresses); for (auto addr : addresses) { - if (fIncludeWatchonly || HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + if (fIncludeWatchonly || pwalletMain->HaveSproutSpendingKey(addr)) { ret.push_back(keyIO.EncodePaymentAddress(addr)); } } @@ -2989,7 +3001,7 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) std::set addresses; pwalletMain->GetSaplingPaymentAddresses(addresses); for (auto addr : addresses) { - if (fIncludeWatchonly || HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { + if (fIncludeWatchonly || pwalletMain->HaveSaplingSpendingKeyForAddress(addr)) { ret.push_back(keyIO.EncodePaymentAddress(addr)); } } @@ -3141,14 +3153,10 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) std::vector saplingEntries; pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, decoded, nMinDepth, false, false); - std::set> nullifierSet; - auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), decoded.value()); - if (hasSpendingKey) { - nullifierSet = pwalletMain->GetNullifiersForAddresses(std::visit(GetRawAddresses(), decoded.value())); - } - std::visit(match { - [&](libzcash::SproutPaymentAddress addr) { + [&](const libzcash::SproutPaymentAddress& addr) { + bool hasSpendingKey = pwalletMain->HaveSproutSpendingKey(addr); + auto nullifierSet = pwalletMain->GetNullifiersForAddresses({addr}); for (SproutNoteEntry & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.jsop.hash.ToString()); @@ -3171,7 +3179,9 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) result.push_back(obj); } }, - [&](libzcash::SaplingPaymentAddress addr) { + [&](const libzcash::SaplingPaymentAddress& addr) { + bool hasSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(addr); + auto nullifierSet = pwalletMain->GetNullifiersForAddresses({addr}); for (SaplingNoteEntry & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.op.hash.ToString()); @@ -3187,12 +3197,12 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) obj.pushKV("blocktime", BlockData.time); if (hasSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); + obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); } result.push_back(obj); } }, - [&](libzcash::UnifiedAddress) { + [&](const libzcash::UnifiedAddress& addr) { // TODO UNIFIED } }, decoded.value()); @@ -3721,6 +3731,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) auto fromaddress = params[0].get_str(); bool fromTaddr = false; bool fromSapling = false; + bool fromSprout = false; KeyIO keyIO(Params()); if (fromaddress == "ANY_TADDR") { fromTaddr = true; @@ -3728,27 +3739,24 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) CTxDestination taddr = keyIO.DecodeDestination(fromaddress); fromTaddr = IsValidDestination(taddr); if (!fromTaddr) { - auto decoded = keyIO.DecodePaymentAddress(fromaddress); - if (!decoded.has_value()) { + auto addr = keyIO.DecodePaymentAddress(fromaddress); + if (!addr.has_value()) { // invalid throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } - // Check that we have the spending key - libzcash::PaymentAddress addr(decoded.value()); - if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), addr)) { + // This is a sanity check; the actual checks will come later when the spend is attempted. + if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), addr.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); } // Remember whether this is a Sprout or Sapling address - fromSapling = std::holds_alternative(addr); + fromSapling = std::holds_alternative(addr.value()); + fromSprout = std::holds_alternative(addr.value()); } } - // This logic will need to be updated if we add a new shielded pool - bool fromSprout = !(fromTaddr || fromSapling); UniValue outputs = params[1].get_array(); - if (outputs.size()==0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amounts array is empty."); @@ -3781,13 +3789,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) bool isZaddr = false; CTxDestination taddr = keyIO.DecodeDestination(address); if (!IsValidDestination(taddr)) { - auto decoded = keyIO.DecodePaymentAddress(address); - if (decoded.has_value()) { - libzcash::PaymentAddress addr(decoded.value()); + auto addr = keyIO.DecodePaymentAddress(address); + if (addr.has_value()) { isZaddr = true; - bool toSapling = std::holds_alternative(addr); - bool toSprout = std::holds_alternative(addr); + bool toSapling = std::holds_alternative(addr.value()); + bool toSprout = std::holds_alternative(addr.value()); noSproutAddrs = !toSprout && noSproutAddrs && toSapling; containsSproutOutput |= toSprout; @@ -3885,19 +3892,20 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) for (int i = 0; i < zaddrRecipients.size(); i++) { auto address = zaddrRecipients[i].address; auto decoded = keyIO.DecodePaymentAddress(address); + if (decoded.has_value()) { std::visit(match { - [&](libzcash::SaplingPaymentAddress addr) { + [&](const libzcash::SaplingPaymentAddress& addr) { mtx.vShieldedOutput.push_back(OutputDescription()); }, - [&](libzcash::SproutPaymentAddress addr) { + [&](const libzcash::SproutPaymentAddress& addr) { JSDescription jsdesc; if (mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)) { jsdesc.proof = GrothProof(); } mtx.vJoinSplit.push_back(jsdesc); }, - [&](libzcash::UnifiedAddress) { + [&](const libzcash::UnifiedAddress& ua) { // TODO UNIFIED } }, decoded.value()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c6fd173fd0b..4504d5d3000 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -25,6 +25,7 @@ #include "script/sign.h" #include "timedata.h" #include "utilmoneystr.h" +#include "util/match.h" #include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" #include "crypter.h" @@ -5166,7 +5167,7 @@ void CWallet::GetFilteredNotes( } // skip notes which cannot be spent - if (requireSpendingKey && !HaveSpendingKeyForPaymentAddress(this)(pa)) { + if (requireSpendingKey && !HaveSaplingSpendingKeyForAddress(pa)) { continue; } @@ -5227,7 +5228,7 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl if (m_wallet->mapSaplingZKeyMetadata.count(ivk) > 0 && m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath != "") { return LegacyHDSeed; - } else if (HaveSpendingKeyForPaymentAddress(m_wallet)(zaddr)) { + } else if (m_wallet->HaveSaplingSpendingKeyForAddress(zaddr)) { return Imported; } else { return ImportedWatchOnly; diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index fd95418d4e9..83792eddf7b 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -125,7 +125,7 @@ typedef std::variant< SproutPaymentAddress, SaplingPaymentAddress, UnifiedAddress> PaymentAddress; -/** Viewing keys that can have a string encoding. */ +/** Viewing keys that can be decoded from a string representation. */ typedef std::variant< SproutViewingKey, SaplingExtendedFullViewingKey> ViewingKey; From 890e1d841d9046a1e0da1fa158fc7507e0a8d235 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 23 Dec 2021 15:08:11 -0700 Subject: [PATCH 234/514] Add raw transparent address types to PaymentAddress The addition of `UnifiedAddress` to the `PaymentAddress` type introduced the need for methods that interact with payment addresses to support transparent receivers as shielded ones, which is somewhat inconsistent with previous uses of the `PaymentAddress` type. This commit adds both `CKeyID` and `CScriptID` as new variants of the `PaymentAddress` type. Following commits will shift encoding and decoding to use the `PaymentAddress` type exclusively wherever possible, rather than the current mix of `CDestination` and `PaymentAddress` that complicates the wallet codebase. --- src/init.cpp | 16 +-- src/key_io.cpp | 105 ++++++++++++++---- src/miner.h | 10 +- src/rpc/misc.cpp | 22 ++++ .../asyncrpcoperation_shieldcoinbase.cpp | 8 ++ src/wallet/asyncrpcoperation_shieldcoinbase.h | 2 + src/wallet/rpcwallet.cpp | 68 +++++++----- src/wallet/wallet.cpp | 83 +++++++++++--- src/wallet/wallet.h | 11 ++ src/zcash/Address.cpp | 50 ++++++--- src/zcash/Address.hpp | 10 +- 11 files changed, 290 insertions(+), 95 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 040ff0707d2..851c1f9bba0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1678,18 +1678,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifdef ENABLE_WALLET bool minerAddressInLocalWallet = false; if (pwalletMain) { - CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]); - if (IsValidDestination(addr)) { - CKeyID keyID = std::get(addr); - minerAddressInLocalWallet = pwalletMain->HaveKey(keyID); - } else { - auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); - if (!zaddr.has_value()) { - return InitError(_("-mineraddress is not a valid zcash address.")); - } - minerAddressInLocalWallet = std::visit( - HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); + auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); + if (!zaddr.has_value()) { + return InitError(_("-mineraddress is not a valid zcash address.")); } + minerAddressInLocalWallet = std::visit( + HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); } if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) { return InitError(_("-mineraddress is not in the local wallet. Either use a local address, or set -minetolocalwallet=0")); diff --git a/src/key_io.cpp b/src/key_io.cpp index c8f1802961b..6c1a40344e8 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -16,6 +16,7 @@ #include #include #include +#include "util/match.h" namespace { @@ -113,6 +114,18 @@ class PaymentAddressEncoder public: PaymentAddressEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {} + std::string operator()(const CKeyID& id) const + { + std::vector data = keyConstants.Base58Prefix(KeyConstants::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + std::string operator()(const CScriptID& id) const + { + std::vector data = keyConstants.Base58Prefix(KeyConstants::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -349,17 +362,16 @@ std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) return std::visit(PaymentAddressEncoder(keyConstants), zaddr); } -template -std::optional DecodeAny( - const KeyConstants& keyConstants, - const std::string& str, - std::pair sprout, - std::pair sapling) +template +std::optional DecodeSprout( + const KeyConstants& keyConstants, + const std::string& str, + const std::pair& keyMeta) { std::vector data; if (DecodeBase58Check(str, data)) { - const std::vector& prefix = keyConstants.Base58Prefix(sprout.first); - if ((data.size() == sprout.second + prefix.size()) && + const std::vector& prefix = keyConstants.Base58Prefix(keyMeta.first); + if ((data.size() == keyMeta.second + prefix.size()) && std::equal(prefix.begin(), prefix.end(), data.begin())) { CSerializeData serialized(data.begin() + prefix.size(), data.end()); CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); @@ -371,15 +383,26 @@ std::optional DecodeAny( } } - data.clear(); + memory_cleanse(data.data(), data.size()); + return std::nullopt; +} + +template +std::optional DecodeSapling( + const KeyConstants& keyConstants, + const std::string& str, + const std::pair& keyMeta) +{ + std::vector data; + auto bech = bech32::Decode(str); - if (bech.first == keyConstants.Bech32HRP(sapling.first) && - bech.second.size() == sapling.second) { + if (bech.first == keyConstants.Bech32HRP(keyMeta.first) && + bech.second.size() == keyMeta.second) { // Bech32 decoding data.reserve((bech.second.size() * 5) / 8); if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) { CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); - T3 ret; + T2 ret; ss >> ret; memory_cleanse(data.data(), data.size()); return ret; @@ -390,6 +413,26 @@ std::optional DecodeAny( return std::nullopt; } +template +std::optional DecodeAny( + const KeyConstants& keyConstants, + const std::string& str, + const std::pair& sproutKeyMeta, + const std::pair& saplingKeyMeta) +{ + auto sprout = DecodeSprout(keyConstants, str, sproutKeyMeta); + if (sprout.has_value()) { + return sprout.value(); + } + + auto sapling = DecodeSapling(keyConstants, str, saplingKeyMeta); + if (sapling.has_value()) { + return sapling.value(); + } + + return std::nullopt; +} + /** * `raw` MUST be 43 bytes. */ @@ -457,15 +500,39 @@ std::optional KeyIO::DecodePaymentAddress(const std::s return ua; } - // Fall back on trying Sprout or Sapling. - return DecodeAny( + // Try parsing as a Sapling address + auto sapling = DecodeSapling( keyConstants, str, - std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize), - std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize) - ); + std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize)); + if (sapling.has_value()) { + return sapling.value(); + } + + // Try parsing as a Sprout address + auto sprout = DecodeSprout( + keyConstants, + str, + std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize)); + if (sprout.has_value()) { + return sprout.value(); + } + + // Finally, try parsing as transparent + return std::visit(match { + [](const CKeyID& keyIdIn) { + std::optional keyId = keyIdIn; + return keyId; + }, + [](const CScriptID& scriptIdIn) { + std::optional scriptId = scriptIdIn; + return scriptId; + }, + [](const CNoDestination& d) { + std::optional result = std::nullopt; + return result; + } + }, DecodeDestination(str)); } bool KeyIO::IsValidPaymentAddressString(const std::string& str) { diff --git a/src/miner.h b/src/miner.h index 61dcc938b22..6f9073dc578 100644 --- a/src/miner.h +++ b/src/miner.h @@ -32,6 +32,12 @@ class ExtractMinerAddress public: ExtractMinerAddress() {} + std::optional operator()(const CKeyID &addr) const { + return std::nullopt; + } + std::optional operator()(const CScriptID &addr) const { + return std::nullopt; + } std::optional operator()(const libzcash::SproutPaymentAddress &addr) const { return std::nullopt; } @@ -40,7 +46,7 @@ class ExtractMinerAddress } std::optional operator()(const libzcash::UnifiedAddress &addr) const { auto recipient = RecipientForPaymentAddress()(addr); - if (recipient) { + if (recipient.has_value()) { // This looks like a recursive call, but we are actually calling // ExtractMinerAddress with a different type: // - libzcash::PaymentAddress has a libzcash::UnifiedAddress @@ -51,7 +57,7 @@ class ExtractMinerAddress // This works because std::visit does not require the visitor to // solely match the std::variant, only that it can handle all of // the variant's alternatives. - return std::visit(ExtractMinerAddress(), *recipient); + return std::visit(ExtractMinerAddress(), recipient.value()); } else { // Either the UA only contains unknown shielded receivers (unlikely that we // wouldn't know about them), or it only contains transparent receivers diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 0f21145b77c..14f28d8f62e 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -217,6 +217,28 @@ UniValue validateaddress(const UniValue& params, bool fHelp) class DescribePaymentAddressVisitor { public: + UniValue operator()(const CKeyID &addr) const { + UniValue obj(UniValue::VOBJ); + obj.pushKV("type", "p2pkh"); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.pushKV("ismine", pwalletMain->HaveKey(addr)); + } +#endif + return obj; + } + + UniValue operator()(const CScriptID &addr) const { + UniValue obj(UniValue::VOBJ); + obj.pushKV("type", "p2sh"); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.pushKV("ismine", pwalletMain->HaveCScript(addr)); + } +#endif + return obj; + } + UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const { UniValue obj(UniValue::VOBJ); obj.pushKV("type", "sprout"); diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 0b1960b7647..9c564110aca 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -202,6 +202,14 @@ bool AsyncRPCOperation_shieldcoinbase::main_impl() { return std::visit(ShieldToAddress(this, sendAmount), tozaddr_); } +bool ShieldToAddress::operator()(const CKeyID &addr) const { + return false; +} + +bool ShieldToAddress::operator()(const CScriptID &addr) const { + return false; +} + bool ShieldToAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { // update the transaction with these inputs CMutableTransaction rawTx(m_op->tx_); diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index c1048c00a8f..403e9f40c17 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -103,6 +103,8 @@ class ShieldToAddress ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) : m_op(op), sendAmount(sendAmount) {} + bool operator()(const CKeyID &zaddr) const; + bool operator()(const CScriptID &zaddr) const; bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 984d02969a3..60151e5bbe2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2273,7 +2273,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) // We want to return unspent notes corresponding to any receiver within a // Unified Address. - for (const auto ra : std::visit(GetRawAddresses(), zaddr.value())) { + for (const auto ra : std::visit(GetRawShieldedAddresses(), zaddr.value())) { bool hasSpendingKey = std::visit(match { [&](const SaplingPaymentAddress& addr) { return pwalletMain->HaveSaplingSpendingKeyForAddress(addr); @@ -3154,10 +3154,16 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, decoded, nMinDepth, false, false); std::visit(match { + [&](const CKeyID& addr) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transparent addresses are not supported by this endpoint."); + }, + [&](const CScriptID& addr) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transparent addresses are not supported by this endpoint."); + }, [&](const libzcash::SproutPaymentAddress& addr) { bool hasSpendingKey = pwalletMain->HaveSproutSpendingKey(addr); auto nullifierSet = pwalletMain->GetNullifiersForAddresses({addr}); - for (SproutNoteEntry & entry : sproutEntries) { + for (const SproutNoteEntry& entry : sproutEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.jsop.hash.ToString()); obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); @@ -3182,7 +3188,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) [&](const libzcash::SaplingPaymentAddress& addr) { bool hasSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(addr); auto nullifierSet = pwalletMain->GetNullifiersForAddresses({addr}); - for (SaplingNoteEntry & entry : saplingEntries) { + for (const SaplingNoteEntry& entry : saplingEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.op.hash.ToString()); obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); @@ -3895,6 +3901,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) if (decoded.has_value()) { std::visit(match { + [&](const CKeyID&) { + // Handled elsewhere + }, + [&](const CScriptID&) { + // Handled elsewhere + }, [&](const libzcash::SaplingPaymentAddress& addr) { mtx.vShieldedOutput.push_back(OutputDescription()); }, @@ -4458,7 +4470,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (zaddr.has_value()) { // We want to merge notes corresponding to any receiver within a // Unified Address. - for (const libzcash::RawAddress& ra : std::visit(GetRawAddresses(), zaddr.value())) { + for (const libzcash::RawAddress& ra : std::visit(GetRawShieldedAddresses(), zaddr.value())) { zaddrs.insert(ra); if (std::holds_alternative(ra)) { isFromNonSprout = true; @@ -4489,30 +4501,34 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // Validate the destination address auto destaddress = params[1].get_str(); + bool isToTaddr = false; bool isToSproutZaddr = false; bool isToSaplingZaddr = false; - CTxDestination taddr = keyIO.DecodeDestination(destaddress); - if (!IsValidDestination(taddr)) { - auto zaddr = keyIO.DecodePaymentAddress(destaddress); - if (zaddr.has_value()) { - std::visit(match { - [&](libzcash::SaplingPaymentAddress addr) { - isToSaplingZaddr = true; - // If Sapling is not active, do not allow sending to a sapling addresses. - if (!saplingActive) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); - } - }, - [&](libzcash::SproutPaymentAddress addr) { - isToSproutZaddr = true; - }, - [&](libzcash::UnifiedAddress) { - // TODO UNIFIED + auto paymentAddress = keyIO.DecodePaymentAddress(destaddress); + if (paymentAddress.has_value()) { + std::visit(match { + [&](CKeyID addr) { + isToTaddr = true; + }, + [&](CScriptID addr) { + isToTaddr = true; + }, + [&](libzcash::SaplingPaymentAddress addr) { + isToSaplingZaddr = true; + // If Sapling is not active, do not allow sending to a sapling addresses. + if (!saplingActive) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); } - }, zaddr.value()); - } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); - } + }, + [&](libzcash::SproutPaymentAddress addr) { + isToSproutZaddr = true; + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED + } + }, paymentAddress.value()); + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); } if (canopyActive && isFromNonSprout && isToSproutZaddr) { @@ -4750,7 +4766,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) contextInfo.pushKV("toaddress", params[1]); contextInfo.pushKV("fee", ValueFromAmount(nFee)); - if (!sproutNoteInputs.empty() || !saplingNoteInputs.empty() || !IsValidDestination(taddr)) { + if (!sproutNoteInputs.empty() || !saplingNoteInputs.empty() || !isToTaddr) { // We have shielded inputs or the recipient is a shielded address, and // therefore we cannot create transactions before Sapling activates. if (!saplingActive) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4504d5d3000..ae0673c4a76 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5041,7 +5041,7 @@ void CWallet::GetFilteredNotes( std::set filterAddresses; if (address.has_value()) { - for (const auto ra : std::visit(GetRawAddresses(), address.value())) { + for (const auto ra : std::visit(GetRawShieldedAddresses(), address.value())) { filterAddresses.insert(ra); } } @@ -5185,37 +5185,57 @@ void CWallet::GetFilteredNotes( // -// Shielded key and address generalizations +// Payment address operations // +// PaymentAddressBelongsToWallet + +bool PaymentAddressBelongsToWallet::operator()(const CKeyID &addr) const +{ + CScript script = GetScriptForDestination(addr); + return m_wallet->HaveKey(addr) || m_wallet->HaveWatchOnly(script); +} +bool PaymentAddressBelongsToWallet::operator()(const CScriptID &addr) const +{ + CScript script = GetScriptForDestination(addr); + return m_wallet->HaveCScript(addr) || m_wallet->HaveWatchOnly(script); +} bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const { return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr); } - bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingIncomingViewingKey ivk; // If we have a SaplingExtendedSpendingKey in the wallet, then we will // also have the corresponding SaplingExtendedFullViewingKey. - return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) && + return + m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) && m_wallet->HaveSaplingFullViewingKey(ivk); } - bool PaymentAddressBelongsToWallet::operator()(const libzcash::UnifiedAddress &uaddr) const { // TODO return false; } -/// +// GetSourceForPaymentAddress +PaymentAddressSource GetSourceForPaymentAddress::operator()(const CKeyID &zaddr) const +{ + // TODO + return AddressNotFound; +} +PaymentAddressSource GetSourceForPaymentAddress::operator()(const CScriptID &zaddr) const +{ + // TODO + return AddressNotFound; +} PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { return Random; } - PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingIncomingViewingKey ivk; @@ -5240,15 +5260,24 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl return AddressNotFound; } } - PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const { // TODO return AddressNotFound; } -/// +// GetViewingKeyForPaymentAddress +std::optional GetViewingKeyForPaymentAddress::operator()( + const CKeyID &zaddr) const +{ + return std::nullopt; +} +std::optional GetViewingKeyForPaymentAddress::operator()( + const CScriptID &zaddr) const +{ + return std::nullopt; +} std::optional GetViewingKeyForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -5262,7 +5291,6 @@ std::optional GetViewingKeyForPaymentAddress::operator()( } return libzcash::ViewingKey(vk); } - std::optional GetViewingKeyForPaymentAddress::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { @@ -5277,19 +5305,27 @@ std::optional GetViewingKeyForPaymentAddress::operator()( return std::nullopt; } } - std::optional GetViewingKeyForPaymentAddress::operator()( const libzcash::UnifiedAddress &uaddr) const { // TODO - return libzcash::ViewingKey(); + return std::nullopt; } +// HaveSpendingKeyForPaymentAddress + +bool HaveSpendingKeyForPaymentAddress::operator()(const CKeyID &addr) const +{ + return m_wallet->HaveKey(addr); +} +bool HaveSpendingKeyForPaymentAddress::operator()(const CScriptID &addr) const +{ + return m_wallet->HaveCScript(addr); +} bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { return m_wallet->HaveSproutSpendingKey(zaddr); } - bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingIncomingViewingKey ivk; @@ -5299,13 +5335,24 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPayment m_wallet->GetSaplingFullViewingKey(ivk, extfvk) && m_wallet->HaveSaplingSpendingKey(extfvk); } - bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const { // TODO return false; } +// GetSpendingKeyForPaymentAddress + +std::optional GetSpendingKeyForPaymentAddress::operator()( + const CKeyID &zaddr) const +{ + return std::nullopt; +} +std::optional GetSpendingKeyForPaymentAddress::operator()( + const CScriptID &zaddr) const +{ + return std::nullopt; +} std::optional GetSpendingKeyForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -5316,7 +5363,6 @@ std::optional GetSpendingKeyForPaymentAddress::operator() return std::nullopt; } } - std::optional GetSpendingKeyForPaymentAddress::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { @@ -5327,7 +5373,6 @@ std::optional GetSpendingKeyForPaymentAddress::operator() return std::nullopt; } } - std::optional GetSpendingKeyForPaymentAddress::operator()( const libzcash::UnifiedAddress &uaddr) const { @@ -5335,6 +5380,8 @@ std::optional GetSpendingKeyForPaymentAddress::operator() return libzcash::SpendingKey(); } +// AddViewingKeyToWallet + KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey &vkey) const { auto addr = vkey.address(); @@ -5348,7 +5395,6 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey return KeyNotAdded; } } - KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFullViewingKey &extfvk) const { if (m_wallet->HaveSaplingSpendingKey(extfvk)) { return SpendingKeyExists; @@ -5361,6 +5407,8 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu } } +// AddSpendingKeyToWallet + KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { auto addr = sk.address(); KeyIO keyIO(Params()); @@ -5376,7 +5424,6 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKe return KeyNotAdded; } } - KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const { auto extfvk = sk.ToXFVK(); auto ivk = extfvk.fvk.in_viewing_key(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 57fda636663..e1790dad52f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1369,6 +1369,8 @@ class PaymentAddressBelongsToWallet public: PaymentAddressBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {} + bool operator()(const CKeyID &zaddr) const; + bool operator()(const CScriptID &zaddr) const; bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; @@ -1381,6 +1383,8 @@ class GetViewingKeyForPaymentAddress public: GetViewingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + std::optional operator()(const CKeyID &zaddr) const; + std::optional operator()(const CScriptID &zaddr) const; std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; @@ -1393,6 +1397,8 @@ class HaveSpendingKeyForPaymentAddress public: HaveSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + bool operator()(const CKeyID &addr) const; + bool operator()(const CScriptID &addr) const; bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; @@ -1405,8 +1411,11 @@ class GetSpendingKeyForPaymentAddress public: GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + std::optional operator()(const CKeyID &zaddr) const; + std::optional operator()(const CScriptID &zaddr) const; std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + // FIXME: this doesn't make sense std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; }; @@ -1426,6 +1435,8 @@ class GetSourceForPaymentAddress public: GetSourceForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + PaymentAddressSource operator()(const CKeyID &zaddr) const; + PaymentAddressSource operator()(const CScriptID &zaddr) const; PaymentAddressSource operator()(const libzcash::SproutPaymentAddress &zaddr) const; PaymentAddressSource operator()(const libzcash::SaplingPaymentAddress &zaddr) const; PaymentAddressSource operator()(const libzcash::UnifiedAddress &uaddr) const; diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index fe048c1a9ea..121f9061152 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -56,61 +56,67 @@ uint32_t TypecodeForReceiver::operator()( { return ZCASH_UA_TYPECODE_SAPLING; } - uint32_t TypecodeForReceiver::operator()( const CScriptID &p2sh) const { return ZCASH_UA_TYPECODE_P2SH; } - uint32_t TypecodeForReceiver::operator()( const CKeyID &p2sh) const { return ZCASH_UA_TYPECODE_P2PKH; } - uint32_t TypecodeForReceiver::operator()( const libzcash::UnknownReceiver &unknown) const { return unknown.typecode; } +// ReceiverToRawAddress + std::optional ReceiverToRawAddress::operator()( - const libzcash::SaplingPaymentAddress &zaddr) const + const CKeyID &p2sh) const { - return zaddr; + return std::nullopt; } - std::optional ReceiverToRawAddress::operator()( const CScriptID &p2sh) const { return std::nullopt; } - std::optional ReceiverToRawAddress::operator()( - const CKeyID &p2sh) const + const libzcash::SaplingPaymentAddress &zaddr) const { - return std::nullopt; + return zaddr; } - std::optional ReceiverToRawAddress::operator()( const libzcash::UnknownReceiver &p2sh) const { return std::nullopt; } +// RecipientForPaymentAddress + +std::optional RecipientForPaymentAddress::operator()( + const CKeyID &p2sh) const +{ + return std::nullopt; +} +std::optional RecipientForPaymentAddress::operator()( + const CScriptID &p2sh) const +{ + return std::nullopt; +} std::optional RecipientForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { return zaddr; } - std::optional RecipientForPaymentAddress::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { return zaddr; } - std::optional RecipientForPaymentAddress::operator()( const libzcash::UnifiedAddress &uaddr) const { @@ -122,19 +128,29 @@ std::optional RecipientForPaymentAddress::operator()( return std::nullopt; } -std::set GetRawAddresses::operator()( +// GetRawShieldedAddresses + +std::set GetRawShieldedAddresses::operator()( + const CKeyID &addr) const +{ + return {}; +} +std::set GetRawShieldedAddresses::operator()( + const CScriptID &addr) const +{ + return {}; +} +std::set GetRawShieldedAddresses::operator()( const libzcash::SproutPaymentAddress &zaddr) const { return {zaddr}; } - -std::set GetRawAddresses::operator()( +std::set GetRawShieldedAddresses::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { return {zaddr}; } - -std::set GetRawAddresses::operator()( +std::set GetRawShieldedAddresses::operator()( const libzcash::UnifiedAddress &uaddr) const { std::set ret; diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 83792eddf7b..5cd709c6cb4 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -122,6 +122,8 @@ class UnifiedAddress { /** Addresses that can appear in a string encoding. */ typedef std::variant< + CKeyID, + CScriptID, SproutPaymentAddress, SaplingPaymentAddress, UnifiedAddress> PaymentAddress; @@ -181,6 +183,8 @@ class RecipientForPaymentAddress { public: RecipientForPaymentAddress() {} + std::optional operator()(const CKeyID &addr) const; + std::optional operator()(const CScriptID &addr) const; std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; @@ -189,10 +193,12 @@ class RecipientForPaymentAddress { /** * Returns all protocol addresses contained within the given payment address. */ -class GetRawAddresses { +class GetRawShieldedAddresses { public: - GetRawAddresses() {} + GetRawShieldedAddresses() {} + std::set operator()(const CKeyID &addr) const; + std::set operator()(const CScriptID &addr) const; std::set operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::set operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::set operator()(const libzcash::UnifiedAddress &uaddr) const; From 4f2ff2a9f8579b81968082e14e64c75a14f2a140 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 20 Dec 2021 12:25:44 -0700 Subject: [PATCH 235/514] Remove spurious variant from asyncrpcoperation_sendmany The `spendingkey_` field previously used by asyncrpcoperation_sendmany caused a situation where call sites would assume the type of the spending key based upon boolean flags that were derived elsewhere. In attempting to support unified addresses, it is desirable to remove these boolean deductions and instead work primarily based on the direct evidence of the data at hand. This commit therefore removes the `spendingkey_` field that could potentially produce results that would disagree with these boolean values, in favor of such direct evidence, making these call sites a bit less error prone. --- src/wallet/asyncrpcoperation_sendmany.cpp | 15 +++--- src/wallet/asyncrpcoperation_sendmany.h | 1 - src/wallet/rpcdump.cpp | 66 ++++++++++++++++++----- src/wallet/wallet.cpp | 56 +++++++++++++++---- src/wallet/wallet.h | 31 +++++++---- 5 files changed, 129 insertions(+), 40 deletions(-) diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 30b8992070c..e2493e16178 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -106,7 +106,6 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( isfromzaddr_ = true; frompaymentaddress_ = address.value(); - spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()).value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address"); } @@ -306,9 +305,9 @@ bool AsyncRPCOperation_sendmany::main_impl() { // Get various necessary keys SaplingExpandedSpendingKey expsk; uint256 ovk; - if (isfromzaddr_) { - auto sk = std::get(spendingkey_); - expsk = sk.expsk; + auto saplingKey = std::visit(GetSaplingKeyForPaymentAddress(pwalletMain), frompaymentaddress_); + if (saplingKey.has_value()) { + expsk = saplingKey.value().expsk; ovk = expsk.full_viewing_key().ovk; } else { // Sending from a t-address, which we don't have an ovk for. Instead, @@ -523,6 +522,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { UniValue obj(UniValue::VOBJ); while (zOutputsDeque.size() > 0) { AsyncJoinSplitInfo info; + // FIXME: make sure this .value() call is safe info.vpub_old = 0; info.vpub_new = 0; int n = 0; @@ -641,7 +641,9 @@ bool AsyncRPCOperation_sendmany::main_impl() { 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(std::get(spendingkey_).receiving_key()); + // FIXME: make sure this .value() call is safe + auto sk = std::visit(GetSproutKeyForPaymentAddress(pwalletMain), frompaymentaddress_).value(); + ZCNoteDecryption decryptor(sk.receiving_key()); auto hSig = ZCJoinSplit::h_sig( prevJoinSplit.randomSeed, prevJoinSplit.nullifiers, @@ -1023,7 +1025,8 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( if (!witnesses[i]) { throw runtime_error("joinsplit input could not be found in tree"); } - info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], std::get(spendingkey_))); + auto sk = std::visit(GetSproutKeyForPaymentAddress(pwalletMain), frompaymentaddress_).value(); + info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], sk)); } // Make sure there are two inputs and two outputs diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index 7bd92dfeaad..cb3c4b9cbec 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -104,7 +104,6 @@ class AsyncRPCOperation_sendmany : public AsyncRPCOperation { bool isfromzaddr_; CTxDestination fromtaddr_; PaymentAddress frompaymentaddress_; - SpendingKey spendingkey_; Ed25519VerificationKey joinSplitPubKey_; Ed25519SigningKey joinSplitPrivKey_; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 0ad8e24b16a..faaea7cf006 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -3,6 +3,7 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "chain.h" +#include "core_io.h" #include "key_io.h" #include "rpc/server.h" #include "init.h" @@ -11,6 +12,7 @@ #include "script/standard.h" #include "sync.h" #include "util.h" +#include "util/match.h" #include "utiltime.h" #include "wallet.h" @@ -67,7 +69,7 @@ std::string DecodeDumpString(const std::string &str) { for (unsigned int pos = 0; pos < str.length(); pos++) { unsigned char c = str[pos]; if (c == '%' && pos+2 < str.length()) { - c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | + c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15)); pos += 2; } @@ -80,7 +82,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( "importprivkey \"zcashprivkey\" ( \"label\" rescan )\n" @@ -189,7 +191,7 @@ UniValue importaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 4) throw runtime_error( "importaddress \"address\" ( \"label\" rescan p2sh )\n" @@ -338,7 +340,7 @@ UniValue importwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "importwallet \"filename\"\n" @@ -476,7 +478,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "dumpprivkey \"t-addr\"\n" @@ -520,7 +522,7 @@ UniValue z_exportwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "z_exportwallet \"filename\"\n" @@ -769,10 +771,10 @@ UniValue z_importkey(const UniValue& params, bool fHelp) if (addResult == KeyNotAdded) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet"); } - + // whenever a key is imported, we need to scan the whole chain pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' - + // We want to scan for transactions and notes if (fRescan) { pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true); @@ -909,12 +911,48 @@ UniValue z_exportkey(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } - // Sapling support - auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()); - if (!sk) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr"); - } - return keyIO.EncodeSpendingKey(sk.value()); + std::string result = std::visit(match { + [&](const CKeyID& addr) { + CKey key; + if (pwalletMain->GetKey(addr, key)) { + return keyIO.EncodeSecret(key); + } else { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private key for this address."); + } + }, + [&](const CScriptID& addr) { + CScript redeemScript; + if (pwalletMain->GetCScript(addr, redeemScript)) { + return FormatScript(redeemScript); + } else { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private key for this address."); + } + }, + [&](const libzcash::SproutPaymentAddress& addr) { + libzcash::SproutSpendingKey key; + if (pwalletMain->GetSproutSpendingKey(addr, key)) { + return keyIO.EncodeSpendingKey(key); + } else { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private zkey for this zaddr"); + } + }, + [&](const libzcash::SaplingPaymentAddress& addr) { + libzcash::SaplingExtendedSpendingKey extsk; + if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) { + return keyIO.EncodeSpendingKey(extsk); + } else { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private zkey for this zaddr"); + } + }, + [&](const libzcash::UnifiedAddress& ua) { + throw JSONRPCError( + RPC_WALLET_ERROR, + "No serialized form is defined for unified spending keys. " + "Use the emergency recovery phrase for this wallet for backup purposes instead."); + return std::string(); //unreachable, here to make the compiler happy + } + }, address.value()); + return result; } UniValue z_exportviewingkey(const UniValue& params, bool fHelp) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ae0673c4a76..efba55107f7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5341,43 +5341,79 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress return false; } -// GetSpendingKeyForPaymentAddress +// GetSproutKeyForPaymentAddress -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( const CKeyID &zaddr) const { return std::nullopt; } -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( const CScriptID &zaddr) const { return std::nullopt; } -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { libzcash::SproutSpendingKey k; if (m_wallet->GetSproutSpendingKey(zaddr, k)) { - return libzcash::SpendingKey(k); + return k; } else { return std::nullopt; } } -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( + const libzcash::SaplingPaymentAddress &zaddr) const +{ + return std::nullopt; +} +std::optional GetSproutKeyForPaymentAddress::operator()( + const libzcash::UnifiedAddress &uaddr) const +{ + return std::nullopt; +} + +// GetSaplingKeyForPaymentAddress + +std::optional GetSaplingKeyForPaymentAddress::operator()( + const CKeyID &zaddr) const +{ + return std::nullopt; +} +std::optional GetSaplingKeyForPaymentAddress::operator()( + const CScriptID &zaddr) const +{ + return std::nullopt; +} +std::optional GetSaplingKeyForPaymentAddress::operator()( + const libzcash::SproutPaymentAddress &zaddr) const +{ + return std::nullopt; +} +std::optional GetSaplingKeyForPaymentAddress::operator()( const libzcash::SaplingPaymentAddress &zaddr) const { libzcash::SaplingExtendedSpendingKey extsk; if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) { - return libzcash::SpendingKey(extsk); + return extsk; } else { return std::nullopt; } } -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSaplingKeyForPaymentAddress::operator()( const libzcash::UnifiedAddress &uaddr) const { - // TODO - return libzcash::SpendingKey(); + for (const libzcash::Receiver& receiver: uaddr) { + auto saplingAddr = std::get_if(&receiver); + if (saplingAddr != nullptr) { + libzcash::SaplingExtendedSpendingKey extsk; + if (m_wallet->GetSaplingExtendedSpendingKey(*saplingAddr, extsk)) { + return extsk; + } + } + } + return std::nullopt; } // AddViewingKeyToWallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e1790dad52f..bbc6ecfc2f1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1404,19 +1404,32 @@ class HaveSpendingKeyForPaymentAddress bool operator()(const libzcash::UnifiedAddress &uaddr) const; }; -class GetSpendingKeyForPaymentAddress +class GetSproutKeyForPaymentAddress { private: CWallet *m_wallet; public: - GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} - - std::optional operator()(const CKeyID &zaddr) const; - std::optional operator()(const CScriptID &zaddr) const; - std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; - std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - // FIXME: this doesn't make sense - std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; + GetSproutKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + + std::optional operator()(const CKeyID &zaddr) const; + std::optional operator()(const CScriptID &zaddr) const; + std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; +}; + +class GetSaplingKeyForPaymentAddress +{ +private: + CWallet *m_wallet; +public: + GetSaplingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + + std::optional operator()(const CKeyID &zaddr) const; + std::optional operator()(const CScriptID &zaddr) const; + std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; }; enum PaymentAddressSource { From f5d4f6fef1c3553717ca589f4bb881afd2c63898 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 23 Dec 2021 23:06:03 -0700 Subject: [PATCH 236/514] Remove `RawAddress` This adds a new `AddrSet` type for use in note retrieval as a filter, in place of a heterogeneous list of `RawAddress` values. `RawAddress` will complicate the handling of addresses within the wallet after the addition of unified addresses, because it does not contain transparent receiver types, and if we retain this polymorphism it means a lot of invalid states are represented in places we don't want them to be. It's better to figure out what types of addresses we're working with as soon as possible after parsing, and use monomorphic calls from there on in. --- qa/rpc-tests/wallet_shieldingcoinbase.py | 2 +- src/miner.h | 23 +-- src/rust/src/zip339_ffi.rs | 2 +- src/wallet/asyncrpcoperation_sendmany.cpp | 6 +- src/wallet/rpcwallet.cpp | 181 ++++++++++------------ src/wallet/wallet.cpp | 147 +++++++++++------- src/wallet/wallet.h | 51 ++++-- src/zcash/Address.cpp | 91 ----------- src/zcash/Address.hpp | 44 ------ 9 files changed, 225 insertions(+), 322 deletions(-) diff --git a/qa/rpc-tests/wallet_shieldingcoinbase.py b/qa/rpc-tests/wallet_shieldingcoinbase.py index c6be8fdce53..01f7ee8e6b4 100755 --- a/qa/rpc-tests/wallet_shieldingcoinbase.py +++ b/qa/rpc-tests/wallet_shieldingcoinbase.py @@ -151,7 +151,7 @@ def run_test (self): results = self.nodes[1].z_listunspent(1, 999, False, [myzaddr]) except JSONRPCException as e: errorString = e.error['message'] - assert_equal("Invalid parameter, spending key for address does not belong to wallet" in errorString, True) + assert_equal("Invalid parameter, spending key for an address does not belong to the wallet.", errorString) # Verify that debug=zrpcunsafe logs params, and that full txid is associated with opid initialized_line = check_node_log(self, 0, myopid + ": z_sendmany initialized", False) diff --git a/src/miner.h b/src/miner.h index 6f9073dc578..3b2081f58ef 100644 --- a/src/miner.h +++ b/src/miner.h @@ -45,25 +45,12 @@ class ExtractMinerAddress return addr; } std::optional operator()(const libzcash::UnifiedAddress &addr) const { - auto recipient = RecipientForPaymentAddress()(addr); - if (recipient.has_value()) { - // This looks like a recursive call, but we are actually calling - // ExtractMinerAddress with a different type: - // - libzcash::PaymentAddress has a libzcash::UnifiedAddress - // alternative, which invokes this method. - // - RecipientForPaymentAddress() returns libzcash::RawAddress, - // which does not have a libzcash::UnifiedAddress alternative. - // - // This works because std::visit does not require the visitor to - // solely match the std::variant, only that it can handle all of - // the variant's alternatives. - return std::visit(ExtractMinerAddress(), recipient.value()); - } else { - // Either the UA only contains unknown shielded receivers (unlikely that we - // wouldn't know about them), or it only contains transparent receivers - // (which are invalid). - return std::nullopt; + for (const auto& receiver: addr) { + if (std::holds_alternative(receiver)) { + return std::get(receiver); + } } + return std::nullopt; } }; diff --git a/src/rust/src/zip339_ffi.rs b/src/rust/src/zip339_ffi.rs index eeafa5bcd0f..852c1092152 100644 --- a/src/rust/src/zip339_ffi.rs +++ b/src/rust/src/zip339_ffi.rs @@ -63,7 +63,7 @@ pub extern "C" fn zip339_free_phrase(phrase: *const c_char) { if !phrase.is_null() { unsafe { // It is correct to cast away const here; the memory is not actually immutable. - CString::from_raw(phrase as *mut c_char); + drop(CString::from_raw(phrase as *mut c_char)); } } } diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index e2493e16178..ac2880e86a9 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -935,7 +935,11 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { std::vector saplingEntries; // TODO: move this to the caller auto zaddr = KeyIO(Params()).DecodePaymentAddress(fromaddress_); - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddr, mindepth_); + std::optional noteFilter = std::nullopt; + if (zaddr.has_value()) { + noteFilter = AddrSet::ForPaymentAddresses(std::vector({zaddr.value()})); + } + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noteFilter, mindepth_); // If using the TransactionBuilder, we only want Sapling notes. // If not using it, we only want Sprout notes. diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 60151e5bbe2..445175d7382 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1987,7 +1987,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) return true; } -CAmount getBalanceZaddr(std::optional address, int minDepth = 1, int maxDepth = INT_MAX, bool ignoreUnspendable=true); +CAmount getBalanceZaddr(std::optional address, int minDepth = 1, int maxDepth = INT_MAX, bool ignoreUnspendable=true); UniValue getwalletinfo(const UniValue& params, bool fHelp) { @@ -2241,8 +2241,6 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Maximum number of confirmations must be greater or equal to the minimum number of confirmations"); } - std::set zaddrs = {}; - bool fIncludeWatchonly = false; if (params.size() > 2) { fIncludeWatchonly = params[2].get_bool(); @@ -2250,109 +2248,93 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + std::optional noteFilter = std::nullopt; + std::set> sproutNullifiers; + std::set> saplingNullifiers; + KeyIO keyIO(Params()); // User has supplied zaddrs to filter on if (params.size() > 3) { UniValue addresses = params[3].get_array(); - if (addresses.size()==0) + if (addresses.size() == 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, addresses array is empty."); - - // Keep track of addresses to spot duplicates - set setAddress; + } // Sources + std::vector sourceAddrs; for (const UniValue& o : addresses.getValues()) { if (!o.isStr()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string"); } - string address = o.get_str(); - auto zaddr = keyIO.DecodePaymentAddress(address); + + auto zaddr = keyIO.DecodePaymentAddress(o.get_str()); if (!zaddr.has_value()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, not a valid Zcash address: ") + o.get_str()); } - // We want to return unspent notes corresponding to any receiver within a - // Unified Address. - for (const auto ra : std::visit(GetRawShieldedAddresses(), zaddr.value())) { - bool hasSpendingKey = std::visit(match { - [&](const SaplingPaymentAddress& addr) { - return pwalletMain->HaveSaplingSpendingKeyForAddress(addr); - }, - [&](const SproutPaymentAddress& addr) { - return pwalletMain->HaveSproutSpendingKey(addr); - } - }, ra); - - // If we don't include watchonly addresses, we must reject any address - // for which we do not have the spending key. - if (!fIncludeWatchonly && !hasSpendingKey) { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address); - } + sourceAddrs.push_back(zaddr.value()); + } - zaddrs.insert(ra); - } + noteFilter = AddrSet::ForPaymentAddresses(sourceAddrs); + sproutNullifiers = pwalletMain->GetSproutNullifiers(noteFilter.value().GetSproutAddresses()); + saplingNullifiers = pwalletMain->GetSaplingNullifiers(noteFilter.value().GetSaplingAddresses()); - if (setAddress.count(address)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address); - } - setAddress.insert(address); + // If we don't include watchonly addresses, we must reject any address + // for which we do not have the spending key. + if (!fIncludeWatchonly && !pwalletMain->HasSpendingKeys(noteFilter.value())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for an address does not belong to the wallet.")); } - } - else { + } else { // User did not provide zaddrs, so use default i.e. all addresses std::set sproutzaddrs = {}; pwalletMain->GetSproutPaymentAddresses(sproutzaddrs); + sproutNullifiers = pwalletMain->GetSproutNullifiers(sproutzaddrs); // Sapling support std::set saplingzaddrs = {}; pwalletMain->GetSaplingPaymentAddresses(saplingzaddrs); - - zaddrs.insert(sproutzaddrs.begin(), sproutzaddrs.end()); - zaddrs.insert(saplingzaddrs.begin(), saplingzaddrs.end()); + saplingNullifiers = pwalletMain->GetSaplingNullifiers(saplingzaddrs); } UniValue results(UniValue::VARR); - if (zaddrs.size() > 0) { - std::vector sproutEntries; - std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); - auto nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs); - - for (auto & entry : sproutEntries) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("txid", entry.jsop.hash.ToString()); - obj.pushKV("jsindex", (int)entry.jsop.js ); - obj.pushKV("jsoutindex", (int)entry.jsop.n); - obj.pushKV("confirmations", entry.confirmations); - bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(entry.address); - obj.pushKV("spendable", hasSproutSpendingKey); - obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); - obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); - std::string data(entry.memo.begin(), entry.memo.end()); - obj.pushKV("memo", HexStr(data)); - if (hasSproutSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); - } - results.push_back(obj); + std::vector sproutEntries; + std::vector saplingEntries; + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noteFilter, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); + + for (auto & entry : sproutEntries) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("txid", entry.jsop.hash.ToString()); + obj.pushKV("jsindex", (int)entry.jsop.js ); + obj.pushKV("jsoutindex", (int)entry.jsop.n); + obj.pushKV("confirmations", entry.confirmations); + bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(entry.address); + obj.pushKV("spendable", hasSproutSpendingKey); + obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); + std::string data(entry.memo.begin(), entry.memo.end()); + obj.pushKV("memo", HexStr(data)); + if (hasSproutSpendingKey) { + obj.pushKV("change", pwalletMain->IsNoteSproutChange(sproutNullifiers, entry.address, entry.jsop)); } + results.push_back(obj); + } - for (auto & entry : saplingEntries) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("txid", entry.op.hash.ToString()); - obj.pushKV("outindex", (int)entry.op.n); - obj.pushKV("confirmations", entry.confirmations); - bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address); - obj.pushKV("spendable", hasSaplingSpendingKey); - // TODO: If we found this entry via a UA, show that instead. - obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); - obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value() - obj.pushKV("memo", HexStr(entry.memo)); - if (hasSaplingSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); - } - results.push_back(obj); + for (auto & entry : saplingEntries) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("txid", entry.op.hash.ToString()); + obj.pushKV("outindex", (int)entry.op.n); + obj.pushKV("confirmations", entry.confirmations); + bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address); + obj.pushKV("spendable", hasSaplingSpendingKey); + // TODO: If we found this entry via a UA, show that instead. + obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value() + obj.pushKV("memo", HexStr(entry.memo)); + if (hasSaplingSpendingKey) { + obj.pushKV("change", pwalletMain->IsNoteSaplingChange(saplingNullifiers, entry.address, entry.op)); } + results.push_back(obj); } return results; @@ -3053,18 +3035,18 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign return balance; } -CAmount getBalanceZaddr(std::optional address, int minDepth, int maxDepth, bool ignoreUnspendable) { +CAmount getBalanceZaddr(std::optional address, int minDepth, int maxDepth, bool ignoreUnspendable) { CAmount balance = 0; std::vector sproutEntries; std::vector saplingEntries; LOCK2(cs_main, pwalletMain->cs_wallet); - std::set filterAddresses; - if (address) { - filterAddresses.insert(address.value()); + std::optional noteFilter = std::nullopt; + if (address.has_value()) { + noteFilter = AddrSet::ForPaymentAddresses(std::vector({address.value()})); } - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, maxDepth, true, ignoreUnspendable); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noteFilter, minDepth, maxDepth, true, ignoreUnspendable); for (auto & entry : sproutEntries) { balance += CAmount(entry.note.value()); } @@ -3151,7 +3133,8 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) UniValue result(UniValue::VARR); std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, decoded, nMinDepth, false, false); + auto noteFilter = AddrSet::ForPaymentAddresses(std::vector({decoded.value()})); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noteFilter, nMinDepth, INT_MAX, false, false); std::visit(match { [&](const CKeyID& addr) { @@ -3162,7 +3145,10 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) }, [&](const libzcash::SproutPaymentAddress& addr) { bool hasSpendingKey = pwalletMain->HaveSproutSpendingKey(addr); - auto nullifierSet = pwalletMain->GetNullifiersForAddresses({addr}); + std::set> nullifierSet; + if (hasSpendingKey) { + nullifierSet = pwalletMain->GetSproutNullifiers({addr}); + } for (const SproutNoteEntry& entry : sproutEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.jsop.hash.ToString()); @@ -3187,7 +3173,10 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) }, [&](const libzcash::SaplingPaymentAddress& addr) { bool hasSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(addr); - auto nullifierSet = pwalletMain->GetNullifiersForAddresses({addr}); + std::set> nullifierSet; + if (hasSpendingKey) { + nullifierSet = pwalletMain->GetSaplingNullifiers({addr}); + } for (const SaplingNoteEntry& entry : saplingEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.op.hash.ToString()); @@ -3273,8 +3262,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) nBalance = getBalanceTaddr(fromaddress, nMinDepth, false); } else { // TODO: Return an error if a UA is provided (once we support UAs). - auto zaddr = std::visit(RecipientForPaymentAddress(), pa.value()).value(); - nBalance = getBalanceZaddr(zaddr, nMinDepth, INT_MAX, false); + nBalance = getBalanceZaddr(pa, nMinDepth, INT_MAX, false); } // inZat @@ -4069,10 +4057,9 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) { { std::vector sproutEntries; std::vector saplingEntries; - std::set noFilter; // Here we are looking for any and all Sprout notes for which we have the spending key, including those // which are locked and/or only exist in the mempool, as they should be included in the unmigrated amount. - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noFilter, 0, INT_MAX, true, true, false); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0, INT_MAX, true, true, false); CAmount unmigratedAmount = 0; for (const auto& sproutEntry : sproutEntries) { unmigratedAmount += sproutEntry.note.value(); @@ -4432,8 +4419,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) bool useAnyUTXO = false; bool useAnySprout = false; bool useAnySapling = false; - std::set taddrs = {}; - std::set zaddrs = {}; + std::set taddrs; + std::vector zaddrs; UniValue addresses = params[0].get_array(); if (addresses.size()==0) @@ -4468,13 +4455,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } else { auto zaddr = keyIO.DecodePaymentAddress(address); if (zaddr.has_value()) { - // We want to merge notes corresponding to any receiver within a - // Unified Address. - for (const libzcash::RawAddress& ra : std::visit(GetRawShieldedAddresses(), zaddr.value())) { - zaddrs.insert(ra); - if (std::holds_alternative(ra)) { - isFromNonSprout = true; - } + zaddrs.push_back(zaddr.value()); + if (std::holds_alternative(zaddr.value())) { + isFromNonSprout = true; } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address); @@ -4651,7 +4634,11 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // Get available notes std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs); + std::optional noteFilter = + useAnySprout || useAnySapling ? + std::nullopt : + std::optional(AddrSet::ForPaymentAddresses(zaddrs)); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noteFilter); // If Sapling is not active, do not allow sending from a sapling addresses. if (!saplingActive && saplingEntries.size() > 0) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index efba55107f7..0d37b8e34a8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -684,49 +684,58 @@ void CWallet::SetBestChain(const CBlockLocator& loc) SetBestChainINTERNAL(walletdb, loc); } -std::set> CWallet::GetNullifiersForAddresses( - const std::set & addresses) -{ - std::set> nullifierSet; - // Sapling ivk -> list of addrs map - // (There may be more than one diversified address for a given ivk.) - std::map> ivkMap; - for (const auto & addr : addresses) { - auto saplingAddr = std::get_if(&addr); - if (saplingAddr != nullptr) { - libzcash::SaplingIncomingViewingKey ivk; - this->GetSaplingIncomingViewingKey(*saplingAddr, ivk); - ivkMap[ivk].push_back(*saplingAddr); - } - } - for (const auto & txPair : mapWallet) { - // Sprout - for (const auto & noteDataPair : txPair.second.mapSproutNoteData) { - auto & noteData = noteDataPair.second; - auto & nullifier = noteData.nullifier; - auto & address = noteData.address; - if (nullifier && addresses.count(address)) { - nullifierSet.insert(std::make_pair(address, nullifier.value())); +std::set> CWallet::GetSproutNullifiers( + const std::set& addresses) { + std::set> nullifierSet; + if (!addresses.empty()) { + for (const auto& txPair : mapWallet) { + for (const auto & noteDataPair : txPair.second.mapSproutNoteData) { + auto & noteData = noteDataPair.second; + auto & nullifier = noteData.nullifier; + auto & address = noteData.address; + if (nullifier && addresses.count(address) > 0) { + nullifierSet.insert(std::make_pair(address, nullifier.value())); + } } } - // Sapling - for (const auto & noteDataPair : txPair.second.mapSaplingNoteData) { - auto & noteData = noteDataPair.second; - auto & nullifier = noteData.nullifier; - auto & ivk = noteData.ivk; - if (nullifier && ivkMap.count(ivk)) { - for (const auto & addr : ivkMap[ivk]) { - nullifierSet.insert(std::make_pair(addr, nullifier.value())); + } + + return nullifierSet; +} + +std::set> CWallet::GetSaplingNullifiers( + const std::set& addresses) { + std::set> nullifierSet; + if (!addresses.empty()) { + // Sapling ivk -> list of addrs map + // (There may be more than one diversified address for a given ivk.) + std::map> ivkMap; + for (const auto & addr : addresses) { + libzcash::SaplingIncomingViewingKey ivk; + this->GetSaplingIncomingViewingKey(addr, ivk); + ivkMap[ivk].push_back(addr); + } + + for (const auto& txPair : mapWallet) { + for (const auto& noteDataPair : txPair.second.mapSaplingNoteData) { + auto & noteData = noteDataPair.second; + auto & nullifier = noteData.nullifier; + auto & ivk = noteData.ivk; + if (nullifier && ivkMap.count(ivk) > 0) { + for (const auto & addr : ivkMap[ivk]) { + nullifierSet.insert(std::make_pair(addr, nullifier.value())); + } } } } } + return nullifierSet; } bool CWallet::IsNoteSproutChange( - const std::set> & nullifierSet, - const libzcash::RawAddress & address, + const std::set> & nullifierSet, + const libzcash::SproutPaymentAddress& address, const JSOutPoint & jsop) { // A Note is marked as "change" if the address that received it @@ -748,8 +757,9 @@ bool CWallet::IsNoteSproutChange( return false; } -bool CWallet::IsNoteSaplingChange(const std::set> & nullifierSet, - const libzcash::RawAddress & address, +bool CWallet::IsNoteSaplingChange( + const std::set> & nullifierSet, + const libzcash::SaplingPaymentAddress& address, const SaplingOutPoint & op) { // A Note is marked as "change" if the address that received it @@ -760,6 +770,8 @@ bool CWallet::IsNoteSaplingChange(const std::set& sproutEntries, - std::vector& saplingEntries, - std::optional address, - int minDepth, - bool ignoreSpent, - bool requireSpendingKey) -{ - std::set filterAddresses; +AddrSet AddrSet::ForPaymentAddresses(const std::vector& paymentAddrs) { + AddrSet addrs; + for (const auto& addr: paymentAddrs) { + std::visit(match { + [&](const CKeyID& keyId) { }, + [&](const CScriptID& scriptId) { }, + [&](const libzcash::SproutPaymentAddress& addr) { + addrs.sproutAddresses.insert(addr); + }, + [&](const libzcash::SaplingPaymentAddress& addr) { + addrs.saplingAddresses.insert(addr); + }, + [&](const libzcash::UnifiedAddress& uaddr) { + for (auto& receiver : uaddr) { + std::visit(match { + [&](const libzcash::SaplingPaymentAddress& addr) { + addrs.saplingAddresses.insert(addr); + }, + [&](const auto& other) { } + }, receiver); + } + }, + }, addr); + } + return addrs; +} - if (address.has_value()) { - for (const auto ra : std::visit(GetRawShieldedAddresses(), address.value())) { - filterAddresses.insert(ra); +bool CWallet::HasSpendingKeys(const AddrSet& addrSet) const { + for (const auto& zaddr : addrSet.GetSproutAddresses()) { + if (!HaveSproutSpendingKey(zaddr)) { + return false; } } - - GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey); + for (const auto& zaddr : addrSet.GetSaplingAddresses()) { + if (!HaveSaplingSpendingKeyForAddress(zaddr)) { + return false; + } + } + return true; } + /** * Find notes in the wallet filtered by payment addresses, min depth, max depth, * if the note is spent, if a spending key is required, and if the notes are locked. @@ -5057,7 +5088,7 @@ void CWallet::GetFilteredNotes( void CWallet::GetFilteredNotes( std::vector& sproutEntries, std::vector& saplingEntries, - std::set& filterAddresses, + const std::optional& noteFilter, int minDepth, int maxDepth, bool ignoreSpent, @@ -5087,8 +5118,8 @@ void CWallet::GetFilteredNotes( SproutNoteData nd = pair.second; SproutPaymentAddress pa = nd.address; - // skip notes which belong to a different payment address in the wallet - if (!(filterAddresses.empty() || filterAddresses.count(pa))) { + // skip notes which do not conform to the filter, if supplied + if (noteFilter.has_value() && !noteFilter.value().HasSproutAddress(pa)) { continue; } @@ -5157,8 +5188,8 @@ void CWallet::GetFilteredNotes( assert(static_cast(maybe_pa)); auto pa = maybe_pa.value(); - // skip notes which belong to a different payment address in the wallet - if (!(filterAddresses.empty() || filterAddresses.count(pa))) { + // skip notes which do not conform to the filter, if supplied + if (noteFilter.has_value() && !noteFilter.value().HasSaplingAddress(pa)) { continue; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index bbc6ecfc2f1..f32c4873bbe 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -623,7 +623,31 @@ class CWalletTx : public CMerkleTx }; +class AddrSet { +private: + std::set sproutAddresses; + std::set saplingAddresses; + AddrSet() {} +public: + static AddrSet ForPaymentAddresses(const std::vector& addrs); + + const std::set& GetSproutAddresses() const { + return sproutAddresses; + } + + const std::set& GetSaplingAddresses() const { + return saplingAddresses; + } + + bool HasSproutAddress(libzcash::SproutPaymentAddress addr) const { + return sproutAddresses.count(addr) > 0; + } + + bool HasSaplingAddress(libzcash::SaplingPaymentAddress addr) const { + return saplingAddresses.count(addr) > 0; + } +}; class COutput { @@ -1194,9 +1218,20 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void AddPendingSaplingMigrationTx(const CTransaction& tx); /** Saves witness caches and best block locator to disk. */ void SetBestChain(const CBlockLocator& loc); - std::set> GetNullifiersForAddresses(const std::set & addresses); - bool IsNoteSproutChange(const std::set> & nullifierSet, const libzcash::RawAddress & address, const JSOutPoint & entry); - bool IsNoteSaplingChange(const std::set> & nullifierSet, const libzcash::RawAddress & address, const SaplingOutPoint & entry); + + std::set> GetSproutNullifiers( + const std::set& addresses); + bool IsNoteSproutChange( + const std::set> & nullifierSet, + const libzcash::SproutPaymentAddress& address, + const JSOutPoint & entry); + + std::set> GetSaplingNullifiers( + const std::set& addresses); + bool IsNoteSaplingChange( + const std::set> & nullifierSet, + const libzcash::SaplingPaymentAddress& address, + const SaplingOutPoint & entry); DBErrors LoadWallet(bool& fFirstRunRet); DBErrors ZapWalletTx(std::vector& vWtx); @@ -1304,19 +1339,13 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); - /* Find notes filtered by (optional) payment address, min depth, ability to spend */ - void GetFilteredNotes(std::vector& sproutEntries, - std::vector& saplingEntries, - std::optional address, - int minDepth=1, - bool ignoreSpent=true, - bool requireSpendingKey=true); + bool HasSpendingKeys(const AddrSet& noteFilter) const; /* Find notes filtered by payment addresses, min depth, max depth, if they are spent, if a spending key is required, and if they are locked */ void GetFilteredNotes(std::vector& sproutEntries, std::vector& saplingEntries, - std::set& filterAddresses, + const std::optional& noteFilter, int minDepth=1, int maxDepth=INT_MAX, bool ignoreSpent=true, diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 121f9061152..65e0084e787 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -71,94 +71,3 @@ uint32_t TypecodeForReceiver::operator()( { return unknown.typecode; } - -// ReceiverToRawAddress - -std::optional ReceiverToRawAddress::operator()( - const CKeyID &p2sh) const -{ - return std::nullopt; -} -std::optional ReceiverToRawAddress::operator()( - const CScriptID &p2sh) const -{ - return std::nullopt; -} -std::optional ReceiverToRawAddress::operator()( - const libzcash::SaplingPaymentAddress &zaddr) const -{ - return zaddr; -} -std::optional ReceiverToRawAddress::operator()( - const libzcash::UnknownReceiver &p2sh) const -{ - return std::nullopt; -} - -// RecipientForPaymentAddress - -std::optional RecipientForPaymentAddress::operator()( - const CKeyID &p2sh) const -{ - return std::nullopt; -} -std::optional RecipientForPaymentAddress::operator()( - const CScriptID &p2sh) const -{ - return std::nullopt; -} -std::optional RecipientForPaymentAddress::operator()( - const libzcash::SproutPaymentAddress &zaddr) const -{ - return zaddr; -} -std::optional RecipientForPaymentAddress::operator()( - const libzcash::SaplingPaymentAddress &zaddr) const -{ - return zaddr; -} -std::optional RecipientForPaymentAddress::operator()( - const libzcash::UnifiedAddress &uaddr) const -{ - for (auto& receiver : uaddr) { - // Return the first one. - return std::visit(ReceiverToRawAddress(), receiver); - } - - return std::nullopt; -} - -// GetRawShieldedAddresses - -std::set GetRawShieldedAddresses::operator()( - const CKeyID &addr) const -{ - return {}; -} -std::set GetRawShieldedAddresses::operator()( - const CScriptID &addr) const -{ - return {}; -} -std::set GetRawShieldedAddresses::operator()( - const libzcash::SproutPaymentAddress &zaddr) const -{ - return {zaddr}; -} -std::set GetRawShieldedAddresses::operator()( - const libzcash::SaplingPaymentAddress &zaddr) const -{ - return {zaddr}; -} -std::set GetRawShieldedAddresses::operator()( - const libzcash::UnifiedAddress &uaddr) const -{ - std::set ret; - for (auto& receiver : uaddr) { - auto ra = std::visit(ReceiverToRawAddress(), receiver); - if (ra) { - ret.insert(*ra); - } - } - return ret; -} diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 5cd709c6cb4..11e4f5bfe5a 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -12,9 +12,6 @@ namespace libzcash { -/** Protocol addresses that can receive funds in a transaction. */ -typedef std::variant RawAddress; - class UnknownReceiver { public: uint32_t typecode; @@ -163,45 +160,4 @@ class TypecodeForReceiver { uint32_t operator()(const libzcash::UnknownReceiver &p2pkh) const; }; -/** - * Converts the given UA receiver to a protocol address, if it is a shielded receiver. - */ -class ReceiverToRawAddress { -public: - ReceiverToRawAddress() {} - - std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - std::optional operator()(const CScriptID &p2sh) const; - std::optional operator()(const CKeyID &p2pkh) const; - std::optional operator()(const libzcash::UnknownReceiver &p2pkh) const; -}; - -/** - * Returns the protocol address that should be used in transaction outputs. - */ -class RecipientForPaymentAddress { -public: - RecipientForPaymentAddress() {} - - std::optional operator()(const CKeyID &addr) const; - std::optional operator()(const CScriptID &addr) const; - std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; - std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; -}; - -/** - * Returns all protocol addresses contained within the given payment address. - */ -class GetRawShieldedAddresses { -public: - GetRawShieldedAddresses() {} - - std::set operator()(const CKeyID &addr) const; - std::set operator()(const CScriptID &addr) const; - std::set operator()(const libzcash::SproutPaymentAddress &zaddr) const; - std::set operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - std::set operator()(const libzcash::UnifiedAddress &uaddr) const; -}; - #endif // ZC_ADDRESS_H_ From d376e2838285376de2b07e468bda2b4319030cf5 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 29 Dec 2021 11:55:46 -0700 Subject: [PATCH 237/514] Replace `DecodeDestination` in `GetMinerAddress` with `DecodePaymentAddress` --- src/miner.cpp | 45 +++++++++++++++++++++++++++++---------------- src/miner.h | 25 +++++-------------------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index d2a21a9870f..7ab770c2f06 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -702,27 +702,40 @@ class MinerAddressScript : public CReserveScript void KeepScript() {} }; +std::optional ExtractMinerAddress::operator()(const CKeyID &keyID) const { + boost::shared_ptr mAddr(new MinerAddressScript()); + mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; + return mAddr; +} +std::optional ExtractMinerAddress::operator()(const CScriptID &addr) const { + return std::nullopt; +} +std::optional ExtractMinerAddress::operator()(const libzcash::SproutPaymentAddress &addr) const { + return std::nullopt; +} +std::optional ExtractMinerAddress::operator()(const libzcash::SaplingPaymentAddress &addr) const { + return addr; +} +std::optional ExtractMinerAddress::operator()(const libzcash::UnifiedAddress &addr) const { + for (const auto& receiver: addr) { + if (std::holds_alternative(receiver)) { + return std::get(receiver); + } + } + return std::nullopt; +} + + void GetMinerAddress(MinerAddress &minerAddress) { KeyIO keyIO(Params()); - // Try a transparent address first auto mAddrArg = GetArg("-mineraddress", ""); - CTxDestination addr = keyIO.DecodeDestination(mAddrArg); - if (IsValidDestination(addr)) { - boost::shared_ptr mAddr(new MinerAddressScript()); - CKeyID keyID = std::get(addr); - - mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; - minerAddress = mAddr; - } else { - // Try a payment address - auto zaddr0 = keyIO.DecodePaymentAddress(mAddrArg); - if (zaddr0.has_value()) { - auto zaddr = std::visit(ExtractMinerAddress(), zaddr0.value()); - if (zaddr.has_value()) { - minerAddress = zaddr.value(); - } + auto zaddr0 = keyIO.DecodePaymentAddress(mAddrArg); + if (zaddr0.has_value()) { + auto zaddr = std::visit(ExtractMinerAddress(), zaddr0.value()); + if (zaddr.has_value()) { + minerAddress = zaddr.value(); } } } diff --git a/src/miner.h b/src/miner.h index 3b2081f58ef..b6851f3f36b 100644 --- a/src/miner.h +++ b/src/miner.h @@ -32,26 +32,11 @@ class ExtractMinerAddress public: ExtractMinerAddress() {} - std::optional operator()(const CKeyID &addr) const { - return std::nullopt; - } - std::optional operator()(const CScriptID &addr) const { - return std::nullopt; - } - std::optional operator()(const libzcash::SproutPaymentAddress &addr) const { - return std::nullopt; - } - std::optional operator()(const libzcash::SaplingPaymentAddress &addr) const { - return addr; - } - std::optional operator()(const libzcash::UnifiedAddress &addr) const { - for (const auto& receiver: addr) { - if (std::holds_alternative(receiver)) { - return std::get(receiver); - } - } - return std::nullopt; - } + std::optional operator()(const CKeyID &keyID) const; + std::optional operator()(const CScriptID &addr) const; + std::optional operator()(const libzcash::SproutPaymentAddress &addr) const; + std::optional operator()(const libzcash::SaplingPaymentAddress &addr) const; + std::optional operator()(const libzcash::UnifiedAddress &addr) const; }; class KeepMinerAddress From 20266ac91112cf753a6c74b8b13d2d2c49540937 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 29 Dec 2021 13:44:55 -0700 Subject: [PATCH 238/514] Remove uses of KeyIO::DecodeDestination --- qa/rpc-tests/wallet_shieldingcoinbase.py | 8 +- src/chainparams.cpp | 8 +- src/consensus/params.cpp | 29 ++- src/init.cpp | 15 +- .../asyncrpcoperation_mergetoaddress.cpp | 39 +++- src/wallet/asyncrpcoperation_sendmany.cpp | 38 +++- src/wallet/asyncrpcoperation_sendmany.h | 10 +- src/wallet/rpcwallet.cpp | 182 ++++++++++-------- src/wallet/test/rpc_wallet_tests.cpp | 17 +- 9 files changed, 212 insertions(+), 134 deletions(-) diff --git a/qa/rpc-tests/wallet_shieldingcoinbase.py b/qa/rpc-tests/wallet_shieldingcoinbase.py index 01f7ee8e6b4..129af7011c1 100755 --- a/qa/rpc-tests/wallet_shieldingcoinbase.py +++ b/qa/rpc-tests/wallet_shieldingcoinbase.py @@ -82,9 +82,11 @@ def run_test (self): # Node 3 will test that watch only address utxos are not selected self.nodes[3].importaddress(mytaddr) recipients= [{"address":myzaddr, "amount": Decimal('1')}] - myopid = self.nodes[3].z_sendmany(mytaddr, recipients) - - wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient transparent funds, no UTXOs found for taddr from address.", 10) + try: + myopid = self.nodes[3].z_sendmany(mytaddr, recipients) + except JSONRPCException as e: + errorString = e.error['message'] + assert_equal("Invalid from address: does not belong to this node, spending key not found.", errorString); # This send will fail because our wallet does not allow any change when shielding a coinbase utxo, # as it's currently not possible to specify a change address in z_sendmany. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d9a7cb182e1..d117db8ee48 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -822,10 +822,10 @@ CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const { assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight(nHeight)); KeyIO keyIO(*this); - CTxDestination address = keyIO.DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str()); - assert(IsValidDestination(address)); - assert(IsScriptDestination(address)); - CScriptID scriptID = std::get(address); // address is a variant + auto address = keyIO.DecodePaymentAddress(GetFoundersRewardAddressAtHeight(nHeight).c_str()); + assert(address.has_value()); + assert(std::holds_alternative(address.value())); + CScriptID scriptID = std::get(address.value()); CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return script; } diff --git a/src/consensus/params.cpp b/src/consensus/params.cpp index 58528b7b498..f1641e7aa0b 100644 --- a/src/consensus/params.cpp +++ b/src/consensus/params.cpp @@ -9,6 +9,7 @@ #include