mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
192 lines
6.8 KiB
Python
Executable File
192 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2016 The Bitcoin Core developers
|
|
# Distributed under the MIT/X11 software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
#
|
|
|
|
from test_framework.blocktools import create_block
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.util import assert_equal
|
|
|
|
from binascii import b2a_hex
|
|
from decimal import Decimal
|
|
|
|
def find_unspent(node, txid, amount):
|
|
for utxo in node.listunspent(0):
|
|
if utxo['txid'] != txid:
|
|
continue
|
|
if utxo['amount'] != amount:
|
|
continue
|
|
return {'txid': utxo['txid'], 'vout': utxo['vout']}
|
|
|
|
def solve_template_hex(tmpl, txlist):
|
|
block = create_block(tmpl=tmpl, txlist=txlist)
|
|
block.solve()
|
|
b = block.serialize()
|
|
x = b2a_hex(b).decode('ascii')
|
|
return x
|
|
|
|
def get_modified_size(node, txdata):
|
|
decoded = node.decoderawtransaction(txdata)
|
|
size = decoded['vsize']
|
|
for inp in decoded['vin']:
|
|
offset = 41 + min(len(inp['scriptSig']['hex']) // 2, 110)
|
|
if offset <= size:
|
|
size -= offset
|
|
return size
|
|
|
|
def assert_approximate(a, b):
|
|
assert_equal(int(a), int(b))
|
|
|
|
BTC = Decimal('100000000')
|
|
|
|
class PriorityTest(BitcoinTestFramework):
|
|
def add_options(self, parser):
|
|
self.add_wallet_options(parser)
|
|
|
|
def set_test_params(self):
|
|
self.num_nodes = 3
|
|
self.testmsg_num = 0
|
|
|
|
def setup_nodes(self):
|
|
self.extra_args = [
|
|
['-blockmaxsize=0'],
|
|
['-blockprioritysize=1000000', '-blockmaxsize=1000000', '-printpriority'],
|
|
['-blockmaxsize=0'],
|
|
]
|
|
|
|
super().setup_nodes()
|
|
|
|
def assert_prio(self, txid, starting, current):
|
|
node = self.nodes[1]
|
|
|
|
tmpl = node.getblocktemplate({'rules':('segwit',)})
|
|
tmplentry = None
|
|
for tx in tmpl['transactions']:
|
|
if tx['txid'] == txid:
|
|
tmplentry = tx
|
|
break
|
|
# GBT does not expose starting priority, so we don't check that
|
|
assert_approximate(tmplentry['priority'], current)
|
|
|
|
mempoolentry = node.getrawmempool(True)[txid]
|
|
assert_approximate(mempoolentry['startingpriority'], starting)
|
|
assert_approximate(mempoolentry['currentpriority'], current)
|
|
|
|
def testmsg(self, msg):
|
|
self.testmsg_num += 1
|
|
self.log.info('Test %d: %s' % (self.testmsg_num, msg))
|
|
|
|
def skip_test_if_missing_module(self):
|
|
self.skip_if_no_wallet()
|
|
|
|
def run_test(self):
|
|
node = self.nodes[0]
|
|
miner = self.nodes[1]
|
|
|
|
self.generate(node, 50)
|
|
self.generate(miner, 101)
|
|
|
|
fee = Decimal('0.0001')
|
|
amt = Decimal('11')
|
|
|
|
txid_a = node.sendtoaddress(node.getnewaddress(), amt)
|
|
txdata_b = node.createrawtransaction([find_unspent(node, txid_a, amt)], {node.getnewaddress(): amt - fee})
|
|
txdata_b = node.signrawtransactionwithwallet(txdata_b)['hex']
|
|
txmodsize_b = get_modified_size(node, txdata_b)
|
|
txid_b = node.sendrawtransaction(txdata_b)
|
|
self.sync_all()
|
|
|
|
self.testmsg('priority starts at 0 with all unconfirmed inputs')
|
|
self.assert_prio(txid_b, 0, 0)
|
|
|
|
self.testmsg('priority increases correctly when that input is mined')
|
|
|
|
# Mine only the sendtoaddress transaction
|
|
tmpl = node.getblocktemplate({'rules':('segwit',)})
|
|
rawblock = solve_template_hex(tmpl, [node.getrawtransaction(txid_a)])
|
|
assert_equal(node.submitblock(rawblock), None)
|
|
self.sync_all()
|
|
|
|
self.assert_prio(txid_b, 0, amt * BTC / txmodsize_b)
|
|
|
|
self.testmsg('priority continues to increase the deeper the block confirming its inputs gets buried')
|
|
|
|
self.generate(node, 2)
|
|
|
|
self.assert_prio(txid_b, 0, amt * BTC * 3 / txmodsize_b)
|
|
|
|
self.testmsg('with a confirmed input, the initial priority is calculated correctly')
|
|
|
|
self.generate(miner, 4)
|
|
|
|
amt_c = (amt - fee) / 2
|
|
amt_c2 = amt_c - fee
|
|
txdata_c = node.createrawtransaction([find_unspent(node, txid_b, amt - fee)], {node.getnewaddress(): amt_c, node.getnewaddress(): amt_c2})
|
|
txdata_c = node.signrawtransactionwithwallet(txdata_c)['hex']
|
|
txmodsize_c = get_modified_size(node, txdata_c)
|
|
txid_c = node.sendrawtransaction(txdata_c)
|
|
self.sync_all()
|
|
|
|
txid_c_starting_prio = (amt - fee) * BTC * 4 / txmodsize_c
|
|
self.assert_prio(txid_c, txid_c_starting_prio, txid_c_starting_prio)
|
|
|
|
self.testmsg('with an input confirmed prior to the transaction, the priority gets incremented correctly as it gets buried deeper')
|
|
|
|
self.generate(node, 1)
|
|
|
|
self.assert_prio(txid_c, txid_c_starting_prio, (amt - fee) * BTC * 5 / txmodsize_c)
|
|
|
|
self.testmsg('with an input confirmed prior to the transaction, the priority gets incremented correctly as it gets buried deeper and deeper')
|
|
|
|
self.generate(node, 2)
|
|
|
|
self.assert_prio(txid_c, txid_c_starting_prio, (amt - fee) * BTC * 7 / txmodsize_c)
|
|
|
|
self.log.info('(preparing for reorg test)')
|
|
|
|
self.generate(miner, 1)
|
|
|
|
self.split_network()
|
|
node = self.nodes[0]
|
|
miner = self.nodes[1]
|
|
competing_miner = self.nodes[2]
|
|
|
|
txdata_d = node.createrawtransaction([find_unspent(node, txid_c, amt_c)], {node.getnewaddress(): amt_c - fee})
|
|
txdata_d = node.signrawtransactionwithwallet(txdata_d)['hex']
|
|
get_modified_size(node, txdata_d)
|
|
txid_d = node.sendrawtransaction(txdata_d)
|
|
self.sync_all(self.nodes[:2])
|
|
self.sync_all(self.nodes[2:])
|
|
|
|
self.generate(miner, 1, sync_fun=self.no_op)
|
|
self.sync_all(self.nodes[:2])
|
|
self.sync_all(self.nodes[2:])
|
|
|
|
txdata_e = node.createrawtransaction([find_unspent(node, txid_d, amt_c - fee), find_unspent(node, txid_c, amt_c2)], {node.getnewaddress(): (amt_c - fee) + amt_c2 - fee})
|
|
txdata_e = node.signrawtransactionwithwallet(txdata_e)['hex']
|
|
txmodsize_e = get_modified_size(node, txdata_e)
|
|
txid_e = node.sendrawtransaction(txdata_e)
|
|
self.sync_all(self.nodes[:2])
|
|
self.sync_all(self.nodes[2:])
|
|
|
|
txid_e_starting_prio = (((amt_c - fee) * BTC) + (amt_c2 * BTC * 2)) / txmodsize_e
|
|
self.assert_prio(txid_e, txid_e_starting_prio, txid_e_starting_prio) # Sanity check 1
|
|
|
|
self.generate(competing_miner, 5, sync_fun=self.no_op)
|
|
self.sync_all(self.nodes[:2])
|
|
self.sync_all(self.nodes[2:])
|
|
|
|
self.assert_prio(txid_e, txid_e_starting_prio, txid_e_starting_prio) # Sanity check 2
|
|
|
|
self.testmsg('priority is updated correctly when input-confirming block is reorganised out')
|
|
|
|
self.connect_nodes(1, 2)
|
|
self.sync_blocks()
|
|
|
|
txid_e_reorg_prio = (amt_c2 * BTC * 6) / txmodsize_e
|
|
self.assert_prio(txid_e, txid_e_starting_prio, txid_e_reorg_prio)
|
|
|
|
if __name__ == '__main__':
|
|
PriorityTest(__file__).main()
|