@@ -101,6 +101,9 @@ def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
101
101
self ._address , self ._internal_key = create_deterministic_address_bcrt1_p2tr_op_true ()
102
102
self ._scriptPubKey = bytes .fromhex (self ._test_node .validateaddress (self ._address )['scriptPubKey' ])
103
103
104
+ def _create_utxo (self , * , txid , vout , value , height ):
105
+ return {"txid" : txid , "vout" : vout , "value" : value , "height" : height }
106
+
104
107
def get_balance (self ):
105
108
return sum (u ['value' ] for u in self ._utxos )
106
109
@@ -110,13 +113,22 @@ def rescan_utxos(self):
110
113
res = self ._test_node .scantxoutset (action = "start" , scanobjects = [self .get_descriptor ()])
111
114
assert_equal (True , res ['success' ])
112
115
for utxo in res ['unspents' ]:
113
- self ._utxos .append ({ ' txid' : utxo [' txid' ], ' vout' : utxo [' vout' ], ' value' : utxo [' amount' ], ' height' : utxo [' height' ]} )
116
+ self ._utxos .append (self . _create_utxo ( txid = utxo [" txid" ], vout = utxo [" vout" ], value = utxo [" amount" ], height = utxo [" height" ]) )
114
117
115
118
def scan_tx (self , tx ):
116
- """Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
119
+ """Scan the tx and adjust the internal list of owned utxos"""
120
+ for spent in tx ["vin" ]:
121
+ # Mark spent. This may happen when the caller has ownership of a
122
+ # utxo that remained in this wallet. For example, by passing
123
+ # mark_as_spent=False to get_utxo or by using an utxo returned by a
124
+ # create_self_transfer* call.
125
+ try :
126
+ self .get_utxo (txid = spent ["txid" ], vout = spent ["vout" ])
127
+ except StopIteration :
128
+ pass
117
129
for out in tx ['vout' ]:
118
130
if out ['scriptPubKey' ]['hex' ] == self ._scriptPubKey .hex ():
119
- self ._utxos .append ({ ' txid' : tx [' txid' ], ' vout' : out ['n' ], ' value' : out [' value' ], ' height' : 0 } )
131
+ self ._utxos .append (self . _create_utxo ( txid = tx [" txid" ], vout = out ["n" ], value = out [" value" ], height = 0 ) )
120
132
121
133
def sign_tx (self , tx , fixed_length = True ):
122
134
"""Sign tx that has been created by MiniWallet in P2PK mode"""
@@ -135,12 +147,16 @@ def sign_tx(self, tx, fixed_length=True):
135
147
tx .rehash ()
136
148
137
149
def generate (self , num_blocks , ** kwargs ):
138
- """Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list """
150
+ """Generate blocks with coinbase outputs to the internal address, and call rescan_utxos """
139
151
blocks = self ._test_node .generatetodescriptor (num_blocks , self .get_descriptor (), ** kwargs )
140
- for b in blocks :
141
- block_info = self ._test_node .getblock (blockhash = b , verbosity = 2 )
142
- cb_tx = block_info ['tx' ][0 ]
143
- self ._utxos .append ({'txid' : cb_tx ['txid' ], 'vout' : 0 , 'value' : cb_tx ['vout' ][0 ]['value' ], 'height' : block_info ['height' ]})
152
+ # Calling rescan_utxos here makes sure that after a generate the utxo
153
+ # set is in a clean state. For example, the wallet will update
154
+ # - if the caller consumed utxos, but never used them
155
+ # - if the caller sent a transaction that is not mined or got rbf'd
156
+ # - after block re-orgs
157
+ # - the utxo height for mined mempool txs
158
+ # - However, the wallet will not consider remaining mempool txs
159
+ self .rescan_utxos ()
144
160
return blocks
145
161
146
162
def get_scriptPubKey (self ):
@@ -206,20 +222,10 @@ def send_to(self, *, from_node, scriptPubKey, amount, fee=1000):
206
222
return txid , 1
207
223
208
224
def send_self_transfer_multi (self , * , from_node , ** kwargs ):
209
- """
210
- Create and send a transaction that spends the given UTXOs and creates a
211
- certain number of outputs with equal amounts.
212
-
213
- Returns a dictionary with
214
- - txid
215
- - serialized transaction in hex format
216
- - transaction as CTransaction instance
217
- - list of newly created UTXOs, ordered by vout index
218
- """
225
+ """Call create_self_transfer_multi and send the transaction."""
219
226
tx = self .create_self_transfer_multi (** kwargs )
220
- txid = self .sendrawtransaction (from_node = from_node , tx_hex = tx .serialize ().hex ())
221
- return {'new_utxos' : [self .get_utxo (txid = txid , vout = vout ) for vout in range (len (tx .vout ))],
222
- 'txid' : txid , 'hex' : tx .serialize ().hex (), 'tx' : tx }
227
+ self .sendrawtransaction (from_node = from_node , tx_hex = tx ["hex" ])
228
+ return tx
223
229
224
230
def create_self_transfer_multi (
225
231
self ,
@@ -253,7 +259,18 @@ def create_self_transfer_multi(
253
259
outputs_value_total = inputs_value_total - fee_per_output * num_outputs
254
260
for o in tx .vout :
255
261
o .nValue = outputs_value_total // num_outputs
256
- return tx
262
+ txid = tx .rehash ()
263
+ return {
264
+ "new_utxos" : [self ._create_utxo (
265
+ txid = txid ,
266
+ vout = i ,
267
+ value = Decimal (tx .vout [i ].nValue ) / COIN ,
268
+ height = 0 ,
269
+ ) for i in range (len (tx .vout ))],
270
+ "txid" : txid ,
271
+ "hex" : tx .serialize ().hex (),
272
+ "tx" : tx ,
273
+ }
257
274
258
275
def create_self_transfer (self , * , fee_rate = Decimal ("0.003" ), utxo_to_spend = None , locktime = 0 , sequence = 0 ):
259
276
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
@@ -264,12 +281,12 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), utxo_to_spend=None,
264
281
vsize = Decimal (168 ) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)
265
282
else :
266
283
assert False
267
- send_value = int ( COIN * ( utxo_to_spend [' value' ] - fee_rate * ( vsize / 1000 )) )
284
+ send_value = utxo_to_spend [" value" ] - ( fee_rate * vsize / 1000 )
268
285
assert send_value > 0
269
286
270
287
tx = CTransaction ()
271
288
tx .vin = [CTxIn (COutPoint (int (utxo_to_spend ['txid' ], 16 ), utxo_to_spend ['vout' ]), nSequence = sequence )]
272
- tx .vout = [CTxOut (send_value , self ._scriptPubKey )]
289
+ tx .vout = [CTxOut (int ( COIN * send_value ) , self ._scriptPubKey )]
273
290
tx .nLockTime = locktime
274
291
if self ._mode == MiniWalletMode .RAW_P2PK :
275
292
self .sign_tx (tx )
@@ -283,8 +300,9 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), utxo_to_spend=None,
283
300
tx_hex = tx .serialize ().hex ()
284
301
285
302
assert_equal (tx .get_vsize (), vsize )
303
+ new_utxo = self ._create_utxo (txid = tx .rehash (), vout = 0 , value = send_value , height = 0 )
286
304
287
- return {' txid' : tx . rehash (), ' wtxid' : tx .getwtxid (), ' hex' : tx_hex , 'tx' : tx }
305
+ return {" txid" : new_utxo [ "txid" ], " wtxid" : tx .getwtxid (), " hex" : tx_hex , "tx" : tx , "new_utxo" : new_utxo }
288
306
289
307
def sendrawtransaction (self , * , from_node , tx_hex , maxfeerate = 0 , ** kwargs ):
290
308
txid = from_node .sendrawtransaction (hexstring = tx_hex , maxfeerate = maxfeerate , ** kwargs )
0 commit comments