Changeset View
Changeset View
Standalone View
Standalone View
test/functional/utxocommit.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
File Mode | null | 100755 |
#!/usr/bin/env python3 | |||||
# Copyright (c) 2015-2016 The Bitcoin developers | |||||
# Distributed under the MIT software license, see the accompanying | |||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | |||||
# Test the UTXO commitment with different ways of building it | |||||
# | |||||
from test_framework.mininode import (CBlockHeader, | |||||
COutPoint, | |||||
CTxIn, | |||||
NetworkThread, | |||||
NodeConn, | |||||
NodeConnCB, | |||||
msg_block, | |||||
msg_headers) | |||||
from test_framework.test_framework import BitcoinTestFramework | |||||
from test_framework.util import * | |||||
from test_framework.blocktools import * | |||||
NO_COMMITMENT = '0000000000000000000000000000000000000000000000000000000000000000' | |||||
class UtxoCommitTest(BitcoinTestFramework): | |||||
def set_test_params(self): | |||||
self.setup_clean_chain = True | |||||
self.num_nodes = 5 | |||||
"Setup network starts one node and one node_conn to it" | |||||
def setup_network(self): | |||||
self.add_nodes(self.num_nodes) | |||||
self.start_node(0) | |||||
self.node = self.nodes[0] | |||||
self.setup_chain() | |||||
self.node_conn = NodeConnCB() | |||||
self.node_conn.add_connection( | |||||
NodeConn('127.0.0.1', p2p_port(0), self.node, self.node_conn)) | |||||
NetworkThread().start() | |||||
self.node_conn.wait_for_verack() | |||||
self.nodenumber = 0 | |||||
"Start a fresh node and make a new connection from node_conn" | |||||
def next_node(self, extra_args=None): | |||||
self.nodenumber += 1 | |||||
self.start_node(self.nodenumber, extra_args) | |||||
self.node = self.nodes[self.nodenumber] | |||||
self.node_conn = NodeConnCB() | |||||
self.node_conn.add_connection( | |||||
NodeConn('127.0.0.1', p2p_port(self.nodenumber), self.node, self.node_conn)) | |||||
self.node_conn.wait_for_verack() | |||||
"Create a set of blocks to be added to nodes in various ways" | |||||
def setup_blocks(self): | |||||
# Include one unused block to ensure height==index | |||||
blocks = [CBlock()] | |||||
tip = int("0x" + self.nodes[0].getbestblockhash(), 0) | |||||
block_time = int(time.time()) + 1 | |||||
# Add 100 blocks with a anyonecanspend coinbase | |||||
for height in range(1, 101): | |||||
block = create_block(tip, create_coinbase(height), block_time) | |||||
block.solve() | |||||
tip = block.sha256 | |||||
block_time += 1 | |||||
blocks.append(block) | |||||
alttime = block_time | |||||
# Add 100 blocks that spend earlier coinbases | |||||
# to include UTXO removals | |||||
for height in range(101, 201): | |||||
block = create_block(tip, create_coinbase(height), block_time) | |||||
tx = create_transaction( | |||||
blocks[height-100].vtx[0], 0, b'\x51', 50 * COIN) | |||||
block.vtx.append(tx) | |||||
block.hashMerkleRoot = block.calc_merkle_root() | |||||
block.rehash() | |||||
block.solve() | |||||
tip = block.sha256 | |||||
block_time += 1 | |||||
blocks.append(block) | |||||
# Add 2100 blocks to allow assumevalid to mature | |||||
for height in range(201, 2301): | |||||
block = create_block(tip, create_coinbase(height), block_time) | |||||
block.nVersion = 4 | |||||
block.solve() | |||||
tip = block.sha256 | |||||
block_time += 1 | |||||
blocks.append(block) | |||||
# Alternative chain for reorgs, differs from 151 onwards | |||||
altblocks = blocks[0:151] | |||||
block_time = alttime | |||||
tip = blocks[150].sha256 | |||||
for height in range(151, 201): | |||||
block = create_block(tip, create_coinbase(height), block_time) | |||||
# amount=49 instead of 50 | |||||
tx = create_transaction( | |||||
blocks[height - 100].vtx[0], 0, b'\x51', 49 * COIN) | |||||
block.vtx.append(tx) | |||||
block.hashMerkleRoot = block.calc_merkle_root() | |||||
block.rehash() | |||||
block.solve() | |||||
tip = block.sha256 | |||||
block_time += 1 | |||||
altblocks.append(block) | |||||
self.blocks = blocks | |||||
self.altblocks = altblocks | |||||
def send_headers(self, blocks): | |||||
headers_message = msg_headers() | |||||
headers_message.headers = [CBlockHeader(b) for b in blocks] | |||||
self.node_conn.send_message(headers_message) | |||||
self.node_conn.sync_with_ping(20) | |||||
def send_blocks(self, blocks): | |||||
for b in blocks: | |||||
self.node_conn.send_message(msg_block(b)) | |||||
self.node_conn.sync_with_ping(20) | |||||
# Check commitment | |||||
txoutsetinfo = self.node.gettxoutsetinfo() | |||||
self.current_commitment = txoutsetinfo['commitment_calculated'] | |||||
self.is_maintaining = txoutsetinfo['commitment'] != NO_COMMITMENT | |||||
if self.is_maintaining: | |||||
assert_equal(self.current_commitment, txoutsetinfo['commitment']) | |||||
def run_test(self): | |||||
self.setup_blocks() | |||||
self.log.info("Node 1: Add blocks and record commitment") | |||||
self.send_headers(self.blocks[1:201]) | |||||
self.commitments = [0] | |||||
for b in self.blocks[1:201]: | |||||
self.node_conn.send_message(msg_block(b)) | |||||
self.node_conn.sync_with_ping(2) | |||||
self.commitments.append(self.node.gettxoutsetinfo()['commitment']) | |||||
self.log.info("Last commitment: %s", self.commitments[-1]) | |||||
# Check if loaded as intended | |||||
txoutsetinfo = self.node.gettxoutsetinfo() | |||||
self.log.info(txoutsetinfo) | |||||
assert_equal(txoutsetinfo['transactions'], 200) | |||||
assert_equal(txoutsetinfo['txouts'], 200) | |||||
# ** Node 2 | |||||
self.log.info( | |||||
"Node 2: assumevalid=150, send some blocks, send all headers, send remaining blocks") | |||||
self.next_node(['-assumevalid=' + hex(self.blocks[150].sha256)]) | |||||
self.send_blocks(self.blocks[1:21]) | |||||
assert_equal(self.current_commitment, self.commitments[20]) | |||||
assert(self.is_maintaining) | |||||
# sending all headers should activate assumevalid | |||||
self.send_headers(self.blocks[1:2001]) | |||||
self.send_headers(self.blocks[2001:]) | |||||
self.send_blocks(self.blocks[21:41]) | |||||
assert_equal(self.current_commitment, self.commitments[40]) | |||||
assert(not self.is_maintaining) | |||||
self.send_blocks(self.blocks[41:151]) | |||||
assert_equal(self.current_commitment, self.commitments[150]) | |||||
assert (not self.is_maintaining) | |||||
# expect switch to assumevalid=false | |||||
self.send_blocks(self.blocks[151:152]) | |||||
assert_equal(self.current_commitment, self.commitments[151]) | |||||
assert(self.is_maintaining) | |||||
self.send_blocks(self.blocks[152:201]) | |||||
assert_equal(self.current_commitment, self.commitments[200]) | |||||
assert(not (self.current_commitment == self.commitments[199])) | |||||
assert(self.is_maintaining) | |||||
# ** Node 3 | |||||
self.log.info("Node 3: reorg after 175 blocks") | |||||
self.next_node() | |||||
self.send_blocks(self.altblocks[1:175]) | |||||
assert(not self.current_commitment == self.commitments[174]) | |||||
assert(self.is_maintaining) | |||||
# reorg | |||||
self.send_blocks(self.blocks[150:180]) | |||||
assert_equal(self.current_commitment, self.commitments[179]) | |||||
assert(self.is_maintaining) | |||||
if __name__ == '__main__': | |||||
UtxoCommitTest().main() |