@@ -90,7 +90,6 @@ def run_test(self):
9090 self .test_op_return ()
9191 self .test_watchonly ()
9292 self .test_all_watched_funds ()
93- self .test_feerate_with_conf_target_and_estimate_mode ()
9493 self .test_option_feerate ()
9594 self .test_address_reuse ()
9695 self .test_option_subtract_fee_from_outputs ()
@@ -708,74 +707,89 @@ def test_all_watched_funds(self):
708707 wwatch .unloadwallet ()
709708
710709 def test_option_feerate (self ):
711- self .log .info ("Test fundrawtxn feeRate option" )
712-
713- # Make sure there is exactly one input so coin selection can't skew the result.
714- assert_equal (len (self .nodes [3 ].listunspent (1 )), 1 )
715-
716- inputs = []
717- outputs = {self .nodes [3 ].getnewaddress () : 1 }
718- rawtx = self .nodes [3 ].createrawtransaction (inputs , outputs )
719- result = self .nodes [3 ].fundrawtransaction (rawtx ) # uses self.min_relay_tx_fee (set by settxfee)
720- result2 = self .nodes [3 ].fundrawtransaction (rawtx , {"feeRate" : 2 * self .min_relay_tx_fee })
721- result3 = self .nodes [3 ].fundrawtransaction (rawtx , {"feeRate" : 10 * self .min_relay_tx_fee })
722- assert_raises_rpc_error (- 4 , "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)" , self .nodes [3 ].fundrawtransaction , rawtx , {"feeRate" : 1 })
723- result_fee_rate = result ['fee' ] * 1000 / count_bytes (result ['hex' ])
724- assert_fee_amount (result2 ['fee' ], count_bytes (result2 ['hex' ]), 2 * result_fee_rate )
725- assert_fee_amount (result3 ['fee' ], count_bytes (result3 ['hex' ]), 10 * result_fee_rate )
726-
727- def test_feerate_with_conf_target_and_estimate_mode (self ):
728- self .log .info ("Test fundrawtxn passing an explicit fee rate using conf_target and estimate_mode" )
710+ self .log .info ("Test fundrawtxn with explicit fee rates (fee_rate sat/vB and feeRate BTC/kvB)" )
729711 node = self .nodes [3 ]
730712 # Make sure there is exactly one input so coin selection can't skew the result.
731- assert_equal (len (node .listunspent (1 )), 1 )
713+ assert_equal (len (self . nodes [ 3 ] .listunspent (1 )), 1 )
732714 inputs = []
733715 outputs = {node .getnewaddress () : 1 }
734716 rawtx = node .createrawtransaction (inputs , outputs )
735717
736- for unit , fee_rate in {"btc/kb" : 0.1 , "sat/b" : 10000 }.items ():
737- self .log .info ("Test fundrawtxn with conf_target {} estimate_mode {} produces expected fee" .format (fee_rate , unit ))
738- # With no arguments passed, expect fee of 141 sats/b.
739- assert_approx (node .fundrawtransaction (rawtx )["fee" ], vexp = 0.00000141 , vspan = 0.00000001 )
740- # Expect fee to be 10,000x higher when explicit fee 10,000x greater is specified.
741- result = node .fundrawtransaction (rawtx , {"conf_target" : fee_rate , "estimate_mode" : unit })
742- assert_approx (result ["fee" ], vexp = 0.0141 , vspan = 0.0001 )
718+ result = node .fundrawtransaction (rawtx ) # uses self.min_relay_tx_fee (set by settxfee)
719+ btc_kvb_to_sat_vb = 100000 # (1e5)
720+ result1 = node .fundrawtransaction (rawtx , {"fee_rate" : 2 * btc_kvb_to_sat_vb * self .min_relay_tx_fee })
721+ result2 = node .fundrawtransaction (rawtx , {"feeRate" : 2 * self .min_relay_tx_fee })
722+ result3 = node .fundrawtransaction (rawtx , {"fee_rate" : 10 * btc_kvb_to_sat_vb * self .min_relay_tx_fee })
723+ result4 = node .fundrawtransaction (rawtx , {"feeRate" : 10 * self .min_relay_tx_fee })
724+ result_fee_rate = result ['fee' ] * 1000 / count_bytes (result ['hex' ])
725+ assert_fee_amount (result1 ['fee' ], count_bytes (result2 ['hex' ]), 2 * result_fee_rate )
726+ assert_fee_amount (result2 ['fee' ], count_bytes (result2 ['hex' ]), 2 * result_fee_rate )
727+ assert_fee_amount (result3 ['fee' ], count_bytes (result3 ['hex' ]), 10 * result_fee_rate )
728+ assert_fee_amount (result4 ['fee' ], count_bytes (result3 ['hex' ]), 10 * result_fee_rate )
743729
744- for field , fee_rate in { "conf_target" : 0.1 , "estimate_mode" : "sat/b" }. items ():
745- self . log . info ( "Test fundrawtxn raises RPC error if both feeRate and {} are passed" . format ( field ) )
746- assert_raises_rpc_error (
747- - 8 , "Cannot specify both {} and feeRate" . format ( field ),
748- lambda : node . fundrawtransaction ( rawtx , { "feeRate" : 0.1 , field : fee_rate }) )
730+ # With no arguments passed, expect fee of 141 satoshis.
731+ assert_approx ( node . fundrawtransaction ( rawtx )[ "fee" ], vexp = 0.00000141 , vspan = 0.00000001 )
732+ # Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
733+ result = node . fundrawtransaction ( rawtx , { "fee_rate" : 10000 })
734+ assert_approx ( result [ "fee" ], vexp = 0.0141 , vspan = 0.0001 )
749735
750736 self .log .info ("Test fundrawtxn with invalid estimate_mode settings" )
751737 for k , v in {"number" : 42 , "object" : {"foo" : "bar" }}.items ():
752738 assert_raises_rpc_error (- 3 , "Expected type string for estimate_mode, got {}" .format (k ),
753- lambda : self . nodes [ 1 ]. fundrawtransaction ( rawtx , {"estimate_mode" : v , "conf_target" : 0.1 }) )
754- for mode in ["foo" , Decimal ("3.141592" )]:
739+ node . fundrawtransaction , rawtx , {"estimate_mode" : v , "conf_target" : 0.1 , "add_inputs" : True } )
740+ for mode in ["" , " foo" , Decimal ("3.141592" )]:
755741 assert_raises_rpc_error (- 8 , "Invalid estimate_mode parameter" ,
756- lambda : self . nodes [ 1 ]. fundrawtransaction ( rawtx , {"estimate_mode" : mode , "conf_target" : 0.1 }) )
742+ node . fundrawtransaction , rawtx , {"estimate_mode" : mode , "conf_target" : 0.1 , "add_inputs" : True } )
757743
758744 self .log .info ("Test fundrawtxn with invalid conf_target settings" )
759- for mode in ["unset" , "economical" , "conservative" , "btc/kb" , "sat/b" ]:
745+ for mode in ["unset" , "economical" , "conservative" ]:
760746 self .log .debug ("{}" .format (mode ))
761747 for k , v in {"string" : "" , "object" : {"foo" : "bar" }}.items ():
762748 assert_raises_rpc_error (- 3 , "Expected type number for conf_target, got {}" .format (k ),
763- lambda : self .nodes [1 ].fundrawtransaction (rawtx , {"estimate_mode" : mode , "conf_target" : v }))
764- if mode in ["btc/kb" , "sat/b" ]:
765- assert_raises_rpc_error (- 3 , "Amount out of range" ,
766- lambda : self .nodes [1 ].fundrawtransaction (rawtx , {"estimate_mode" : mode , "conf_target" : - 1 }))
767- assert_raises_rpc_error (- 4 , "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)" ,
768- lambda : self .nodes [1 ].fundrawtransaction (rawtx , {"estimate_mode" : mode , "conf_target" : 0 }))
769- else :
770- for n in [- 1 , 0 , 1009 ]:
771- assert_raises_rpc_error (- 8 , "Invalid conf_target, must be between 1 and 1008" ,
772- lambda : self .nodes [1 ].fundrawtransaction (rawtx , {"estimate_mode" : mode , "conf_target" : n }))
773-
774- for unit , fee_rate in {"sat/B" : 0.99999999 , "BTC/kB" : 0.00000999 }.items ():
775- self .log .info ("- raises RPC error 'fee rate too low' if conf_target {} and estimate_mode {} are passed" .format (fee_rate , unit ))
776- assert_raises_rpc_error (- 4 , "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)" ,
777- lambda : self .nodes [1 ].fundrawtransaction (rawtx , {"estimate_mode" : unit , "conf_target" : fee_rate , "add_inputs" : True }))
778-
749+ node .fundrawtransaction , rawtx , {"estimate_mode" : mode , "conf_target" : v , "add_inputs" : True })
750+ for n in [- 1 , 0 , 1009 ]:
751+ assert_raises_rpc_error (- 8 , "Invalid conf_target, must be between 1 and 1008" , # max value of 1008 per src/policy/fees.h
752+ node .fundrawtransaction , rawtx , {"estimate_mode" : mode , "conf_target" : n , "add_inputs" : True })
753+
754+ self .log .info ("Test invalid fee rate settings" )
755+ assert_raises_rpc_error (- 4 , "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)" ,
756+ node .fundrawtransaction , rawtx , {"fee_rate" : 0 , "add_inputs" : True })
757+ assert_raises_rpc_error (- 8 , "Invalid feeRate 0.00000000 BTC/kB (must be greater than 0)" ,
758+ node .fundrawtransaction , rawtx , {"feeRate" : 0 , "add_inputs" : True })
759+ for param , value in {("fee_rate" , 100000 ), ("feeRate" , 1.000 )}:
760+ assert_raises_rpc_error (- 4 , "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)" ,
761+ node .fundrawtransaction , rawtx , {param : value , "add_inputs" : True })
762+ assert_raises_rpc_error (- 3 , "Amount out of range" ,
763+ node .fundrawtransaction , rawtx , {"fee_rate" : - 1 , "add_inputs" : True })
764+ assert_raises_rpc_error (- 3 , "Amount is not a number or string" ,
765+ node .fundrawtransaction , rawtx , {"fee_rate" : {"foo" : "bar" }, "add_inputs" : True })
766+ assert_raises_rpc_error (- 3 , "Invalid amount" ,
767+ node .fundrawtransaction , rawtx , {"fee_rate" : "" , "add_inputs" : True })
768+
769+ # Test setting explicit fee rate just below the minimum.
770+ self .log .info ("- raises RPC error 'fee rate too low' if fee_rate of 0.99999999 sat/vB is passed" )
771+ msg = "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)"
772+ assert_raises_rpc_error (- 4 , msg , node .fundrawtransaction , rawtx , {"fee_rate" : 0.99999999 , "add_inputs" : True })
773+ # This feeRate test only passes if `coinControl.fOverrideFeeRate = true` in wallet/rpcwallet.cpp::FundTransaction is removed.
774+ # assert_raises_rpc_error(-4, msg, node.fundrawtransaction, rawtx, {"feeRate": 0.00000999, "add_inputs": True})
775+
776+ self .log .info ("- raises RPC error if both feeRate and fee_rate are passed" )
777+ assert_raises_rpc_error (- 8 , "Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB)" ,
778+ node .fundrawtransaction , rawtx , {"fee_rate" : 0.1 , "feeRate" : 0.1 , "add_inputs" : True })
779+
780+ self .log .info ("- raises RPC error if both feeRate and estimate_mode passed" )
781+ assert_raises_rpc_error (- 8 , "Cannot specify both estimate_mode and feeRate" ,
782+ node .fundrawtransaction , rawtx , {"estimate_mode" : "economical" , "feeRate" : 0.1 , "add_inputs" : True })
783+
784+ for param in ["feeRate" , "fee_rate" ]:
785+ self .log .info ("- raises RPC error if both {} and conf_target are passed" .format (param ))
786+ assert_raises_rpc_error (- 8 , "Cannot specify both conf_target and {}. Please provide either a confirmation "
787+ "target in blocks for automatic fee estimation, or an explicit fee rate." .format (param ),
788+ node .fundrawtransaction , rawtx , {param : 1 , "conf_target" : 1 , "add_inputs" : True })
789+
790+ self .log .info ("- raises RPC error if both fee_rate and estimate_mode are passed" )
791+ assert_raises_rpc_error (- 8 , "Cannot specify both estimate_mode and fee_rate" ,
792+ node .fundrawtransaction , rawtx , {"fee_rate" : 1 , "estimate_mode" : "economical" , "add_inputs" : True })
779793
780794 def test_address_reuse (self ):
781795 """Test no address reuse occurs."""
@@ -803,12 +817,32 @@ def test_option_subtract_fee_from_outputs(self):
803817 outputs = {self .nodes [2 ].getnewaddress (): 1 }
804818 rawtx = self .nodes [3 ].createrawtransaction (inputs , outputs )
805819
820+ # Test subtract fee from outputs with feeRate (BTC/kvB)
806821 result = [self .nodes [3 ].fundrawtransaction (rawtx ), # uses self.min_relay_tx_fee (set by settxfee)
807822 self .nodes [3 ].fundrawtransaction (rawtx , {"subtractFeeFromOutputs" : []}), # empty subtraction list
808823 self .nodes [3 ].fundrawtransaction (rawtx , {"subtractFeeFromOutputs" : [0 ]}), # uses self.min_relay_tx_fee (set by settxfee)
809824 self .nodes [3 ].fundrawtransaction (rawtx , {"feeRate" : 2 * self .min_relay_tx_fee }),
810825 self .nodes [3 ].fundrawtransaction (rawtx , {"feeRate" : 2 * self .min_relay_tx_fee , "subtractFeeFromOutputs" : [0 ]}),]
826+ dec_tx = [self .nodes [3 ].decoderawtransaction (tx_ ['hex' ]) for tx_ in result ]
827+ output = [d ['vout' ][1 - r ['changepos' ]]['value' ] for d , r in zip (dec_tx , result )]
828+ change = [d ['vout' ][r ['changepos' ]]['value' ] for d , r in zip (dec_tx , result )]
829+
830+ assert_equal (result [0 ]['fee' ], result [1 ]['fee' ], result [2 ]['fee' ])
831+ assert_equal (result [3 ]['fee' ], result [4 ]['fee' ])
832+ assert_equal (change [0 ], change [1 ])
833+ assert_equal (output [0 ], output [1 ])
834+ assert_equal (output [0 ], output [2 ] + result [2 ]['fee' ])
835+ assert_equal (change [0 ] + result [0 ]['fee' ], change [2 ])
836+ assert_equal (output [3 ], output [4 ] + result [4 ]['fee' ])
837+ assert_equal (change [3 ] + result [3 ]['fee' ], change [4 ])
811838
839+ # Test subtract fee from outputs with fee_rate (sat/vB)
840+ btc_kvb_to_sat_vb = 100000 # (1e5)
841+ result = [self .nodes [3 ].fundrawtransaction (rawtx ), # uses self.min_relay_tx_fee (set by settxfee)
842+ self .nodes [3 ].fundrawtransaction (rawtx , {"subtractFeeFromOutputs" : []}), # empty subtraction list
843+ self .nodes [3 ].fundrawtransaction (rawtx , {"subtractFeeFromOutputs" : [0 ]}), # uses self.min_relay_tx_fee (set by settxfee)
844+ self .nodes [3 ].fundrawtransaction (rawtx , {"fee_rate" : 2 * btc_kvb_to_sat_vb * self .min_relay_tx_fee }),
845+ self .nodes [3 ].fundrawtransaction (rawtx , {"fee_rate" : 2 * btc_kvb_to_sat_vb * self .min_relay_tx_fee , "subtractFeeFromOutputs" : [0 ]}),]
812846 dec_tx = [self .nodes [3 ].decoderawtransaction (tx_ ['hex' ]) for tx_ in result ]
813847 output = [d ['vout' ][1 - r ['changepos' ]]['value' ] for d , r in zip (dec_tx , result )]
814848 change = [d ['vout' ][r ['changepos' ]]['value' ] for d , r in zip (dec_tx , result )]
0 commit comments