[tests] Fix style warnings in feature_fee_estimation.py
This commit is contained in:
parent
4cad91663d
commit
d119f2ec1a
1 changed files with 59 additions and 60 deletions
|
@ -3,39 +3,41 @@
|
|||
# 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 decimal import Decimal
|
||||
import random
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
from test_framework.script import CScript, OP_1, OP_DROP, OP_2, OP_HASH160, OP_EQUAL, hash160, OP_TRUE
|
||||
from test_framework.mininode import CTransaction, CTxIn, CTxOut, COutPoint, ToHex, COIN
|
||||
from test_framework.script import CScript, OP_1, OP_DROP, OP_2, OP_HASH160, OP_EQUAL, hash160, OP_TRUE
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import satoshi_round, sync_mempools, sync_blocks, connect_nodes, assert_greater_than
|
||||
|
||||
# Construct 2 trivial P2SH's and the ScriptSigs that spend them
|
||||
# So we can create many transactions without needing to spend
|
||||
# time signing.
|
||||
redeem_script_1 = CScript([OP_1, OP_DROP])
|
||||
redeem_script_2 = CScript([OP_2, OP_DROP])
|
||||
P2SH_1 = CScript([OP_HASH160, hash160(redeem_script_1), OP_EQUAL])
|
||||
P2SH_2 = CScript([OP_HASH160, hash160(redeem_script_2), OP_EQUAL])
|
||||
REDEEM_SCRIPT_1 = CScript([OP_1, OP_DROP])
|
||||
REDEEM_SCRIPT_2 = CScript([OP_2, OP_DROP])
|
||||
P2SH_1 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_1), OP_EQUAL])
|
||||
P2SH_2 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_2), OP_EQUAL])
|
||||
|
||||
# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2
|
||||
SCRIPT_SIG = [CScript([OP_TRUE, redeem_script_1]), CScript([OP_TRUE, redeem_script_2])]
|
||||
SCRIPT_SIG = [CScript([OP_TRUE, REDEEM_SCRIPT_1]), CScript([OP_TRUE, REDEEM_SCRIPT_2])]
|
||||
|
||||
global log
|
||||
|
||||
def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment):
|
||||
"""
|
||||
Create and send a transaction with a random fee.
|
||||
"""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)
|
||||
"""
|
||||
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))
|
||||
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)
|
||||
tx = CTransaction()
|
||||
|
@ -50,83 +52,81 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
|
|||
total_in += t["amount"]
|
||||
tx.vin.append(CTxIn(COutPoint(int(t["txid"], 16), t["vout"]), b""))
|
||||
if total_in <= amount + fee:
|
||||
raise RuntimeError("Insufficient funds: need %d, have %d"%(amount+fee, total_in))
|
||||
tx.vout.append(CTxOut(int((total_in - amount - fee)*COIN), P2SH_1))
|
||||
tx.vout.append(CTxOut(int(amount*COIN), P2SH_2))
|
||||
raise RuntimeError("Insufficient funds: need %d, have %d" % (amount + fee, total_in))
|
||||
tx.vout.append(CTxOut(int((total_in - amount - fee) * COIN), P2SH_1))
|
||||
tx.vout.append(CTxOut(int(amount * COIN), P2SH_2))
|
||||
# These transactions don't need to be signed, but we still have to insert
|
||||
# the ScriptSig that will satisfy the ScriptPubKey.
|
||||
for inp in tx.vin:
|
||||
inp.scriptSig = SCRIPT_SIG[inp.prevout.n]
|
||||
txid = from_node.sendrawtransaction(ToHex(tx), True)
|
||||
unconflist.append({ "txid" : txid, "vout" : 0 , "amount" : total_in - amount - fee})
|
||||
unconflist.append({ "txid" : txid, "vout" : 1 , "amount" : amount})
|
||||
unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee})
|
||||
unconflist.append({"txid": txid, "vout": 1, "amount": amount})
|
||||
|
||||
return (ToHex(tx), fee)
|
||||
|
||||
def split_inputs(from_node, txins, txouts, initial_split = False):
|
||||
"""
|
||||
We need to generate a lot of inputs so we can generate a ton of transactions.
|
||||
def split_inputs(from_node, txins, txouts, initial_split=False):
|
||||
"""Generate a lot of inputs so we can generate a ton of transactions.
|
||||
|
||||
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.
|
||||
Previously this was designed to be small inputs so they wouldn't have
|
||||
a high coin age when the notion of priority still existed.
|
||||
"""
|
||||
a high coin age when the notion of priority still existed."""
|
||||
|
||||
prevtxout = txins.pop()
|
||||
tx = CTransaction()
|
||||
tx.vin.append(CTxIn(COutPoint(int(prevtxout["txid"], 16), prevtxout["vout"]), b""))
|
||||
|
||||
half_change = satoshi_round(prevtxout["amount"]/2)
|
||||
rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000")
|
||||
tx.vout.append(CTxOut(int(half_change*COIN), P2SH_1))
|
||||
tx.vout.append(CTxOut(int(rem_change*COIN), P2SH_2))
|
||||
half_change = satoshi_round(prevtxout["amount"] / 2)
|
||||
rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000")
|
||||
tx.vout.append(CTxOut(int(half_change * COIN), P2SH_1))
|
||||
tx.vout.append(CTxOut(int(rem_change * COIN), P2SH_2))
|
||||
|
||||
# If this is the initial split we actually need to sign the transaction
|
||||
# Otherwise we just need to insert the proper ScriptSig
|
||||
if (initial_split) :
|
||||
if (initial_split):
|
||||
completetx = from_node.signrawtransaction(ToHex(tx))["hex"]
|
||||
else :
|
||||
else:
|
||||
tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]]
|
||||
completetx = ToHex(tx)
|
||||
txid = from_node.sendrawtransaction(completetx, True)
|
||||
txouts.append({ "txid" : txid, "vout" : 0 , "amount" : half_change})
|
||||
txouts.append({ "txid" : txid, "vout" : 1 , "amount" : rem_change})
|
||||
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) ]
|
||||
def check_estimates(node, fees_seen, max_invalid, print_estimates=True):
|
||||
"""Call estimatefee and verify 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
|
||||
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):
|
||||
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)))
|
||||
% (float(e), min(fees_seen), max(fees_seen)))
|
||||
# Estimates should be monotonically decreasing
|
||||
if float(e)-delta > last_e:
|
||||
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)))
|
||||
% (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
|
||||
for i, e in enumerate(all_estimates): # estimate is for i+1
|
||||
if e >= 0:
|
||||
valid_estimate = True
|
||||
if i >= 13: # for n>=14 estimatesmartfee(n/2) should be at least as high as estimatefee(n)
|
||||
assert(node.estimatesmartfee((i+1)//2)["feerate"] > float(e) - delta)
|
||||
assert_greater_than(node.estimatesmartfee((i + 1) // 2)["feerate"], float(e) - delta)
|
||||
|
||||
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)
|
||||
approx_estimate = node.estimatesmartfee(i + 1)["feerate"]
|
||||
answer_found = node.estimatesmartfee(i + 1)["blocks"]
|
||||
assert_greater_than(approx_estimate, 0)
|
||||
assert_greater_than(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
|
||||
|
@ -136,7 +136,7 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True):
|
|||
# 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))
|
||||
raise AssertionError("More than (%d) invalid estimates" % (max_invalid))
|
||||
return all_estimates
|
||||
|
||||
|
||||
|
@ -160,7 +160,6 @@ class EstimateFeeTest(BitcoinTestFramework):
|
|||
# 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
|
||||
|
@ -169,14 +168,14 @@ class EstimateFeeTest(BitcoinTestFramework):
|
|||
# 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)
|
||||
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)
|
||||
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"]
|
||||
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 = []
|
||||
|
@ -210,13 +209,13 @@ class EstimateFeeTest(BitcoinTestFramework):
|
|||
# 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):
|
||||
# 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):
|
||||
# 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)
|
||||
|
@ -235,7 +234,7 @@ class EstimateFeeTest(BitcoinTestFramework):
|
|||
|
||||
self.fees_per_kb = []
|
||||
self.memutxo = []
|
||||
self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting
|
||||
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):
|
||||
|
|
Loading…
Reference in a new issue