@@ -90,6 +90,9 @@ def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
9090 self ._address = ADDRESS_BCRT1_P2SH_OP_TRUE
9191 self ._scriptPubKey = bytes .fromhex (self ._test_node .validateaddress (self ._address )['scriptPubKey' ])
9292
93+ def _create_utxo (self , * , txid , vout , value , height ):
94+ return {"txid" : txid , "vout" : vout , "value" : value , "height" : height }
95+
9396 def get_balance (self ):
9497 return sum (u ['value' ] for u in self ._utxos )
9598
@@ -99,13 +102,22 @@ def rescan_utxos(self):
99102 res = self ._test_node .scantxoutset (action = "start" , scanobjects = [self .get_descriptor ()])
100103 assert_equal (True , res ['success' ])
101104 for utxo in res ['unspents' ]:
102- self ._utxos .append ({ ' txid' : utxo [' txid' ], ' vout' : utxo [' vout' ], ' value' : utxo [' amount' ], ' height' : utxo [' height' ]} )
105+ self ._utxos .append (self . _create_utxo ( txid = utxo [" txid" ], vout = utxo [" vout" ], value = utxo [" amount" ], height = utxo [" height" ]) )
103106
104107 def scan_tx (self , tx ):
105- """Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
108+ """Scan the tx and adjust the internal list of owned utxos"""
109+ for spent in tx ["vin" ]:
110+ # Mark spent. This may happen when the caller has ownership of a
111+ # utxo that remained in this wallet. For example, by passing
112+ # mark_as_spent=False to get_utxo or by using an utxo returned by a
113+ # create_self_transfer* call.
114+ try :
115+ self .get_utxo (txid = spent ["txid" ], vout = spent ["vout" ])
116+ except StopIteration :
117+ pass
106118 for out in tx ['vout' ]:
107119 if out ['scriptPubKey' ]['hex' ] == self ._scriptPubKey .hex ():
108- self ._utxos .append ({ ' txid' : tx [' txid' ], ' vout' : out ['n' ], ' value' : out [' value' ], ' height' : 0 } )
120+ self ._utxos .append (self . _create_utxo ( txid = tx [" txid" ], vout = out ["n" ], value = out [" value" ], height = 0 ) )
109121
110122 def sign_tx (self , tx , fixed_length = True ):
111123 """Sign tx that has been created by MiniWallet in P2PK mode"""
@@ -124,12 +136,16 @@ def sign_tx(self, tx, fixed_length=True):
124136 tx .rehash ()
125137
126138 def generate (self , num_blocks , ** kwargs ):
127- """Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list """
139+ """Generate blocks with coinbase outputs to the internal address, and call rescan_utxos """
128140 blocks = self ._test_node .generatetodescriptor (num_blocks , self .get_descriptor (), ** kwargs )
129- for b in blocks :
130- block_info = self ._test_node .getblock (blockhash = b , verbosity = 2 )
131- cb_tx = block_info ['tx' ][0 ]
132- self ._utxos .append ({'txid' : cb_tx ['txid' ], 'vout' : 0 , 'value' : cb_tx ['vout' ][0 ]['value' ], 'height' : block_info ['height' ]})
141+ # Calling rescan_utxos here makes sure that after a generate the utxo
142+ # set is in a clean state. For example, the wallet will update
143+ # - if the caller consumed utxos, but never used them
144+ # - if the caller sent a transaction that is not mined or got rbf'd
145+ # - after block re-orgs
146+ # - the utxo height for mined mempool txs
147+ # - However, the wallet will not consider remaining mempool txs
148+ self .rescan_utxos ()
133149 return blocks
134150
135151 def get_scriptPubKey (self ):
@@ -188,20 +204,10 @@ def send_to(self, *, from_node, scriptPubKey, amount, fee=1000):
188204 return txid , 1
189205
190206 def send_self_transfer_multi (self , * , from_node , ** kwargs ):
191- """
192- Create and send a transaction that spends the given UTXOs and creates a
193- certain number of outputs with equal amounts.
194-
195- Returns a dictionary with
196- - txid
197- - serialized transaction in hex format
198- - transaction as CTransaction instance
199- - list of newly created UTXOs, ordered by vout index
200- """
207+ """Call create_self_transfer_multi and send the transaction."""
201208 tx = self .create_self_transfer_multi (** kwargs )
202- txid = self .sendrawtransaction (from_node = from_node , tx_hex = tx .serialize ().hex ())
203- return {'new_utxos' : [self .get_utxo (txid = txid , vout = vout ) for vout in range (len (tx .vout ))],
204- 'txid' : txid , 'hex' : tx .serialize ().hex (), 'tx' : tx }
209+ self .sendrawtransaction (from_node = from_node , tx_hex = tx ["hex" ])
210+ return tx
205211
206212 def create_self_transfer_multi (
207213 self ,
@@ -234,7 +240,18 @@ def create_self_transfer_multi(
234240 outputs_value_total = inputs_value_total - fee_per_output * num_outputs
235241 for o in tx .vout :
236242 o .nValue = outputs_value_total // num_outputs
237- return tx
243+ txid = tx .rehash ()
244+ return {
245+ "new_utxos" : [self ._create_utxo (
246+ txid = txid ,
247+ vout = i ,
248+ value = Decimal (tx .vout [i ].nValue ) / COIN ,
249+ height = 0 ,
250+ ) for i in range (len (tx .vout ))],
251+ "txid" : txid ,
252+ "hex" : tx .serialize ().hex (),
253+ "tx" : tx ,
254+ }
238255
239256 def create_self_transfer (self , * , fee_rate = Decimal ("0.003" ), utxo_to_spend = None , locktime = 0 , sequence = 0 ):
240257 """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
@@ -245,12 +262,12 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), utxo_to_spend=None,
245262 vsize = Decimal (168 ) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)
246263 else :
247264 assert False
248- send_value = int ( COIN * ( utxo_to_spend [' value' ] - fee_rate * ( vsize / 1000 )) )
265+ send_value = utxo_to_spend [" value" ] - ( fee_rate * vsize / 1000 )
249266 assert send_value > 0
250267
251268 tx = CTransaction ()
252269 tx .vin = [CTxIn (COutPoint (int (utxo_to_spend ['txid' ], 16 ), utxo_to_spend ['vout' ]), nSequence = sequence )]
253- tx .vout = [CTxOut (send_value , self ._scriptPubKey )]
270+ tx .vout = [CTxOut (int ( COIN * send_value ) , self ._scriptPubKey )]
254271 tx .nLockTime = locktime
255272 if self ._mode == MiniWalletMode .RAW_P2PK :
256273 self .sign_tx (tx )
@@ -263,8 +280,9 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), utxo_to_spend=None,
263280 tx_hex = tx .serialize ().hex ()
264281
265282 assert_equal (tx .get_vsize (), vsize )
283+ new_utxo = self ._create_utxo (txid = tx .rehash (), vout = 0 , value = send_value , height = 0 )
266284
267- return {' txid' : tx . rehash (), ' hex' : tx_hex , 'tx' : tx }
285+ return {" txid" : new_utxo [ "txid" ], " hex" : tx_hex , "tx" : tx , "new_utxo" : new_utxo }
268286
269287 def sendrawtransaction (self , * , from_node , tx_hex , maxfeerate = 0 , ** kwargs ):
270288 txid = from_node .sendrawtransaction (hexstring = tx_hex , maxfeerate = maxfeerate , ** kwargs )
0 commit comments