diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py deleted file mode 100755 index 0134a72b9..000000000 --- a/test/functional/smartfees.py +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014-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. - -# -# Test fee estimation code -# - -from collections import OrderedDict -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * - -# Construct 2 trivial P2SH's and the ScriptSigs that spend them -# So we can create many many transactions without needing to spend -# time signing. -P2SH_1 = "2MySexEGVzZpRgNQ1JdjdP5bRETznm3roQ2" # P2SH of "OP_1 OP_DROP" -P2SH_2 = "2NBdpwq8Aoo1EEKEXPNrKvr5xQr3M9UfcZA" # P2SH of "OP_2 OP_DROP" -# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2 -# 4 bytes of OP_TRUE and push 2-byte redeem script of "OP_1 OP_DROP" or -# "OP_2 OP_DROP" -SCRIPT_SIG = ["0451025175", "0451025275"] - -global log - - -def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): - ''' - Create and send a transaction with a random fee. - The transaction pays to a trivial P2SH script, and assumes that its inputs - are of the same form. - The function takes a list of confirmed outputs and unconfirmed outputs - and attempts to use the confirmed list first for its inputs. - It adds the newly created outputs to the unconfirmed list. - Returns (raw transaction, fee) - ''' - # It's best to exponentially distribute our random fees - # because the buckets are exponentially spaced. - # Exponentially distributed from 1-128 * fee_increment - rand_fee = float(fee_increment) * (1.1892**random.randint(0, 28)) - # Total fee ranges from min_fee to min_fee + 127*fee_increment - fee = min_fee - fee_increment + satoshi_round(rand_fee) - inputs = [] - total_in = Decimal("0.00000000") - while total_in <= (amount + fee) and len(conflist) > 0: - t = conflist.pop(0) - total_in += t["amount"] - inputs.append({"txid": t["txid"], "vout": t["vout"]}) - if total_in <= amount + fee: - while total_in <= (amount + fee) and len(unconflist) > 0: - t = unconflist.pop(0) - total_in += t["amount"] - inputs.append({"txid": t["txid"], "vout": t["vout"]}) - if total_in <= amount + fee: - raise RuntimeError( - "Insufficient funds: need %d, have %d" % (amount + fee, total_in)) - outputs = {} - outputs = OrderedDict([(P2SH_1, total_in - amount - fee), - (P2SH_2, amount)]) - rawtx = from_node.createrawtransaction(inputs, outputs) - # createrawtransaction constructs a transaction that is ready to be signed. - # These transactions don't need to be signed, but we still have to insert the ScriptSig - # that will satisfy the ScriptPubKey. - completetx = rawtx[0:10] - inputnum = 0 - for inp in inputs: - completetx += rawtx[10 + 82 * inputnum:82 + 82 * inputnum] - completetx += SCRIPT_SIG[inp["vout"]] - completetx += rawtx[84 + 82 * inputnum:92 + 82 * inputnum] - inputnum += 1 - completetx += rawtx[10 + 82 * inputnum:] - txid = from_node.sendrawtransaction(completetx, True) - unconflist.append( - {"txid": txid, "vout": 0, "amount": total_in - amount - fee}) - unconflist.append({"txid": txid, "vout": 1, "amount": amount}) - - return (completetx, fee) - - -def split_inputs(from_node, txins, txouts, initial_split=False): - ''' - We need to generate a lot of very small inputs so we can generate a ton of transactions - and they will have low priority. - This function takes an input from txins, and creates and sends a transaction - which splits the value into 2 outputs which are appended to txouts. - ''' - prevtxout = txins.pop() - inputs = [] - inputs.append({"txid": prevtxout["txid"], "vout": prevtxout["vout"]}) - half_change = satoshi_round(prevtxout["amount"] / 2) - rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000") - outputs = OrderedDict([(P2SH_1, half_change), (P2SH_2, rem_change)]) - rawtx = from_node.createrawtransaction(inputs, outputs) - # If this is the initial split we actually need to sign the transaction - # Otherwise we just need to insert the property ScriptSig - if (initial_split): - completetx = from_node.signrawtransaction(rawtx)["hex"] - else: - completetx = rawtx[0:82] + SCRIPT_SIG[prevtxout["vout"]] + rawtx[84:] - txid = from_node.sendrawtransaction(completetx, True) - txouts.append({"txid": txid, "vout": 0, "amount": half_change}) - txouts.append({"txid": txid, "vout": 1, "amount": rem_change}) - - -def check_estimates(node, fees_seen, max_invalid, print_estimates=True): - ''' - This function calls estimatefee and verifies that the estimates - meet certain invariants. - ''' - all_estimates = [node.estimatefee(i) for i in range(1, 26)] - if print_estimates: - log.info([str(all_estimates[e - 1]) - for e in [1, 2, 3, 6, 15, 25]]) - delta = 1.0e-6 # account for rounding error - last_e = max(fees_seen) - for e in [x for x in all_estimates if x >= 0]: - # Estimates should be within the bounds of what transactions fees - # actually were: - if float(e) + delta < min(fees_seen) or float(e) - delta > max(fees_seen): - raise AssertionError("Estimated fee (%f) out of range (%f,%f)" - % (float(e), min(fees_seen), max(fees_seen))) - # Estimates should be monotonically decreasing - if float(e) - delta > last_e: - raise AssertionError("Estimated fee (%f) larger than last fee (%f) for lower number of confirms" - % (float(e), float(last_e))) - last_e = e - valid_estimate = False - invalid_estimates = 0 - for i, e in enumerate(all_estimates): # estimate is for i+1 - if e >= 0: - valid_estimate = True - # estimatesmartfee should return the same result - assert_equal(node.estimatesmartfee(i + 1)["feerate"], e) - - else: - invalid_estimates += 1 - - # estimatesmartfee should still be valid - approx_estimate = node.estimatesmartfee(i + 1)["feerate"] - answer_found = node.estimatesmartfee(i + 1)["blocks"] - assert(approx_estimate > 0) - assert(answer_found > i + 1) - - # Once we're at a high enough confirmation count that we can give an estimate - # We should have estimates for all higher confirmation counts - if valid_estimate: - raise AssertionError( - "Invalid estimate appears at higher confirm count than valid estimate") - - # Check on the expected number of different confirmation counts - # that we might not have valid estimates for - if invalid_estimates > max_invalid: - raise AssertionError( - "More than (%d) invalid estimates" % (max_invalid)) - return all_estimates - - -class EstimateFeeTest(BitcoinTestFramework): - def set_test_params(self): - self.num_nodes = 3 - - def setup_network(self): - """ - We'll setup the network to have 3 nodes that all mine with different parameters. - But first we need to use one node to create a lot of small low priority outputs - which we will use to generate our transactions. - """ - self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], - ["-blockmaxsize=17000", "-maxorphantx=1000"], - ["-blockmaxsize=8000", "-maxorphantx=1000"]]) - # Use node0 to mine blocks for input splitting - # Node1 mines small blocks but that are bigger than the expected transaction rate. - # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, - # (17k is room enough for 110 or so transactions) - # Node2 is a stingy miner, that - # produces too small blocks (room for only 55 or so transactions) - - def transact_and_mine(self, numblocks, mining_node): - min_fee = Decimal("0.00001") - # We will now mine numblocks blocks generating on average 100 transactions between each block - # We shuffle our confirmed txout set before each set of transactions - # small_txpuzzle_randfee will use the transactions that have inputs already in the chain when possible - # resorting to tx's that depend on the mempool when those run out - for i in range(numblocks): - random.shuffle(self.confutxo) - for j in range(random.randrange(100 - 50, 100 + 50)): - from_index = random.randint(1, 2) - (txhex, fee) = small_txpuzzle_randfee(self.nodes[ - from_index], self.confutxo, - self.memutxo, Decimal("0.005"), min_fee, min_fee) - tx_kbytes = (len(txhex) // 2) / 1000.0 - self.fees_per_kb.append(float(fee) / tx_kbytes) - sync_mempools(self.nodes[0:3], wait=.1) - mined = mining_node.getblock( - mining_node.generate(1)[0], True)["tx"] - sync_blocks(self.nodes[0:3], wait=.1) - # update which txouts are confirmed - newmem = [] - for utx in self.memutxo: - if utx["txid"] in mined: - self.confutxo.append(utx) - else: - newmem.append(utx) - self.memutxo = newmem - - def run_test(self): - self.log.info("This test is time consuming, please be patient") - self.log.info("Splitting inputs so we can generate tx's") - - # Make log handler available to helper functions - global log - log = self.log - - # Start node0 - self.start_node(0) - self.txouts = [] - self.txouts2 = [] - # Split a coinbase into two transaction puzzle outputs - split_inputs(self.nodes[0], self.nodes[0].listunspent( - 0), self.txouts, True) - - # Mine - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - - # Repeatedly split those 2 outputs, doubling twice for each rep - # Use txouts to monitor the available utxo, since these won't be tracked in wallet - reps = 0 - while (reps < 5): - # Double txouts to txouts2 - while (len(self.txouts) > 0): - split_inputs(self.nodes[0], self.txouts, self.txouts2) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - # Double txouts2 to txouts - while (len(self.txouts2) > 0): - split_inputs(self.nodes[0], self.txouts2, self.txouts) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - reps += 1 - self.log.info("Finished splitting") - - # Now we can connect the other nodes, didn't want to connect them earlier - # so the estimates would not be affected by the splitting transactions - self.start_node(1) - self.start_node(2) - connect_nodes(self.nodes[1], 0) - connect_nodes(self.nodes[0], 2) - connect_nodes(self.nodes[2], 1) - - self.sync_all() - - self.fees_per_kb = [] - self.memutxo = [] - self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting - self.log.info("Will output estimates for 1/2/3/6/15/25 blocks") - - for i in range(2): - self.log.info( - "Creating transactions and mining them with a block size that can't keep up") - # Create transactions and mine 10 small blocks with node 2, but - # create txs faster than we can mine - self.transact_and_mine(10, self.nodes[2]) - check_estimates(self.nodes[1], self.fees_per_kb, 14) - - self.log.info( - "Creating transactions and mining them at a block size that is just big enough") - # Generate transactions while mining 10 more blocks, this time with node1 - # which mines blocks with capacity just above the rate that - # transactions are being created - self.transact_and_mine(10, self.nodes[1]) - check_estimates(self.nodes[1], self.fees_per_kb, 2) - - # Finish by mining a normal-sized block: - while len(self.nodes[1].getrawmempool()) > 0: - self.nodes[1].generate(1) - - sync_blocks(self.nodes[0:3], wait=.1) - self.log.info("Final estimates after emptying mempools") - check_estimates(self.nodes[1], self.fees_per_kb, 2) - - -if __name__ == '__main__': - EstimateFeeTest().main() diff --git a/test/functional/timing.json b/test/functional/timing.json index 4b293bd13..15ddd81ef 100644 --- a/test/functional/timing.json +++ b/test/functional/timing.json @@ -1,338 +1,334 @@ [ { "name": "abandonconflict.py", "time": 15 }, { "name": "abc-cmdline.py", "time": 11 }, { "name": "abc-high_priority_transaction.py", "time": 15 }, { "name": "abc-mempool-accept-txn.py", "time": 5 }, { "name": "abc-p2p-compactblocks.py", "time": 222 }, { "name": "abc-p2p-fullblocktest.py", "time": 108 }, { "name": "abc-rpc.py", "time": 4 }, { "name": "assumevalid.py", "time": 16 }, { "name": "bip65-cltv-p2p.py", "time": 9 }, { "name": "bip68-112-113-p2p.py", "time": 28 }, { "name": "bip68-sequence.py", "time": 33 }, { "name": "bip9-softforks.py", "time": 51 }, { "name": "bipdersig-p2p.py", "time": 14 }, { "name": "bitcoin_cli.py", "time": 4 }, { "name": "blockchain.py", "time": 10 }, { "name": "dbcrash.py", "time": 1257 }, { "name": "decodescript.py", "time": 4 }, { "name": "disablewallet.py", "time": 4 }, { "name": "disconnect_ban.py", "time": 8 }, { "name": "example_test.py", "time": 3 }, { "name": "forknotify.py", "time": 4 }, { "name": "fundrawtransaction.py", "time": 83 }, { "name": "getblocktemplate_longpoll.py", "time": 69 }, { "name": "getchaintips.py", "time": 6 }, { "name": "httpbasics.py", "time": 4 }, { "name": "import-rescan.py", "time": 8 }, { "name": "importmulti.py", "time": 22 }, { "name": "importprunedfunds.py", "time": 4 }, { "name": "invalidateblock.py", "time": 10 }, { "name": "invalidblockrequest.py", "time": 5 }, { "name": "invalidtxrequest.py", "time": 6 }, { "name": "keypool-topup.py", "time": 13 }, { "name": "keypool.py", "time": 10 }, { "name": "listsinceblock.py", "time": 8 }, { "name": "listtransactions.py", "time": 18 }, { "name": "maxuploadtarget.py", "time": 58 }, { "name": "mempool_limit.py", "time": 9 }, { "name": "mempool_packages.py", "time": 10 }, { "name": "mempool_persist.py", "time": 19 }, { "name": "mempool_reorg.py", "time": 6 }, { "name": "mempool_resurrect_test.py", "time": 5 }, { "name": "mempool_spendcoinbase.py", "time": 4 }, { "name": "merkle_blocks.py", "time": 5 }, { "name": "minchainwork.py", "time": 13 }, { "name": "mining.py", "time": 10 }, { "name": "multi_rpc.py", "time": 7 }, { "name": "multiwallet.py", "time": 16 }, { "name": "net.py", "time": 4 }, { "name": "nulldummy.py", "time": 4 }, { "name": "p2p-acceptblock.py", "time": 11 }, { "name": "p2p-compactblocks.py", "time": 27 }, { "name": "p2p-feefilter.py", "time": 51 }, { "name": "p2p-fullblocktest.py", "time": 197 }, { "name": "p2p-leaktests.py", "time": 12 }, { "name": "p2p-mempool.py", "time": 4 }, { "name": "p2p-timeouts.py", "time": 64 }, { "name": "p2p-versionbits-warning.py", "time": 12 }, { "name": "preciousblock.py", "time": 5 }, { "name": "prioritise_transaction.py", "time": 27 }, { "name": "proxy_test.py", "time": 8 }, { "name": "pruning.py", "time": 1841 }, { "name": "rawtransactions.py", "time": 15 }, { "name": "receivedby.py", "time": 24 }, { "name": "reindex.py", "time": 16 }, { "name": "resendwallettransactions.py", "time": 6 }, { "name": "rest.py", "time": 9 }, { "name": "rpcbind_test.py", "time": 28 }, { "name": "rpcnamedargs.py", "time": 4 }, { "name": "sendheaders.py", "time": 27 }, { "name": "signmessages.py", "time": 4 }, { "name": "signrawtransactions.py", "time": 3 }, - { - "name": "smartfees.py", - "time": 301 - }, { "name": "txn_clone.py", "time": 6 }, { "name": "txn_clone.py --mineblock", "time": 18 }, { "name": "txn_doublespend.py", "time": 10 }, { "name": "txn_doublespend.py --mineblock", "time": 18 }, { "name": "uptime.py", "time": 5 }, { "name": "wallet-accounts.py", "time": 3 }, { "name": "wallet-dump.py", "time": 8 }, { "name": "wallet-encryption.py", "time": 11 }, { "name": "wallet-hd.py", "time": 27 }, { "name": "wallet.py", "time": 53 }, { "name": "walletbackup.py", "time": 121 }, { "name": "zapwallettxes.py", "time": 21 } ] \ No newline at end of file