Skip to content

Commit

Permalink
Added tests for pos, added to help and cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjaenson committed Aug 27, 2017
1 parent be1d602 commit 4e8268a
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 51 deletions.
5 changes: 1 addition & 4 deletions qa/rpc-tests/qtum-soft-block-gas-limits.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#!/usr/bin/env python3
# Copyright (c) 2015-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
Expand Down Expand Up @@ -30,7 +27,7 @@ def setup_network(self, split=False):
["-staker-max-tx-gas-limit=100000"],
["-staker-max-tx-gas-limit=0"],
["-staker-min-tx-gas-price=0"],
["-staker-min-tx-gas-price=100"]
["-staker-min-tx-gas-price=0.000001"]
])
self.is_network_split = False
# Make the network fully connected
Expand Down
125 changes: 80 additions & 45 deletions qa/rpc-tests/qtum-transaction-prioritization.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#!/usr/bin/env python3
# Copyright (c) 2015-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
Expand All @@ -10,6 +7,7 @@
from test_framework.address import *
import sys
import random
import time

def p2pkh_to_hex_hash(address):
return str(base58_to_byte(address, 25)[1])[2:-1]
Expand All @@ -24,12 +22,34 @@ def __init__(self):
self.num_nodes = 1

def setup_network(self, split=False):
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [['-staking=1']])
self.is_network_split = False
self.node = self.nodes[0]

def restart_node(self):
stop_nodes(self.nodes)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [["-staking=1"]])
self.node = self.nodes[0]


def stake_or_mine(self, old_block_count=None, use_staking=False):
# Since staking is switched on by default, if a block has been staked return that block's hash
if self.node.getblockcount() > old_block_count:
return self.node.getbestblockhash()

if use_staking:
if not old_block_count:
old_block_count = self.node.getblockcount()
while old_block_count == self.node.getblockcount():
time.sleep(0.1)
return self.node.getbestblockhash()
else:
return self.node.generate(1)[0]

def send_transaction_with_fee(self, fee):
unspent = self.node.listunspent()[0]
for unspent in self.node.listunspent():
if unspent['amount'] >= 10000:
break
addr = self.node.getnewaddress()
haddr = p2pkh_to_hex_hash(addr)
tx = CTransaction()
Expand Down Expand Up @@ -93,24 +113,26 @@ def send_op_call_outputs_with_gas_price(self, contract_address, gas_prices, spen
tx_hex_signed = self.node.signrawtransaction(bytes_to_hex_str(tx.serialize()))['hex']
return self.node.sendrawtransaction(tx_hex_signed)

def verify_contract_txs_are_added_last_test(self, with_restart=False):
def verify_contract_txs_are_added_last_test(self, with_restart=False, use_staking=False):
# Set the fee really high so that it should normally be added first if we only looked at the fee/size
contract_txid = self.node.createcontract("00", 4*10**6, 0.0001)['txid']
normal_txid = self.node.sendtoaddress(self.node.getnewaddress(), 1)

old_block_count = self.node.getblockcount()
if with_restart:
stop_nodes(self.nodes)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
self.restart_node()
block_hash = self.stake_or_mine(old_block_count=old_block_count, use_staking=use_staking)

block_hash = self.node.generate(1)[0]
txs = self.node.getblock(block_hash)['tx']
assert_equal(len(txs), 3)
assert_equal(txs.index(normal_txid), 1)
assert_equal(txs.index(contract_txid), 2)
block_txs = self.node.getblock(block_hash)['tx']
if use_staking:
block_txs.pop(1) # Ignore the coinstake tx so we can reuse the tests for both pow and pos
assert_equal(len(block_txs), 3)
assert_equal(block_txs.index(normal_txid), 1)
assert_equal(block_txs.index(contract_txid), 2)

# Verifies that contract transactions are correctly ordered by descending (minimum among outputs) gas price and ascending size
# Sends 7 txs in total
def verify_contract_txs_internal_order_test(self, with_restart=False):
def verify_contract_txs_internal_order_test(self, with_restart=False, use_staking=False):
contract_address = list(self.node.listcontracts().keys())[0]
sender = self.node.getnewaddress()
tx4 = self.send_op_call_outputs_with_gas_price(contract_address, [0.0001])
Expand All @@ -120,15 +142,17 @@ def verify_contract_txs_internal_order_test(self, with_restart=False):
tx2 = self.send_op_call_outputs_with_gas_price(contract_address, [0.002])
tx1 = self.node.sendtoaddress(sender, 1)
tx7 = self.node.sendtocontract(contract_address, "00", 0, 100000, 0.00000001, sender)['txid']

old_block_count = self.node.getblockcount()
if with_restart:
stop_nodes(self.nodes)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)

