def mine_block(self, node, vtx=[], miner_address=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None): bt = node.getblocktemplate() height = bt['height'] tip_hash = bt['previousblockhash'] tip_block = node.getblock(tip_hash) coinbasevalue = bt['coinbasevalue'] if miner_address is None: miner_address = node.getnewaddress() if mn_payee is None: if isinstance(bt['masternode'], list): mn_payee = bt['masternode'][0]['payee'] else: mn_payee = bt['masternode']['payee'] # we can't take the masternode payee amount from the template here as we might have additional fees in vtx # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't # include the template's transactions bt_fees = 0 for tx in bt['transactions']: bt_fees += tx['fee'] new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value # fix fees coinbasevalue -= bt_fees coinbasevalue += new_fees if mn_amount is None: mn_amount = get_masternode_payment(height, coinbasevalue) miner_amount = coinbasevalue - mn_amount outputs = {miner_address: str(Decimal(miner_amount) / COIN)} if mn_amount > 0: outputs[mn_payee] = str(Decimal(mn_amount) / COIN) coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) coinbase.vin = create_coinbase(height).vin # We can't really use this one as it would result in invalid merkle roots for masternode lists if len(bt['coinbase_payload']) != 0: cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) if use_mnmerkleroot_from_tip: if 'cbTx' in tip_block: cbtx.merkleRootMNList = int( tip_block['cbTx']['merkleRootMNList'], 16) else: cbtx.merkleRootMNList = 0 coinbase.nVersion = 3 coinbase.nType = 5 # CbTx coinbase.vExtraPayload = cbtx.serialize() coinbase.calc_sha256() block = create_block(int(tip_hash, 16), coinbase) block.vtx += vtx # Add quorum commitments from template for tx in bt['transactions']: tx2 = FromHex(CTransaction(), tx['data']) if tx2.nType == 6: block.vtx.append(tx2) block.hashMerkleRoot = block.calc_merkle_root() block.solve() result = node.submitblock(ToHex(block)) if expected_error is not None and result != expected_error: raise AssertionError( 'mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result)) elif expected_error is None and result is not None: raise AssertionError('submitblock returned %s' % (result))
def create_block(self, node, vtx=[]): bt = node.getblocktemplate() height = bt['height'] tip_hash = bt['previousblockhash'] coinbasevalue = bt['coinbasevalue'] miner_address = node.getnewaddress() mn_payee = bt['masternode'][0]['payee'] # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't # include the template's transactions bt_fees = 0 for tx in bt['transactions']: bt_fees += tx['fee'] new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value # fix fees coinbasevalue -= bt_fees coinbasevalue += new_fees mn_amount = get_masternode_payment(height, coinbasevalue) miner_amount = coinbasevalue - mn_amount outputs = {miner_address: str(Decimal(miner_amount) / COIN)} if mn_amount > 0: outputs[mn_payee] = str(Decimal(mn_amount) / COIN) coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) coinbase.vin = create_coinbase(height).vin # We can't really use this one as it would result in invalid merkle roots for masternode lists if len(bt['coinbase_payload']) != 0: cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) coinbase.nVersion = 3 coinbase.nType = 5 # CbTx coinbase.vExtraPayload = cbtx.serialize() coinbase.calc_sha256() block = create_block(int(tip_hash, 16), coinbase, nTime=bt['curtime']) block.vtx += vtx # Add quorum commitments from template for tx in bt['transactions']: tx2 = FromHex(CTransaction(), tx['data']) if tx2.nType == 6: block.vtx.append(tx2) block.hashMerkleRoot = block.calc_merkle_root() block.solve() return block
def run_test(self): self.log.info("Wait for DIP3 to activate") while get_bip9_status(self.nodes[0], 'dip0003')['status'] != 'active': self.bump_mocktime(10) self.nodes[0].generate(10) self.sync_blocks() self.nodes[0].add_p2p_connection(P2PDataStore()) network_thread_start() self.nodes[0].p2p.wait_for_verack() self.log.info("Mine all but one remaining block in the window") bi = self.nodes[0].getblockchaininfo() for i in range(498 - bi['blocks']): self.bump_mocktime(1) self.nodes[0].generate(1) self.sync_blocks() self.log.info("Initial state is DEFINED") bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 498) assert_equal(bi['bip9_softforks']['realloc']['status'], 'defined') self.log.info("Advance from DEFINED to STARTED at height = 499") self.nodes[0].generate(1) bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 499) assert_equal(bi['bip9_softforks']['realloc']['status'], 'started') assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(0)) self.signal(399, False) # 1 block short self.log.info("Still STARTED but new threshold should be lower at height = 999") bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 999) assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(1)) self.signal(398, False) # 1 block short again self.log.info("Still STARTED but new threshold should be even lower at height = 1499") bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 1499) assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(2)) pre_locked_in_blockhash = bi['bestblockhash'] self.signal(396, True) # just enough to lock in self.log.info("Advanced to LOCKED_IN at height = 1999") for i in range(49): self.bump_mocktime(10) self.nodes[0].generate(10) self.sync_blocks() self.nodes[0].generate(9) self.sync_blocks() self.log.info("Still LOCKED_IN at height = 2498") bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 2498) assert_equal(bi['bip9_softforks']['realloc']['status'], 'locked_in') self.log.info("Advance from LOCKED_IN to ACTIVE at height = 2499") self.nodes[0].generate(1) # activation bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 2499) assert_equal(bi['bip9_softforks']['realloc']['status'], 'active') assert_equal(bi['bip9_softforks']['realloc']['since'], 2500) self.log.info("Reward split should stay ~50/50 before the first superblock after activation") # This applies even if reallocation was activated right at superblock height like it does here bt = self.nodes[0].getblocktemplate() assert_equal(bt['height'], 2500) assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500)) self.nodes[0].generate(9) self.sync_blocks() bt = self.nodes[0].getblocktemplate() assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500)) assert_equal(bt['coinbasevalue'], 13748571607) assert_equal(bt['masternode'][0]['amount'], 6874285801) # 0.4999999998 self.log.info("Reallocation should kick-in with the superblock mined at height = 2010") for period in range(19): # there will be 19 adjustments, 3 superblocks long each for i in range(3): self.bump_mocktime(10) self.nodes[0].generate(10) self.sync_blocks() bt = self.nodes[0].getblocktemplate() assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500)) self.log.info("Reward split should reach ~60/40 after reallocation is done") assert_equal(bt['coinbasevalue'], 10221599170) assert_equal(bt['masternode'][0]['amount'], 6132959502) # 0.6 self.log.info("Reward split should stay ~60/40 after reallocation is done") for period in range(10): # check 10 next superblocks self.bump_mocktime(10) self.nodes[0].generate(10) self.sync_blocks() bt = self.nodes[0].getblocktemplate() assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500)) assert_equal(bt['coinbasevalue'], 9491484944) assert_equal(bt['masternode'][0]['amount'], 5694890966) # 0.6 self.log.info("Rollback the chain back to the STARTED state") self.mocktime = self.nodes[0].getblock(pre_locked_in_blockhash, 1)['time'] for node in self.nodes: node.invalidateblock(pre_locked_in_blockhash) self.sync_all() # create and send non-signalling block test_block = self.create_test_block() self.nodes[0].p2p.send_blocks_and_test([test_block], self.nodes[0], timeout=5) bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 1499) assert_equal(bi['bip9_softforks']['realloc']['status'], 'started') assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(2)) self.log.info("Check thresholds reach min level and stay there") for i in range(8): # 7 to reach min level and 1 more to check it doesn't go lower than that self.signal(0, False) # no need to signal bi = self.nodes[0].getblockchaininfo() assert_equal(bi['blocks'], 1999 + i * 500) assert_equal(bi['bip9_softforks']['realloc']['status'], 'started') assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(i + 3)) assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], 300)
def mine_block(self, node, vtx=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None): if vtx is None: vtx = [] bt = node.getblocktemplate({'rules': ['segwit']}) height = bt['height'] tip_hash = bt['previousblockhash'] tip_block = node.getblock(tip_hash, 2)["tx"][0] coinbasevalue = 50 * COIN halvings = int(height / 150) # regtest coinbasevalue >>= halvings miner_script = self.nodes[0].getaddressinfo( self.nodes[0].getnewaddress())['scriptPubKey'] if mn_payee is None: if isinstance(bt['masternode'], list): mn_payee = bt['masternode'][0]['script'] else: mn_payee = bt['masternode']['script'] # we can't take the masternode payee amount from the template here as we might have additional fees in vtx new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value if mn_amount is None: mn_amount = get_masternode_payment( height, coinbasevalue, bt['masternode_collateral_height']) + new_fees / 2 miner_amount = int(coinbasevalue * 0.25) miner_amount += new_fees / 2 coinbase = CTransaction() coinbase.vout.append( CTxOut(int(miner_amount), hex_str_to_bytes(miner_script))) coinbase.vout.append(CTxOut(int(mn_amount), hex_str_to_bytes(mn_payee))) coinbase.vin = create_coinbase(height).vin # Recreate mn root as using one in BT would result in invalid merkle roots for masternode lists coinbase.nVersion = bt['version_coinbase'] if len(bt['default_witness_commitment_extra']) != 0: if use_mnmerkleroot_from_tip: cbtx = FromHex(CCbTx(version=2), bt['default_witness_commitment_extra']) if 'cbTx' in tip_block: cbtx.merkleRootMNList = int( tip_block['cbTx']['merkleRootMNList'], 16) else: cbtx.merkleRootMNList = 0 coinbase.extraData = cbtx.serialize() else: coinbase.extraData = hex_str_to_bytes( bt['default_witness_commitment_extra']) coinbase.calc_sha256(with_witness=True) block = create_block(int(tip_hash, 16), coinbase) block.nVersion = 4 block.vtx += vtx block.hashMerkleRoot = block.calc_merkle_root() add_witness_commitment(block) block.solve() result = node.submitblock(ToHex(block)) if expected_error is not None and result != expected_error: raise AssertionError( 'mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result)) elif expected_error is None and result is not None: raise AssertionError('submitblock returned %s' % (result))
def create_block(self, node, vtx=[]): bt = node.getblocktemplate() height = bt['height'] tip_hash = bt['previousblockhash'] coinbasevalue = bt['coinbasevalue'] miner_address = node.getnewaddress() mn_payee = bt['masternode'][0]['payee'] # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't # include the template's transactions bt_fees = 0 for tx in bt['transactions']: bt_fees += tx['fee'] new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value # fix fees coinbasevalue -= bt_fees coinbasevalue += new_fees mn_amount = get_masternode_payment(height, coinbasevalue) miner_amount = coinbasevalue - mn_amount outputs = {miner_address: str(Decimal(miner_amount) / COIN)} if mn_amount > 0: outputs[mn_payee] = str(Decimal(mn_amount) / COIN) coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) coinbase.vin = create_coinbase(height).vin # We can't really use this one as it would result in invalid merkle roots for masternode lists if len(bt['coinbase_payload']) != 0: cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) coinbase.nVersion = 3 coinbase.nType = 5 # CbTx coinbase.vExtraPayload = cbtx.serialize() coinbase.calc_sha256() block = create_block(int(tip_hash, 16), coinbase, nTime=bt['curtime']) block.vtx += vtx # Add quorum commitments from template for tx in bt['transactions']: tx2 = FromHex(CTransaction(), tx['data']) if tx2.nType == 6: block.vtx.append(tx2) block.hashMerkleRoot = block.calc_merkle_root() block.solve() return block
def mine_block(self, node, vtx=[], miner_address=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None): bt = node.getblocktemplate() height = bt['height'] tip_hash = bt['previousblockhash'] tip_block = node.getblock(tip_hash) coinbasevalue = bt['coinbasevalue'] if miner_address is None: miner_address = node.getnewaddress() if mn_payee is None: if isinstance(bt['masternode'], list): mn_payee = bt['masternode'][0]['payee'] else: mn_payee = bt['masternode']['payee'] # we can't take the masternode payee amount from the template here as we might have additional fees in vtx # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't # include the template's transactions bt_fees = 0 for tx in bt['transactions']: bt_fees += tx['fee'] new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value # fix fees coinbasevalue -= bt_fees coinbasevalue += new_fees if mn_amount is None: mn_amount = get_masternode_payment(height, coinbasevalue) miner_amount = coinbasevalue - mn_amount outputs = {miner_address: str(Decimal(miner_amount) / COIN)} if mn_amount > 0: outputs[mn_payee] = str(Decimal(mn_amount) / COIN) coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) coinbase.vin = create_coinbase(height).vin # We can't really use this one as it would result in invalid merkle roots for masternode lists if len(bt['coinbase_payload']) != 0: cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) if use_mnmerkleroot_from_tip: if 'cbTx' in tip_block: cbtx.merkleRootMNList = int(tip_block['cbTx']['merkleRootMNList'], 16) else: cbtx.merkleRootMNList = 0 coinbase.nVersion = 3 coinbase.nType = 5 # CbTx coinbase.vExtraPayload = cbtx.serialize() coinbase.calc_sha256() block = create_block(int(tip_hash, 16), coinbase) block.vtx += vtx # Add quorum commitments from template for tx in bt['transactions']: tx2 = FromHex(CTransaction(), tx['data']) if tx2.nType == 6: block.vtx.append(tx2) block.hashMerkleRoot = block.calc_merkle_root() block.solve() result = node.submitblock(ToHex(block)) if expected_error is not None and result != expected_error: raise AssertionError('mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result)) elif expected_error is None and result is not None: raise AssertionError('submitblock returned %s' % (result))