From b0769e3f1d3a1050c8015a970187f1aac713790e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 23 Feb 2022 10:04:34 +0000 Subject: [PATCH] Add Orchard to default UA receiver types We also add Orchard-specific information to several RPCs in order for tests to pass: - `z_listunifiedreceivers` - `z_getbalanceforaccount` - `z_getbalanceforviewingkey` --- qa/rpc-tests/wallet_accounts.py | 30 +++++++++++++++++++++++++---- qa/rpc-tests/wallet_listreceived.py | 3 ++- src/wallet/rpcwallet.cpp | 25 ++++++++++++++++++++++-- src/wallet/wallet.cpp | 2 +- src/wallet/wallet.h | 4 ++++ src/zcash/address/unified.cpp | 2 +- 6 files changed, 57 insertions(+), 9 deletions(-) diff --git a/qa/rpc-tests/wallet_accounts.py b/qa/rpc-tests/wallet_accounts.py index 15f097d78..34a1e7fd5 100755 --- a/qa/rpc-tests/wallet_accounts.py +++ b/qa/rpc-tests/wallet_accounts.py @@ -55,7 +55,7 @@ class WalletAccountsTest(BitcoinTestFramework): # Generate the first address for account 0. addr0 = self.nodes[0].z_getaddressforaccount(0) assert_equal(addr0['account'], 0) - assert_equal(set(addr0['pools']), set(['transparent', 'sapling'])) + assert_equal(set(addr0['pools']), set(['transparent', 'sapling', 'orchard'])) ua0 = addr0['unifiedaddress'] # We pick mnemonic phrases to ensure that we can always generate the default @@ -70,16 +70,38 @@ class WalletAccountsTest(BitcoinTestFramework): 'no address at diversifier index 0', self.nodes[0].z_getaddressforaccount, 0, [], 0) + # The second address for account 0 is different to the first address. + addr0_2 = self.nodes[0].z_getaddressforaccount(0) + assert_equal(addr0_2['account'], 0) + assert_equal(set(addr0_2['pools']), set(['transparent', 'sapling', 'orchard'])) + ua0_2 = addr0_2['unifiedaddress'] + assert(ua0 != ua0_2) + + # We can generate a fully-shielded address. + addr0_3 = self.nodes[0].z_getaddressforaccount(0, ['sapling', 'orchard']) + assert_equal(addr0_3['account'], 0) + assert_equal(set(addr0_3['pools']), set(['sapling', 'orchard'])) + ua0_3 = addr0_3['unifiedaddress'] + + # We can generate an address without a Sapling receiver. + addr0_4 = self.nodes[0].z_getaddressforaccount(0, ['transparent', 'orchard']) + assert_equal(addr0_4['account'], 0) + assert_equal(set(addr0_4['pools']), set(['transparent', 'orchard'])) + ua0_4 = addr0_4['unifiedaddress'] + # The first address for account 1 is different to account 0. addr1 = self.nodes[0].z_getaddressforaccount(1) assert_equal(addr1['account'], 1) - assert_equal(set(addr1['pools']), set(['transparent', 'sapling'])) + assert_equal(set(addr1['pools']), set(['transparent', 'sapling', 'orchard'])) ua1 = addr1['unifiedaddress'] assert(ua0 != ua1) # The UA contains the expected receiver kinds. - self.check_receiver_types(ua0, ['transparent', 'sapling']) - self.check_receiver_types(ua1, ['transparent', 'sapling']) + self.check_receiver_types(ua0, ['transparent', 'sapling', 'orchard']) + self.check_receiver_types(ua0_2, ['transparent', 'sapling', 'orchard']) + self.check_receiver_types(ua0_3, [ 'sapling', 'orchard']) + self.check_receiver_types(ua0_4, ['transparent', 'orchard']) + self.check_receiver_types(ua1, ['transparent', 'sapling', 'orchard']) # The balances of the accounts are all zero. self.check_balance(0, 0, ua0, {}) diff --git a/qa/rpc-tests/wallet_listreceived.py b/qa/rpc-tests/wallet_listreceived.py index fd67ebb76..ec809eec3 100755 --- a/qa/rpc-tests/wallet_listreceived.py +++ b/qa/rpc-tests/wallet_listreceived.py @@ -381,9 +381,10 @@ class ListReceivedTest (BitcoinTestFramework): r = node.z_getaddressforaccount(account) unified_addr = r['unifiedaddress'] receivers = node.z_listunifiedreceivers(unified_addr) - assert_equal(len(receivers), 2) + assert_equal(len(receivers), 3) assert 'transparent' in receivers assert 'sapling' in receivers + assert 'orchard' in receivers # Wallet contains no notes r = node.z_listreceivedbyaddress(unified_addr, 0) assert_equal(len(r), 0, "unified_addr should have received zero notes") diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index aa99bb0cb..fd8cc05df 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3119,14 +3119,16 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) receivers.insert(ReceiverType::P2PKH); } else if (p == "sapling") { receivers.insert(ReceiverType::Sapling); + } else if (p == "orchard") { + receivers.insert(ReceiverType::Orchard); } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, "pool arguments must be \"transparent\", or \"sapling\""); + throw JSONRPCError(RPC_INVALID_PARAMETER, "pool arguments must be \"transparent\", \"sapling\", or \"orchard\""); } } } if (receivers.empty()) { // Default is the best and second-best shielded pools, and the transparent pool. - receivers = {ReceiverType::P2PKH, ReceiverType::Sapling}; + receivers = {ReceiverType::P2PKH, ReceiverType::Sapling, ReceiverType::Orchard}; } std::optional j = std::nullopt; @@ -3220,6 +3222,9 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) case ReceiverType::Sapling: pools.push_back("sapling"); break; + case ReceiverType::Orchard: + pools.push_back("orchard"); + break; default: // Unreachable assert(false); @@ -3331,6 +3336,12 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); for (const auto& receiver : ua) { std::visit(match { + [&](const libzcash::OrchardRawAddress& addr) { + // Create a single-receiver UA that just contains this Orchard receiver. + UnifiedAddress singleReceiver; + singleReceiver.AddReceiver(addr); + result.pushKV("orchard", keyIO.EncodePaymentAddress(singleReceiver)); + }, [&](const libzcash::SaplingPaymentAddress& addr) { result.pushKV("sapling", keyIO.EncodePaymentAddress(addr)); }, @@ -3762,6 +3773,7 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp) CAmount transparentBalance = 0; CAmount sproutBalance = 0; CAmount saplingBalance = 0; + CAmount orchardBalance = 0; for (const auto& t : spendableInputs.utxos) { transparentBalance += t.Value(); } @@ -3771,6 +3783,9 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp) for (const auto& t : spendableInputs.saplingNoteEntries) { saplingBalance += t.note.value(); } + for (const auto& t : spendableInputs.orchardNoteMetadata) { + orchardBalance += t.GetNoteValue(); + } UniValue pools(UniValue::VOBJ); auto renderBalance = [&](std::string poolName, CAmount balance) { @@ -3783,6 +3798,7 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp) renderBalance("transparent", transparentBalance); renderBalance("sprout", sproutBalance); renderBalance("sapling", saplingBalance); + renderBalance("orchard", orchardBalance); UniValue result(UniValue::VOBJ); result.pushKV("pools", pools); @@ -3863,12 +3879,16 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp) CAmount transparentBalance = 0; CAmount saplingBalance = 0; + CAmount orchardBalance = 0; for (const auto& t : spendableInputs.utxos) { transparentBalance += t.Value(); } for (const auto& t : spendableInputs.saplingNoteEntries) { saplingBalance += t.note.value(); } + for (const auto& t : spendableInputs.orchardNoteMetadata) { + orchardBalance += t.GetNoteValue(); + } UniValue pools(UniValue::VOBJ); auto renderBalance = [&](std::string poolName, CAmount balance) { @@ -3880,6 +3900,7 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp) }; renderBalance("transparent", transparentBalance); renderBalance("sapling", saplingBalance); + renderBalance("orchard", orchardBalance); UniValue result(UniValue::VOBJ); result.pushKV("pools", pools); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 631cfced0..e03e57514 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -605,7 +605,7 @@ std::optional // when the user calls z_getaddressforaccount. auto orchardInternalFvk = orchardSk.ToFullViewingKey().ToInternalIncomingViewingKey(); if (!AddOrchardRawAddress(orchardInternalFvk, orchardInternalFvk.Address(0))) { - throw std::runtime_error("CWallet::GenerateUnifiedSpendingKeyForAccount(): Failed to add Sapling change address to the wallet."); + throw std::runtime_error("CWallet::GenerateUnifiedSpendingKeyForAccount(): Failed to add Orchard change address to the wallet."); }; auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), ufvk); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7bbdef52d..7a25a2947 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -794,6 +794,7 @@ public: std::vector utxos; std::vector sproutNoteEntries; std::vector saplingNoteEntries; + std::vector orchardNoteMetadata; /** * Selectively discard notes that are not required to obtain the desired @@ -816,6 +817,9 @@ public: for (const auto& t : saplingNoteEntries) { result += t.note.value(); } + for (const auto& t : orchardNoteMetadata) { + result += t.GetNoteValue(); + } return result; } diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp index 93dd1b34f..9fe8c131a 100644 --- a/src/zcash/address/unified.cpp +++ b/src/zcash/address/unified.cpp @@ -150,7 +150,7 @@ UnifiedAddressGenerationResult ZcashdUnifiedFullViewingKey::FindAddress( UnifiedAddressGenerationResult ZcashdUnifiedFullViewingKey::FindAddress( const diversifier_index_t& j) const { - return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling}); + return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling, ReceiverType::Orchard}); } std::optional ZcashdUnifiedFullViewingKey::GetChangeAddress(const ChangeRequest& req) const {