self.restart_node()
# Ordering based on gas_price should now be
block_hash = self.node.generate(1)[0]
block_hash = self.stake_or_mine(old_block_count=old_block_count, use_staking=use_staking)
block = self.node.getblock(block_hash)
assert_equal(block['tx'][1:], [tx1, tx2, tx3, tx4, tx5, tx6, tx7])
block_txs = block['tx']
if use_staking:
block_txs.pop(1) # Ignore the coinstake tx so we can reuse the tests for both pow and pos

assert_equal(block_txs[1:], [tx1, tx2, tx3, tx4, tx5, tx6, tx7])



Expand All @@ -140,7 +164,7 @@ def verify_contract_txs_internal_order_test(self, with_restart=False):
# 3. a normal tx with a fee < tx2 and tx3
# 4. a op call contract tx spending tx2.
# Expected transaction ordering in the block should thus be tx1, tx2, tx3, tx4
def verify_ancestor_chain_with_contract_txs_test(self, with_restart=False):
def verify_ancestor_chain_with_contract_txs_test(self, with_restart=False, use_staking=False):
contract_address = list(self.node.listcontracts().keys())[0]
tx1 = self.send_transaction_with_fee(0.001)
tx2 = self.send_transaction_with_fee(0.0005)
Expand All @@ -151,35 +175,24 @@ def verify_ancestor_chain_with_contract_txs_test(self, with_restart=False):
# Make sure that all txs are in the mempool
assert_equal(len(self.node.getrawmempool()), 4)

old_block_count = self.node.getblockcount()
if with_restart:
stop_nodes(self.nodes)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
self.restart_node()
block_hash = self.stake_or_mine(old_block_count=old_block_count, use_staking=use_staking)

block_hash = self.node.generate(1)[0]
block_txs = self.node.getblock(block_hash)['tx']

if use_staking:
block_txs.pop(1) # Ignore the coinstake tx so we can reuse the tests for both pow and pos

assert_equal(len(block_txs), 5)
assert_equal(block_txs[1], tx1)
assert_equal(block_txs[2], tx2)
assert_equal(block_txs[3], tx3)
assert_equal(block_txs[4], tx4)



def verify_many_txs_are_correctly_ordered_in_block_test(self):
contract_address = self.node.listcontracts().keys()[0]
# create 50 contract txs with different gas prices calling the fallback function
for i in range(50):
self.node.sendtocontract(contract_address, "00", 0, 100000, random.uniform(0.00000001, 0.001))

# create 50 normal txs
for i in range(50):
self.node.sendtoaddress(self.node.getnewaddress(), random.randint(1, 1000))

self.node.generate(1)

# Creates two different contract tx chains.
def verify_contract_ancestor_txs_test(self, with_restart=False):
def verify_contract_ancestor_txs_test(self, with_restart=False, use_staking=False):
contract_address = list(self.node.listcontracts().keys())[0]
for unspent in self.node.listunspent():
if unspent['amount'] > 10000:
Expand Down Expand Up @@ -222,20 +235,23 @@ def verify_contract_ancestor_txs_test(self, with_restart=False):
unspent['vout'] = 1
expected_tx_order.append((expected_tx_index, unspent['txid']))

old_block_count = self.node.getblockcount()
if with_restart:
stop_nodes(self.nodes)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)

block_hash = self.node.generate(1)[0]
self.restart_node()
block_hash = self.stake_or_mine(old_block_count=old_block_count, use_staking=use_staking)
block_txs = self.node.getblock(block_hash)['tx']

