From 4f4ef5537bc114ee2a0a9132191541cb19262e7d Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Tue, 13 Sep 2022 08:43:40 -0600 Subject: [PATCH 1/3] Add a finalorchardroot RPC test --- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/finalorchardroot.py | 284 +++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100755 qa/rpc-tests/finalorchardroot.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 1fb6470a9..72200a696 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -57,6 +57,7 @@ BASE_SCRIPTS= [ 'wallet_listreceived.py', 'mempool_tx_expiry.py', 'finalsaplingroot.py', + 'finalorchardroot.py', 'wallet_orchard.py', 'wallet_overwintertx.py', 'wallet_persistence.py', diff --git a/qa/rpc-tests/finalorchardroot.py b/qa/rpc-tests/finalorchardroot.py new file mode 100755 index 000000000..c0957941e --- /dev/null +++ b/qa/rpc-tests/finalorchardroot.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +# Copyright (c) 2022 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 ( + NU5_BRANCH_ID, + assert_equal, + connect_nodes_bi, + get_coinbase_address, + nuparams, + start_nodes, + wait_and_assert_operationid_status, +) + +from decimal import Decimal + +SPROUT_TREE_EMPTY_ROOT = "59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7" +SAPLING_TREE_EMPTY_ROOT = "3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb" +ORCHARD_TREE_EMPTY_ROOT = "2fd8e51a03d9bbe2dd809831b1497aeb68a6e37ddf707ced4aa2d8dff13529ae" +NULL_FIELD = "0000000000000000000000000000000000000000000000000000000000000000" + +# Verify block header field 'hashFinalOrchardRoot' (returned in rpc as 'finalorchardroot') +# is updated when Orchard transactions with outputs (commitments) are mined into a block. +class FinalOrchardRootTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.cache_behavior = 'sprout' + + def setup_network(self, split=False): + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-txindex', # Avoid JSONRPC error: No information available about transaction + '-reindex', # Required due to enabling -txindex + nuparams(NU5_BRANCH_ID, 200), + '-debug', + ]] * self.num_nodes) + connect_nodes_bi(self.nodes,0,1) + self.is_network_split=False + self.sync_all() + + def run_test(self): + # Verify genesis block doesn't contain the final orchard root field. + blk = self.nodes[0].getblock("0") + assert "finalorchardroot" not in blk + treestate = self.nodes[0].z_gettreestate("0") + assert_equal(treestate["height"], 0) + assert_equal(treestate["hash"], self.nodes[0].getblockhash(0)) + + assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT) + assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000") + assert "skipHash" not in treestate["sprout"] + + assert_equal(treestate["sapling"]["commitments"]["finalRoot"], NULL_FIELD) + # There is no sapling state tree yet, and trying to find it in an earlier + # block won't succeed (we're at genesis block), so skipHash is absent. + assert "finalState" not in treestate["sapling"] + assert "skipHash" not in treestate["sapling"] + + assert_equal(treestate["orchard"]["commitments"]["finalRoot"], NULL_FIELD) + # There is no orchard state tree yet, and trying to find it in an earlier + # block won't succeed (we're at genesis block), so skipHash is absent. + assert "finalState" not in treestate["orchard"] + assert "skipHash" not in treestate["orchard"] + + # Verify no generated blocks before NU5 contain the empty root of the Orchard tree. + blockcount = self.nodes[0].getblockcount() + for height in range(1, blockcount + 1): + blk = self.nodes[0].getblock(str(height)) + assert "finalorchardroot" not in blk + + treestate = self.nodes[0].z_gettreestate(str(height)) + assert_equal(treestate["height"], height) + assert_equal(treestate["hash"], self.nodes[0].getblockhash(height)) + + if height < 100: + assert "skipHash" not in treestate["sprout"] + assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT) + assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000") + + assert "skipHash" not in treestate["sapling"] + assert_equal(treestate["sapling"]["commitments"]["finalRoot"], SAPLING_TREE_EMPTY_ROOT) + assert_equal(treestate["sapling"]["commitments"]["finalState"], "000000") + + assert "skipHash" not in treestate["orchard"] + assert_equal(treestate["orchard"]["commitments"]["finalRoot"], NULL_FIELD) + assert "finalState" not in treestate["orchard"] + + self.sync_all() + self.nodes[0].generate(11) + self.sync_all() + + # post-NU5 + + for height in range(200, 211): + blk = self.nodes[0].getblock(str(height)) + assert_equal(blk["finalorchardroot"], ORCHARD_TREE_EMPTY_ROOT) + + treestate = self.nodes[0].z_gettreestate(str(height)) + assert_equal(treestate["height"], height) + assert_equal(treestate["hash"], self.nodes[0].getblockhash(height)) + + if height < 100: + assert "skipHash" not in treestate["sprout"] + assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT) + assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000") + + assert "skipHash" not in treestate["sapling"] + assert_equal(treestate["sapling"]["commitments"]["finalRoot"], SAPLING_TREE_EMPTY_ROOT) + assert_equal(treestate["sapling"]["commitments"]["finalState"], "000000") + + assert "skipHash" not in treestate["orchard"] + assert_equal(treestate["orchard"]["commitments"]["finalRoot"], ORCHARD_TREE_EMPTY_ROOT) + assert_equal(treestate["orchard"]["commitments"]["finalState"], "000000") + + + # Node 0 shields some funds + taddr0 = get_coinbase_address(self.nodes[0]) + acct0 = self.nodes[0].z_getnewaccount()['account'] + addrRes0 = self.nodes[0].z_getaddressforaccount(acct0, ['orchard']) + assert_equal(acct0, addrRes0['account']) + assert_equal(addrRes0['receiver_types'], ['orchard']) + orchardAddr0 = addrRes0['address'] + recipients = [] + recipients.append({"address": orchardAddr0, "amount": Decimal('10')}) + myopid = self.nodes[0].z_sendmany(taddr0, recipients, 1, 0, 'AllowRevealedSenders') + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + # Verify the final Orchard root has changed + blk = self.nodes[0].getblock("211") + root = blk["finalorchardroot"] + assert root is not ORCHARD_TREE_EMPTY_ROOT + assert root is not NULL_FIELD + + # Verify there is a Orchard output description (its commitment was added to tree) + result = self.nodes[0].getrawtransaction(mytxid, 1) + assert_equal(len(result["orchard"]["actions"]), 2) + + # Since there is a now orchard shielded input in the blockchain, + # the orchard values should have changed + new_treestate = self.nodes[0].z_gettreestate(str(-1)) + assert_equal(new_treestate["orchard"]["commitments"]["finalRoot"], root) + assert_equal(new_treestate["sprout"], treestate["sprout"]) + assert new_treestate["orchard"]["commitments"]["finalRoot"] != treestate["orchard"]["commitments"]["finalRoot"] + assert new_treestate["orchard"]["commitments"]["finalState"] != treestate["orchard"]["commitments"]["finalState"] + assert_equal(len(new_treestate["orchard"]["commitments"]["finalRoot"]), 64) + assert_equal(len(new_treestate["orchard"]["commitments"]["finalState"]), 196) + treestate = new_treestate + + # Mine an empty block and verify the final Orchard root does not change + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + assert_equal(root, self.nodes[0].getblock("212")["finalorchardroot"]) + + # Mine a block with a transparent tx and verify the final Orchard root does not change + taddr1 = self.nodes[1].getnewaddress() + self.nodes[0].sendtoaddress(taddr1, Decimal("1.23")) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(len(self.nodes[0].getblock("213")["tx"]), 2) + assert_equal(self.nodes[1].z_getbalance(taddr1), Decimal("1.23")) + assert_equal(root, self.nodes[0].getblock("213")["finalorchardroot"]) + + # Mine a block with a Sprout shielded tx and verify the final Orchard root does not change + zaddr0 = self.nodes[0].listaddresses()[0]['sprout']['addresses'][0] + assert_equal(self.nodes[0].z_getbalance(zaddr0), Decimal('50')) + recipients = [{"address": taddr0, "amount": Decimal('12.34')}] + opid = self.nodes[0].z_sendmany(zaddr0, recipients, 1, 0, 'AllowRevealedRecipients') + wait_and_assert_operationid_status(self.nodes[0], opid) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(len(self.nodes[0].getblock("214")["tx"]), 2) + assert_equal(self.nodes[0].z_getbalance(zaddr0), Decimal("37.66")) + assert_equal(root, self.nodes[0].getblock("214")["finalorchardroot"]) + + new_treestate = self.nodes[0].z_gettreestate(str(-1)) + assert_equal(new_treestate["orchard"]["commitments"]["finalRoot"], root) + assert_equal(new_treestate["orchard"], treestate["orchard"]) + assert new_treestate["sprout"]["commitments"]["finalRoot"] != treestate["sprout"]["commitments"]["finalRoot"] + assert new_treestate["sprout"]["commitments"]["finalState"] != treestate["sprout"]["commitments"]["finalState"] + assert_equal(len(new_treestate["sprout"]["commitments"]["finalRoot"]), 64) + assert_equal(len(new_treestate["sprout"]["commitments"]["finalState"]), 266) + treestate = new_treestate + + # Mine a block with a Sapling shielded tx and verify the final Orchard root does not change + saplingAddr1 = self.nodes[1].z_getnewaddress("sapling") + recipients = [{"address": saplingAddr1, "amount": Decimal('2.34')}] + myopid = self.nodes[0].z_sendmany(zaddr0, recipients, 1, 0, 'AllowRevealedAmounts') + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(len(self.nodes[0].getblock("215")["tx"]), 2) + assert_equal(self.nodes[1].z_getbalance(saplingAddr1), Decimal("2.34")) + assert_equal(root, self.nodes[0].getblock("215")["finalorchardroot"]) + + new_treestate = self.nodes[0].z_gettreestate(str(-1)) + assert_equal(new_treestate["orchard"]["commitments"]["finalRoot"], root) + assert_equal(new_treestate["orchard"], treestate["orchard"]) + assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"] + assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"] + assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64) + assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 70) + treestate = new_treestate + + # Mine a block with an Orchard shielded recipient and verify the final Orchard root changes + acct1 = self.nodes[1].z_getnewaccount()['account'] + addrRes1 = self.nodes[1].z_getaddressforaccount(acct1, ['orchard']) + assert_equal(acct1, addrRes1['account']) + assert_equal(addrRes1['receiver_types'], ['orchard']) + orchardAddr1 = addrRes1['address'] + recipients = [{"address": orchardAddr1, "amount": Decimal('2.34')}] + myopid = self.nodes[0].z_sendmany(orchardAddr0, recipients, 1, 0) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(len(self.nodes[0].getblock("216")["tx"]), 2) + assert_equal(self.nodes[1].z_getbalance(orchardAddr1), Decimal("2.34")) + assert root is not self.nodes[0].getblock("216")["finalorchardroot"] + + # Verify there is a Orchard output description (its commitment was added to tree) + result = self.nodes[0].getrawtransaction(mytxid, 1) + assert_equal(len(result["orchard"]["actions"]), 2) # there is Orchard shielded change + + new_treestate = self.nodes[0].z_gettreestate(str(-1)) + assert_equal(new_treestate["sprout"], treestate["sprout"]) + assert_equal(new_treestate["sapling"], treestate["sapling"]) + assert new_treestate["orchard"]["commitments"]["finalRoot"] != treestate["orchard"]["commitments"]["finalRoot"] + assert new_treestate["orchard"]["commitments"]["finalState"] != treestate["orchard"]["commitments"]["finalState"] + assert_equal(len(new_treestate["orchard"]["commitments"]["finalRoot"]), 64) + assert_equal(len(new_treestate["orchard"]["commitments"]["finalState"]), 260) + treestate = new_treestate + + # Mine a block with an Orchard shielded sender and transparent recipient and verify the final Orchard root changes (because actions) + taddr2 = self.nodes[0].getnewaddress() + recipients = [] + recipients.append({"address": taddr2, "amount": Decimal('2.34')}) + myopid = self.nodes[1].z_sendmany(orchardAddr1, recipients, 1, 0, 'AllowRevealedRecipients') + mytxid = wait_and_assert_operationid_status(self.nodes[1], myopid) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(len(self.nodes[0].getblock("217")["tx"]), 2) + assert_equal(self.nodes[0].z_getbalance(taddr2), Decimal("2.34")) + assert root is not self.nodes[0].getblock("217")["finalorchardroot"] + + # Verify there is a Orchard output description (its commitment was added to tree) + result = self.nodes[0].getrawtransaction(mytxid, 1) + assert_equal(len(result["orchard"]["actions"]), 2) # there is Orchard shielded change + + new_treestate = self.nodes[0].z_gettreestate(str(-1)) + assert_equal(new_treestate["sprout"], treestate["sprout"]) + assert_equal(new_treestate["sapling"], treestate["sapling"]) + assert new_treestate["orchard"]["commitments"]["finalRoot"] != treestate["orchard"]["commitments"]["finalRoot"] + assert new_treestate["orchard"]["commitments"]["finalState"] != treestate["orchard"]["commitments"]["finalState"] + assert_equal(len(new_treestate["orchard"]["commitments"]["finalRoot"]), 64) + assert_equal(len(new_treestate["orchard"]["commitments"]["finalState"]), 260) + + pass + + +if __name__ == '__main__': + FinalOrchardRootTest().main() From bdde47fb5d6d8dfbdbbc93c4e1168101ee65cbc2 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 9 Sep 2022 11:48:37 -0600 Subject: [PATCH 2/3] Fix finalorchardroot serialization Previously, `finalorchardroot` and `orchard.anchor` were serialized differently in the RPC API, which made it difficult to correlate them. This changes the serialization for `finalorchardroot` to match `orchard.anchor`. --- doc/release-notes.md | 10 ++++++++++ qa/rpc-tests/feature_zip221.py | 2 +- qa/rpc-tests/finalorchardroot.py | 2 +- qa/rpc-tests/finalsaplingroot.py | 2 +- src/rpc/blockchain.cpp | 19 ++++++++++++++----- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 62556d98a..5f9cc31e6 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -19,3 +19,13 @@ has a memory limit of 100 MiB. - (counter) `zcashd.wallet.batchscanner.outputs.scanned` - (gauge) `zcashd.wallet.batchscanner.size.transactions` - (gauge) `zcashd.wallet.batchscanner.usage.bytes` + +RPC Interface +------------- + +- The `finalorchardroot` field in the `getblock` result and the + `orchard.commitments.finalRoot` field in the `z_gettreestate` result have + been changed to match the byte ordering used for the `orchard.anchor` + field in the `getrawtransaction` result. These previously produced different + hash values from the `orchard.anchor` field due to having been byte-flipped + in their internal representation in zcashd. diff --git a/qa/rpc-tests/feature_zip221.py b/qa/rpc-tests/feature_zip221.py index 209c5ffd7..a788655a3 100755 --- a/qa/rpc-tests/feature_zip221.py +++ b/qa/rpc-tests/feature_zip221.py @@ -57,7 +57,7 @@ class Zip221Test(BitcoinTestFramework): if height >= 35: orchard_root = hex_str_to_bytes( - self.nodes[0].getblock(str(height))["finalorchardroot"])[::-1] + self.nodes[0].getblock(str(height))["finalorchardroot"]) v2_data = (orchard_root, 0) else: v2_data = None diff --git a/qa/rpc-tests/finalorchardroot.py b/qa/rpc-tests/finalorchardroot.py index c0957941e..fcc110bb2 100755 --- a/qa/rpc-tests/finalorchardroot.py +++ b/qa/rpc-tests/finalorchardroot.py @@ -19,7 +19,7 @@ from decimal import Decimal SPROUT_TREE_EMPTY_ROOT = "59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7" SAPLING_TREE_EMPTY_ROOT = "3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb" -ORCHARD_TREE_EMPTY_ROOT = "2fd8e51a03d9bbe2dd809831b1497aeb68a6e37ddf707ced4aa2d8dff13529ae" +ORCHARD_TREE_EMPTY_ROOT = "ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f" NULL_FIELD = "0000000000000000000000000000000000000000000000000000000000000000" # Verify block header field 'hashFinalOrchardRoot' (returned in rpc as 'finalorchardroot') diff --git a/qa/rpc-tests/finalsaplingroot.py b/qa/rpc-tests/finalsaplingroot.py index d656d8f45..3e02293c7 100755 --- a/qa/rpc-tests/finalsaplingroot.py +++ b/qa/rpc-tests/finalsaplingroot.py @@ -19,7 +19,7 @@ from decimal import Decimal SPROUT_TREE_EMPTY_ROOT = "59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7" SAPLING_TREE_EMPTY_ROOT = "3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb" -ORCHARD_TREE_EMPTY_ROOT = "2fd8e51a03d9bbe2dd809831b1497aeb68a6e37ddf707ced4aa2d8dff13529ae" +ORCHARD_TREE_EMPTY_ROOT = "ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f" NULL_FIELD = "0000000000000000000000000000000000000000000000000000000000000000" # Verify block header field 'hashFinalSaplingRoot' (returned in rpc as 'finalsaplingroot') diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 3952fe6b2..f9646de4c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -241,7 +241,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.pushKV("authdataroot", blockindex->hashAuthDataRoot.GetHex()); result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex()); if (nu5Active) { - result.pushKV("finalorchardroot", blockindex->hashFinalOrchardRoot.GetHex()); + auto finalOrchardRootBytes = blockindex->hashFinalOrchardRoot; + result.pushKV("finalorchardroot", HexStr(finalOrchardRootBytes.begin(), finalOrchardRootBytes.end())); } result.pushKV("chainhistoryroot", blockindex->hashChainHistoryRoot.GetHex()); UniValue txs(UniValue::VARR); @@ -716,9 +717,13 @@ UniValue getblock(const UniValue& params, bool fHelp) " \"version\" : n, (numeric) The block version\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"finalsaplingroot\" : \"xxxx\", (string) The root of the Sapling commitment tree after applying this block\n" - " \"finalorchardroot\" : \"xxxx\", (string) The root of the Orchard commitment tree after applying this block.\n" - " Omitted for blocks prior to NU5 activation. This will be the null\n" - " hash if this block has never been connected to a main chain.\n" + " \"finalorchardroot\" : \"xxxx\", (string, optional) The root of the Orchard commitment tree after\n" + " applying this block. Omitted for blocks prior to NU5 activation. This\n" + " will be the null hash if this block has never been connected to a\n" + " main chain.\n" + " NB: The serialized representation of this field returned by this method\n" + " was byte-flipped relative to its representation in the `getrawtransaction`\n" + " output in prior releases up to v5.2.0. This has now been rectified.\n" " \"tx\" : [ (array of string) The transaction ids\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" @@ -1247,6 +1252,9 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp) " \"skipHash\": \"hash\", (string) hash of most recent block with more information\n" " \"commitments\": {\n" " \"finalRoot\": \"hex\", (string)\n" + " NB: The serialized representation of this field returned by this method\n" + " was byte-flipped relative to its representation in the `getrawtransaction`\n" + " output in prior releases up to v5.2.0. This has now been rectified.\n" " \"finalState\": \"hex\" (string)\n" " }\n" " },\n" @@ -1345,7 +1353,8 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp) if (nu5_activation_height.has_value()) { UniValue orchard_result(UniValue::VOBJ); UniValue orchard_commitments(UniValue::VOBJ); - orchard_commitments.pushKV("finalRoot", pindex->hashFinalOrchardRoot.GetHex()); + auto finalOrchardRootBytes = pindex->hashFinalOrchardRoot; + orchard_commitments.pushKV("finalRoot", HexStr(finalOrchardRootBytes.begin(), finalOrchardRootBytes.end())); bool need_skiphash = false; OrchardMerkleFrontier tree; if (pcoinsTip->GetOrchardAnchorAt(pindex->hashFinalOrchardRoot, tree)) { From 5514542a0a8e1ad62637fd4d502bfd1cce1ae69a Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 23 Sep 2022 18:39:00 -0600 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Daira Hopwood --- qa/rpc-tests/finalorchardroot.py | 1 - src/rpc/blockchain.cpp | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/finalorchardroot.py b/qa/rpc-tests/finalorchardroot.py index fcc110bb2..3ec7b8ded 100755 --- a/qa/rpc-tests/finalorchardroot.py +++ b/qa/rpc-tests/finalorchardroot.py @@ -277,7 +277,6 @@ class FinalOrchardRootTest(BitcoinTestFramework): assert_equal(len(new_treestate["orchard"]["commitments"]["finalRoot"]), 64) assert_equal(len(new_treestate["orchard"]["commitments"]["finalState"]), 260) - pass if __name__ == '__main__': diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f9646de4c..a929d1c19 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -723,7 +723,8 @@ UniValue getblock(const UniValue& params, bool fHelp) " main chain.\n" " NB: The serialized representation of this field returned by this method\n" " was byte-flipped relative to its representation in the `getrawtransaction`\n" - " output in prior releases up to v5.2.0. This has now been rectified.\n" + " output in prior releases up to and including v5.2.0. This has now been\n" + " rectified.\n" " \"tx\" : [ (array of string) The transaction ids\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" @@ -1254,7 +1255,8 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp) " \"finalRoot\": \"hex\", (string)\n" " NB: The serialized representation of this field returned by this method\n" " was byte-flipped relative to its representation in the `getrawtransaction`\n" - " output in prior releases up to v5.2.0. This has now been rectified.\n" + " output in prior releases up to and including v5.2.0. This has now been\n" + " rectified.\n" " \"finalState\": \"hex\" (string)\n" " }\n" " },\n"