1#!/usr/bin/env python3 2 3from test_framework.test_framework import BitcoinTestFramework 4from test_framework.util import * 5from test_framework.script import * 6from test_framework.mininode import * 7from test_framework.qtum import * 8from test_framework.blocktools import * 9import time 10import io 11 12class QtumIdenticalRefunds(BitcoinTestFramework): 13 def set_test_params(self): 14 self.setup_clean_chain = True 15 self.num_nodes = 1 16 17 def skip_test_if_missing_module(self): 18 self.skip_if_no_wallet() 19 20 def run_test(self): 21 self.node = self.nodes[0] 22 self.node.generate(100+COINBASE_MATURITY) 23 """ 24 contract Test { 25 function() payable {} 26 } 27 """ 28 code = "60606040523415600e57600080fd5b5b603980601c6000396000f30060606040525b600b5b5b565b0000a165627a7a72305820e28f464040162c6b98c6be21a9de622f2c8f102259a90476df697d0beb9ac9880029" 29 contract_address = self.node.createcontract(code)['address'] 30 self.node.generate(1) 31 32 # Send 2 evm txs that will call the fallback function of the Test contract with the same sender. 33 # This will result in the same amount of gas being spent and thus the same amount being refunded. 34 sender_address = self.node.getnewaddress() 35 self.node.sendtoaddress(sender_address, 1) 36 self.node.sendtocontract(contract_address, "00", 0, 1000000, QTUM_MIN_GAS_PRICE_STR, sender_address) 37 self.node.sendtoaddress(sender_address, 1) 38 self.node.sendtocontract(contract_address, "00", 0, 1000000, QTUM_MIN_GAS_PRICE_STR, sender_address) 39 40 # Check that all txs were accepted into the mempool. 41 assert_equal(len(self.node.getrawmempool()), 4) 42 block_hash = self.node.generate(1)[0] 43 44 # Verify that all txs were included in the block. 45 assert_equal(len(self.node.getrawmempool()), 0) 46 47 # Fetch the last generated block containing the txs and refund outputs and deserialize it. 48 manipulation_block = CBlock() 49 block_raw = self.node.getblock(block_hash, False) 50 f = io.BytesIO(hex_str_to_bytes(block_raw)) 51 manipulation_block.deserialize(f) 52 53 # Make sure that all expected outputs are present in the coinbase 54 assert_equal(len(manipulation_block.vtx[0].vout), 4) 55 assert_equal(manipulation_block.vtx[0].vout[-2].scriptPubKey == manipulation_block.vtx[0].vout[0].scriptPubKey, False) 56 57 # Now we change the last refund output's scriptPubKey to the miner's scriptPubKey. 58 manipulation_block.vtx[0].vout[-2].scriptPubKey = manipulation_block.vtx[0].vout[0].scriptPubKey 59 manipulation_block.hashMerkleRoot = manipulation_block.calc_merkle_root() 60 manipulation_block.solve() 61 62 # Invalidate the last block so that we can submit the manipulated block. 63 self.node.invalidateblock(self.node.getbestblockhash()) 64 65 block_count = self.node.getblockcount() 66 67 # Resubmit the manipulated block. 68 ret = self.node.submitblock(bytes_to_hex_str(manipulation_block.serialize())) 69 70 # Check that the block was not accepted 71 assert_equal(ret is None, False) 72 73 # Make sure that the block was not accepted. 74 assert_equal(self.node.getblockcount(), block_count) 75 76if __name__ == '__main__': 77 QtumIdenticalRefunds().main() 78