if use_staking:
block_txs.pop(1) # Ignore the coinstake tx so we can reuse the tests for both pow and pos

# Even though the gas prices differ, since they the ancestor txs must be included before the child txs we expect the order by which they were sent.
# Always chosing the tx with the highest gas price whose ancestors have already been included.
# Even though the gas prices differ, since they the ancestor txs must be included before the child txs we expect the order by which they were sent,
# always chosing the tx with the highest gas price whose ancestors have already been included.
for (expected_tx_index, txid) in expected_tx_order:
assert_equal(block_txs[expected_tx_index], txid)

def run_test(self):
self.node.generate(COINBASE_MATURITY+500)
print("running pow tests")
self.verify_contract_txs_are_added_last_test()
self.verify_ancestor_chain_with_contract_txs_test()
self.verify_contract_txs_internal_order_test()
Expand All @@ -245,10 +261,29 @@ def run_test(self):
assert_equal(self.node.getrawmempool(), [])

# Redo the testing and check that the mempool is correctly ordered after a restart
print("running pow tests with restart")
self.verify_contract_txs_are_added_last_test(with_restart=True)
self.verify_ancestor_chain_with_contract_txs_test(with_restart=True)
self.verify_contract_txs_internal_order_test(with_restart=True)
self.verify_contract_ancestor_txs_test(with_restart=True)

# Verify that the mempool is empty before running more tests
assert_equal(self.node.getrawmempool(), [])

print("running pos tests")
self.verify_contract_txs_are_added_last_test(use_staking=True)
self.verify_ancestor_chain_with_contract_txs_test(use_staking=True)
self.verify_contract_txs_internal_order_test(use_staking=True)
self.verify_contract_ancestor_txs_test(use_staking=True)

# Verify that the mempool is empty before running more tests
assert_equal(self.node.getrawmempool(), [])

print("running pos tests with restart")
self.verify_contract_txs_are_added_last_test(with_restart=True, use_staking=True)
self.verify_ancestor_chain_with_contract_txs_test(with_restart=True, use_staking=True)
self.verify_contract_txs_internal_order_test(with_restart=True, use_staking=True)
self.verify_contract_ancestor_txs_test(with_restart=True, use_staking=True)

if __name__ == '__main__':
QtumTransactionPrioritizationTest().main()
5 changes: 5 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,11 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));
strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)));

strUsage += HelpMessageOpt("-staker-min-tx-gas-price=<amt>", _("Any contract execution with a gas price below this will not be included in a block (defaults to the value specified by the DGP)"));
strUsage += HelpMessageOpt("-staker-max-tx-gas-limit=<n>", _("Any contract execution with a gas limit over this amount will not be included in a block (defaults to soft block gas limit)"));
strUsage += HelpMessageOpt("-staker-soft-block-gas-limit=<n>", _("After this amount of gas is surpassed in a block, no more contract executions will be added to the block (defaults to consensus-critical maximum block gas limit)"));

if (showDebug)
strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios");

Expand Down
8 changes: 6 additions & 2 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,12 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
globalSealEngine->setQtumSchedule(qtumDGP.getGasSchedule(nHeight));
uint32_t blockSizeDGP = qtumDGP.getBlockSize(nHeight);
minGasPrice = qtumDGP.getMinGasPrice(nHeight);
minGasPrice = GetArg("-staker-min-tx-gas-price", minGasPrice);
minGasPrice = std::max(minGasPrice, qtumDGP.getMinGasPrice(nHeight));
if(IsArgSet("-staker-min-tx-gas-price")) {
CAmount stakerMinGasPrice;
if(ParseMoney(GetArg("-staker-min-tx-gas-price", ""), stakerMinGasPrice)) {
minGasPrice = std::max(minGasPrice, (uint64_t)stakerMinGasPrice);
}
}
hardBlockGasLimit = qtumDGP.getBlockGasLimit(nHeight);
softBlockGasLimit = GetArg("-staker-soft-block-gas-limit", hardBlockGasLimit);
softBlockGasLimit = std::min(softBlockGasLimit, hardBlockGasLimit);
Expand Down

0 comments on commit 4e8268a

Please sign in to comment.