Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_feature_minerfund.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2020 The Bitcoin developers | # Copyright (c) 2020 The Bitcoin developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
from decimal import Decimal | from decimal import Decimal | ||||
from test_framework.blocktools import create_block, create_coinbase | from test_framework.blocktools import create_block, create_coinbase | ||||
from test_framework.cashaddr import decode | from test_framework.cashaddr import decode | ||||
from test_framework.messages import XEC, CTxOut, ToHex | from test_framework.messages import XEC, CTxOut, ToHex | ||||
from test_framework.script import OP_EQUAL, OP_HASH160, CScript | from test_framework.script import OP_EQUAL, OP_HASH160, CScript | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.txtools import pad_tx | from test_framework.txtools import pad_tx | ||||
from test_framework.util import assert_equal, assert_greater_than_or_equal | from test_framework.util import assert_equal, assert_greater_than_or_equal | ||||
AXION_ACTIVATION_TIME = 2000000600 | |||||
GLUON_ACTIVATION_TIME = 2100000600 | GLUON_ACTIVATION_TIME = 2100000600 | ||||
MINER_FUND_RATIO = 8 | MINER_FUND_RATIO = 8 | ||||
MINER_FUND_ADDR_AXION = 'ecregtest:pqnqv9lt7e5vjyp0w88zf2af0l92l8rxdgz0wv9ltl' | MINER_FUND_ADDR_AXION = 'ecregtest:pqnqv9lt7e5vjyp0w88zf2af0l92l8rxdgz0wv9ltl' | ||||
MINER_FUND_ADDR = 'ecregtest:prfhcnyqnl5cgrnmlfmms675w93ld7mvvq9jcw0zsn' | MINER_FUND_ADDR = 'ecregtest:prfhcnyqnl5cgrnmlfmms675w93ld7mvvq9jcw0zsn' | ||||
class MinerFundTest(BitcoinTestFramework): | class MinerFundTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
self.num_nodes = 1 | self.num_nodes = 1 | ||||
self.extra_args = [[ | self.extra_args = [[ | ||||
'-enableminerfund', | '-enableminerfund', | ||||
'-axionactivationtime={}'.format(AXION_ACTIVATION_TIME), | |||||
'-gluonactivationtime={}'.format(GLUON_ACTIVATION_TIME), | '-gluonactivationtime={}'.format(GLUON_ACTIVATION_TIME), | ||||
]] | ]] | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
address = node.get_deterministic_priv_key().address | address = node.get_deterministic_priv_key().address | ||||
self.log.info('Create some history') | self.log.info('Create some history') | ||||
for _ in range(0, 50): | for _ in range(0, 50): | ||||
node.generatetoaddress(1, address) | node.generatetoaddress(1, address) | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
address = node.get_deterministic_priv_key().address | address = node.get_deterministic_priv_key().address | ||||
# Move MTP forward to axion activation | def get_best_coinbase(): | ||||
node.setmocktime(AXION_ACTIVATION_TIME) | return node.getblock(node.getbestblockhash(), 2)['tx'][0] | ||||
coinbase = get_best_coinbase() | |||||
assert_greater_than_or_equal(len(coinbase['vout']), 2) | |||||
block_reward = sum([vout['value'] for vout in coinbase['vout']]) | |||||
# Move MTP forward to gluon activation | |||||
node.setmocktime(GLUON_ACTIVATION_TIME) | |||||
node.generatetoaddress(6, address) | node.generatetoaddress(6, address) | ||||
assert_equal( | assert_equal( | ||||
node.getblockchaininfo()['mediantime'], | node.getblockchaininfo()['mediantime'], | ||||
AXION_ACTIVATION_TIME) | GLUON_ACTIVATION_TIME) | ||||
# Let's remember the hash of this block for later use. | # Let's remember the hash of this block for later use. | ||||
axion_fork_block_hash = int(node.getbestblockhash(), 16) | gluon_fork_block_hash = int(node.getbestblockhash(), 16) | ||||
def get_best_coinbase(): | |||||
return node.getblock(node.getbestblockhash(), 2)['tx'][0] | |||||
# No money goes to the fund. | |||||
coinbase = get_best_coinbase() | |||||
assert_equal(len(coinbase['vout']), 1) | |||||
block_reward = coinbase['vout'][0]['value'] | |||||
# Now we send part of the coinbase to the fund. | |||||
def check_miner_fund_output(expected_address): | def check_miner_fund_output(expected_address): | ||||
coinbase = get_best_coinbase() | coinbase = get_best_coinbase() | ||||
assert_equal(len(coinbase['vout']), 2) | assert_equal(len(coinbase['vout']), 2) | ||||
assert_equal( | assert_equal( | ||||
coinbase['vout'][1]['scriptPubKey']['addresses'][0], | coinbase['vout'][1]['scriptPubKey']['addresses'][0], | ||||
expected_address) | expected_address) | ||||
total = Decimal() | total = Decimal() | ||||
for o in coinbase['vout']: | for o in coinbase['vout']: | ||||
total += o['value'] | total += o['value'] | ||||
assert_equal(total, block_reward) | assert_equal(total, block_reward) | ||||
assert_greater_than_or_equal( | assert_greater_than_or_equal( | ||||
coinbase['vout'][1]['value'], | coinbase['vout'][1]['value'], | ||||
(MINER_FUND_RATIO * total) / 100) | (MINER_FUND_RATIO * total) / 100) | ||||
# First block with the new rules. | # The coinbase has an output to the legacy miner fund address | ||||
node.generatetoaddress(1, address) | # Now we send part of the coinbase to the fund. | ||||
check_miner_fund_output(MINER_FUND_ADDR_AXION) | check_miner_fund_output(MINER_FUND_ADDR_AXION) | ||||
# Invalidate top block, submit a custom block that do not send anything | # First block with the miner fund address. | ||||
# to the fund and check it is rejected. | node.generatetoaddress(1, address) | ||||
check_miner_fund_output(MINER_FUND_ADDR) | |||||
# Invalidate top block. | |||||
node.invalidateblock(node.getbestblockhash()) | node.invalidateblock(node.getbestblockhash()) | ||||
def check_bad_miner_fund(prev_hash, time, coinbase=None): | def check_bad_miner_fund(prev_hash, time, coinbase=None): | ||||
if coinbase is None: | if coinbase is None: | ||||
coinbase = create_coinbase(node.getblockcount() + 1) | coinbase = create_coinbase(node.getblockcount() + 1) | ||||
block = create_block(prev_hash, coinbase, time, version=4) | block = create_block(prev_hash, coinbase, time, version=4) | ||||
block.solve() | block.solve() | ||||
assert_equal(node.submitblock(ToHex(block)), 'bad-cb-minerfund') | assert_equal(node.submitblock(ToHex(block)), 'bad-cb-minerfund') | ||||
check_bad_miner_fund(axion_fork_block_hash, AXION_ACTIVATION_TIME + 1) | |||||
# Move MTP forward to gluon activation | |||||
node.setmocktime(GLUON_ACTIVATION_TIME) | |||||
node.generatetoaddress(6, address) | |||||
assert_equal( | |||||
node.getblockchaininfo()['mediantime'], | |||||
GLUON_ACTIVATION_TIME) | |||||
# Let's remember the hash of this block for later use. | |||||
gluon_fork_block_hash = int(node.getbestblockhash(), 16) | |||||
# The coinbase has an output to the legacy miner fund address | |||||
# Now we send part of the coinbase to the fund. | |||||
check_miner_fund_output(MINER_FUND_ADDR_AXION) | |||||
# First block with the miner fund address. | |||||
node.generatetoaddress(1, address) | |||||
check_miner_fund_output(MINER_FUND_ADDR) | |||||
# Invalidate top block. | |||||
node.invalidateblock(node.getbestblockhash()) | |||||
# A block with no miner fund coinbase should be rejected. | # A block with no miner fund coinbase should be rejected. | ||||
check_bad_miner_fund(gluon_fork_block_hash, GLUON_ACTIVATION_TIME + 1) | check_bad_miner_fund(gluon_fork_block_hash, GLUON_ACTIVATION_TIME + 1) | ||||
def create_cb_pay_to_address(address): | def create_cb_pay_to_address(address): | ||||
_, _, script_hash = decode(address) | _, _, script_hash = decode(address) | ||||
miner_fund_amount = int( | miner_fund_amount = int( | ||||
block_reward * XEC * MINER_FUND_RATIO / 100) | block_reward * XEC * MINER_FUND_RATIO / 100) | ||||
Show All 38 Lines |