@ -3,25 +3,13 @@
# 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 copy import deepcopy
from decimal import Decimal
import os
import random
from test_framework . messages import (
COIN ,
COutPoint ,
CTransaction ,
CTxIn ,
CTxOut ,
)
from test_framework . script import (
CScript ,
OP_1 ,
OP_DROP ,
OP_TRUE ,
)
from test_framework . script_util import (
script_to_p2sh_script ,
)
from test_framework . test_framework import BitcoinTestFramework
from test_framework . util import (
@ -31,22 +19,14 @@ from test_framework.util import (
assert_raises_rpc_error ,
satoshi_round ,
)
# Construct 2 trivial P2SH's and the ScriptSigs that spend them
# So we can create many transactions without needing to spend
# time signing.
SCRIPT = CScript ( [ OP_1 , OP_DROP ] )
P2SH = script_to_p2sh_script ( SCRIPT )
REDEEM_SCRIPT = CScript ( [ OP_TRUE , SCRIPT ] )
from test_framework . wallet import MiniWallet
def small_txpuzzle_randfee (
from_node, conflist , unconflist , amount , min_fee , fee_increment
wallet , 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 using MiniWallet.
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 .
@ -58,23 +38,29 @@ def small_txpuzzle_randfee(
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 ( )
utxos_to_spend = [ ]
total_in = Decimal ( " 0.00000000 " )
while total_in < = ( amount + fee ) and len ( conflist ) > 0 :
t = conflist . pop ( 0 )
total_in + = t [ " amount " ]
tx. vin . append ( CTxIn ( COutPoint ( int ( t [ " txid " ] , 16 ) , t [ " vout " ] ) , REDEEM_SCRIPT ) )
total_in + = t [ " value " ]
utxos_to_spend. append ( t )
while total_in < = ( amount + fee ) and len ( unconflist ) > 0 :
t = unconflist . pop ( 0 )
total_in + = t [ " amount " ]
tx. vin . append ( CTxIn ( COutPoint ( int ( t [ " txid " ] , 16 ) , t [ " vout " ] ) , REDEEM_SCRIPT ) )
total_in + = t [ " value " ]
utxos_to_spend. append ( t )
if total_in < = amount + fee :
raise RuntimeError ( f " Insufficient funds: need { amount + fee } , have { total_in } " )
tx . vout . append ( CTxOut ( int ( ( total_in - amount - fee ) * COIN ) , P2SH ) )
tx . vout . append ( CTxOut ( int ( amount * COIN ) , P2SH ) )
tx = wallet . create_self_transfer_multi (
from_node = from_node ,
utxos_to_spend = utxos_to_spend ,
fee_per_output = 0 )
tx . vout [ 0 ] . nValue = int ( ( total_in - amount - fee ) * COIN )
tx . vout . append ( deepcopy ( tx . vout [ 0 ] ) )
tx . vout [ 1 ] . nValue = int ( amount * COIN )
txid = from_node . sendrawtransaction ( hexstring = tx . serialize ( ) . hex ( ) , maxfeerate = 0 )
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 , " value " : total_in - amount - fee } )
unconflist . append ( { " txid " : txid , " vout " : 1 , " value " : amount } )
return ( tx . serialize ( ) . hex ( ) , fee )
@ -129,17 +115,13 @@ def check_estimates(node, fees_seen):
check_smart_estimates ( node , fees_seen )
def send_tx ( node, utxo , feerate ) :
def send_tx ( wallet, node, utxo , feerate ) :
""" Broadcast a 1in-1out transaction with a specific input and feerate (sat/vb). """
tx = CTransaction ( )
tx . vin = [ CTxIn ( COutPoint ( int ( utxo [ " txid " ] , 16 ) , utxo [ " vout " ] ) , REDEEM_SCRIPT ) ]
tx . vout = [ CTxOut ( int ( utxo [ " amount " ] * COIN ) , P2SH ) ]
# vbytes == bytes as we are using legacy transactions
fee = tx . get_vsize ( ) * feerate
tx . vout [ 0 ] . nValue - = fee
return node . sendrawtransaction ( tx . serialize ( ) . hex ( ) )
return wallet . send_self_transfer (
from_node = node ,
utxo_to_spend = utxo ,
fee_rate = Decimal ( feerate * 1000 ) / COIN ,
) [ ' txid ' ]
class EstimateFeeTest ( BitcoinTestFramework ) :
@ -152,9 +134,6 @@ class EstimateFeeTest(BitcoinTestFramework):
[ " -whitelist=noban@127.0.0.1 " , " -blockmaxweight=32000 " ] ,
]
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
def setup_network ( self ) :
"""
We ' ll setup the network to have 3 nodes that all mine with different parameters.
@ -168,9 +147,6 @@ class EstimateFeeTest(BitcoinTestFramework):
# (68k weight is room enough for 120 or so transactions)
# Node2 is a stingy miner, that
# produces too small blocks (room for only 55 or so transactions)
self . start_nodes ( )
self . import_deterministic_coinbase_privkeys ( )
self . stop_nodes ( )
def transact_and_mine ( self , numblocks , mining_node ) :
min_fee = Decimal ( " 0.00001 " )
@ -183,6 +159,7 @@ class EstimateFeeTest(BitcoinTestFramework):
for _ in range ( random . randrange ( 100 - 50 , 100 + 50 ) ) :
from_index = random . randint ( 1 , 2 )
( txhex , fee ) = small_txpuzzle_randfee (
self . wallet ,
self . nodes [ from_index ] ,
self . confutxo ,
self . memutxo ,
@ -205,24 +182,10 @@ class EstimateFeeTest(BitcoinTestFramework):
def initial_split ( self , node ) :
""" Split two coinbase UTxOs into many small coins """
utxo_count = 2048
self . confutxo = [ ]
splitted_amount = Decimal ( " 0.04 " )
fee = Decimal ( " 0.1 " )
change = Decimal ( " 100 " ) - splitted_amount * utxo_count - fee
tx = CTransaction ( )
tx . vin = [
CTxIn ( COutPoint ( int ( cb [ " txid " ] , 16 ) , cb [ " vout " ] ) )
for cb in node . listunspent ( ) [ : 2 ]
]
tx . vout = [ CTxOut ( int ( splitted_amount * COIN ) , P2SH ) for _ in range ( utxo_count ) ]
tx . vout . append ( CTxOut ( int ( change * COIN ) , P2SH ) )
txhex = node . signrawtransactionwithwallet ( tx . serialize ( ) . hex ( ) ) [ " hex " ]
txid = node . sendrawtransaction ( txhex )
self . confutxo = [
{ " txid " : txid , " vout " : i , " amount " : splitted_amount }
for i in range ( utxo_count )
]
self . confutxo = self . wallet . send_self_transfer_multi (
from_node = node ,
utxos_to_spend = [ self . wallet . get_utxo ( ) for _ in range ( 2 ) ] ,
num_outputs = 2048 ) [ ' new_utxos ' ]
while len ( node . getrawmempool ( ) ) > 0 :
self . generate ( node , 1 , sync_fun = self . no_op )
@ -284,12 +247,12 @@ class EstimateFeeTest(BitcoinTestFramework):
# Broadcast 45 low fee transactions that will need to be RBF'd
for _ in range ( 45 ) :
u = utxos . pop ( 0 )
txid = send_tx ( node , u , low_feerate )
txid = send_tx ( self . wallet , node , u , low_feerate )
utxos_to_respend . append ( u )
txids_to_replace . append ( txid )
# Broadcast 5 low fee transaction which don't need to
for _ in range ( 5 ) :
send_tx ( node , utxos . pop ( 0 ) , low_feerate )
send_tx ( self . wallet , node , utxos . pop ( 0 ) , low_feerate )
# Mine the transactions on another node
self . sync_mempools ( wait = 0.1 , nodes = [ node , miner ] )
for txid in txids_to_replace :
@ -298,7 +261,7 @@ class EstimateFeeTest(BitcoinTestFramework):
# RBF the low-fee transactions
while len ( utxos_to_respend ) > 0 :
u = utxos_to_respend . pop ( 0 )
send_tx ( node , u , high_feerate )
send_tx ( self . wallet , node , u , high_feerate )
# Mine the last replacement txs
self . sync_mempools ( wait = 0.1 , nodes = [ node , miner ] )
@ -316,6 +279,8 @@ class EstimateFeeTest(BitcoinTestFramework):
# Split two coinbases into many small utxos
self . start_node ( 0 )
self . wallet = MiniWallet ( self . nodes [ 0 ] )
self . wallet . rescan_utxos ( )
self . initial_split ( self . nodes [ 0 ] )
self . log . info ( " Finished splitting " )