Merge pull request #5431 from LarryRuane/2021-12-test-getblocktemplate-submitblock

test: Use result of getblocktemplate to submitblock
This commit is contained in:
Sasha 2021-12-22 16:06:42 -08:00 committed by GitHub
commit bc6e565bcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 34 deletions

View File

@ -3,6 +3,8 @@
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php . # 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.test_framework import BitcoinTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
@ -10,13 +12,24 @@ from test_framework.util import (
CANOPY_BRANCH_ID, CANOPY_BRANCH_ID,
HEARTWOOD_BRANCH_ID, HEARTWOOD_BRANCH_ID,
NU5_BRANCH_ID, NU5_BRANCH_ID,
get_coinbase_address,
hex_str_to_bytes,
nuparams, nuparams,
start_nodes, 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): class GetBlockTemplateTest(BitcoinTestFramework):
''' '''
Test getblocktemplate. Test getblocktemplate, ensure that a block created from its result
can be submitted and accepted.
''' '''
def __init__(self): def __init__(self):
@ -25,7 +38,8 @@ class GetBlockTemplateTest(BitcoinTestFramework):
self.setup_clean_chain = True self.setup_clean_chain = True
def setup_network(self, split=False): def setup_network(self, split=False):
args = [nuparams(BLOSSOM_BRANCH_ID, 1), args = [
nuparams(BLOSSOM_BRANCH_ID, 1),
nuparams(HEARTWOOD_BRANCH_ID, 1), nuparams(HEARTWOOD_BRANCH_ID, 1),
nuparams(CANOPY_BRANCH_ID, 1), nuparams(CANOPY_BRANCH_ID, 1),
nuparams(NU5_BRANCH_ID, 1), nuparams(NU5_BRANCH_ID, 1),
@ -36,40 +50,56 @@ class GetBlockTemplateTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
node = self.nodes[0] node = self.nodes[0]
node.generate(1) # Mine a block to leave initial block download print("Generating blocks")
node.generate(110)
# Test 1: Default to coinbasetxn print("Add transactions to the mempool so they will be in the template")
tmpl = node.getblocktemplate() # This part of the test should be improved, submit some V4 transactions
assert('coinbasetxn' in tmpl) # and varying combinations of shielded and transparent
assert('coinbasevalue' not in tmpl) 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)
# Test 2: Get coinbasetxn if requested # submit a tx with a transparent output
tmpl = node.getblocktemplate({'capabilities': ['coinbasetxn']}) outputs = {node.getnewaddress():0.2}
assert('coinbasetxn' in tmpl) node.sendmany('', outputs)
assert('coinbasevalue' not in tmpl)
# Test 3: coinbasevalue not supported if requested print("Getting block template")
tmpl = node.getblocktemplate({'capabilities': ['coinbasevalue']}) gbt = node.getblocktemplate()
assert('coinbasetxn' in tmpl)
assert('coinbasevalue' not in tmpl)
# Test 4: coinbasevalue not supported if both requested prevhash = int(gbt['previousblockhash'], 16)
tmpl = node.getblocktemplate({'capabilities': ['coinbasetxn', 'coinbasevalue']}) blockcommitmentshash = int(gbt['defaultroots']['blockcommitmentshash'], 16)
assert('coinbasetxn' in tmpl) nTime = gbt['mintime']
assert('coinbasevalue' not in tmpl) nBits = int(gbt['bits'], 16)
# Test 5: General checks f = BytesIO(hex_str_to_bytes(gbt['coinbasetxn']['data']))
tmpl = node.getblocktemplate() coinbase = CTransaction()
assert_equal(16, len(tmpl['noncerange'])) coinbase.deserialize(f)
# should be proposing height 2, since current tip is height 1
assert_equal(2, tmpl['height'])
# Test 6: coinbasetxn checks print("Creating_block from template (simulating a miner)")
assert(tmpl['coinbasetxn']['required']) block = create_block(prevhash, coinbase, nTime, nBits, blockcommitmentshash)
# 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')
print("Submitting block")
node.submitblock(block)
# Test 7: blockcommitmentshash checks
assert('blockcommitmentshash' in tmpl)
assert('00' * 32 != tmpl['finalsaplingroothash'])
if __name__ == '__main__': if __name__ == '__main__':
GetBlockTemplateTest().main() GetBlockTemplateTest().main()

View File

@ -660,7 +660,7 @@ class OutputDescription(object):
self.encCiphertext = f.read(580) self.encCiphertext = f.read(580)
self.outCiphertext = f.read(80) self.outCiphertext = f.read(80)
self.zkproof = Groth16Proof() self.zkproof = Groth16Proof()
self.zkproof.deserialize() self.zkproof.deserialize(f)
def serialize(self): def serialize(self):
r = b"" r = b""
@ -971,6 +971,8 @@ class CTransaction(object):
self.nLockTime = 0 self.nLockTime = 0
self.nExpiryHeight = 0 self.nExpiryHeight = 0
self.valueBalance = 0 self.valueBalance = 0
self.saplingBundle = SaplingBundle()
self.orchardBundle = OrchardBundle()
self.shieldedSpends = [] self.shieldedSpends = []
self.shieldedOutputs = [] self.shieldedOutputs = []
self.vJoinSplit = [] self.vJoinSplit = []
@ -988,6 +990,8 @@ class CTransaction(object):
self.nLockTime = tx.nLockTime self.nLockTime = tx.nLockTime
self.nExpiryHeight = tx.nExpiryHeight self.nExpiryHeight = tx.nExpiryHeight
self.valueBalance = tx.valueBalance self.valueBalance = tx.valueBalance
self.saplingBundle = copy.deepcopy(tx.saplingBundle)
self.orchardBundle = copy.deepcopy(tx.orchardBundle)
self.shieldedSpends = copy.deepcopy(tx.shieldedSpends) self.shieldedSpends = copy.deepcopy(tx.shieldedSpends)
self.shieldedOutputs = copy.deepcopy(tx.shieldedOutputs) self.shieldedOutputs = copy.deepcopy(tx.shieldedOutputs)
self.vJoinSplit = copy.deepcopy(tx.vJoinSplit) self.vJoinSplit = copy.deepcopy(tx.vJoinSplit)
@ -1075,6 +1079,7 @@ class CTransaction(object):
# Common transaction fields # Common transaction fields
r += struct.pack("<I", header) r += struct.pack("<I", header)
r += struct.pack("<I", self.nVersionGroupId)
r += struct.pack("<I", self.nConsensusBranchId) r += struct.pack("<I", self.nConsensusBranchId)
r += struct.pack("<I", self.nLockTime) r += struct.pack("<I", self.nLockTime)
r += struct.pack("<I", self.nExpiryHeight) r += struct.pack("<I", self.nExpiryHeight)

View File

@ -52,7 +52,7 @@ def sapling_digest(saplingBundle):
if len(saplingBundle.spends) + len(saplingBundle.outputs) > 0: if len(saplingBundle.spends) + len(saplingBundle.outputs) > 0:
digest.update(sapling_spends_digest(saplingBundle)) digest.update(sapling_spends_digest(saplingBundle))
digest.update(sapling_outputs_digest(saplingBundle)) digest.update(sapling_outputs_digest(saplingBundle))
digest.update(struct.pack('<Q', saplingBundle.valueBalance)) digest.update(struct.pack('<q', saplingBundle.valueBalance))
return digest.digest() return digest.digest()
@ -126,7 +126,7 @@ def sapling_outputs_noncompact_digest(saplingBundle):
for desc in saplingBundle.outputs: for desc in saplingBundle.outputs:
digest.update(ser_uint256(desc.cv)) digest.update(ser_uint256(desc.cv))
digest.update(desc.encCiphertext[564:]) digest.update(desc.encCiphertext[564:])
digest.update(desc.outCipherText) digest.update(desc.outCiphertext)
return digest.digest() return digest.digest()
# Orchard # Orchard
@ -139,7 +139,7 @@ def orchard_digest(orchardBundle):
digest.update(orchard_actions_memos_digest(orchardBundle)) digest.update(orchard_actions_memos_digest(orchardBundle))
digest.update(orchard_actions_noncompact_digest(orchardBundle)) digest.update(orchard_actions_noncompact_digest(orchardBundle))
digest.update(struct.pack('<B', orchardBundle.flags())) digest.update(struct.pack('<B', orchardBundle.flags()))
digest.update(struct.pack('<Q', orchardBundle.valueBalance)) digest.update(struct.pack('<q', orchardBundle.valueBalance))
digest.update(bytes(orchardBundle.anchor)) digest.update(bytes(orchardBundle.anchor))
return digest.digest() return digest.digest()

View File

@ -449,9 +449,15 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
"{\n" "{\n"
" \"version\" : n, (numeric) The block version\n" " \"version\" : n, (numeric) The block version\n"
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\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" " \"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" " \"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" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
" {\n" " {\n"
" \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"