1
1
import asyncio
2
+ import threading
2
3
import copy
3
4
5
+ from typing import Dict , Sequence
4
6
from . import util
5
7
from .logging import Logger
6
8
from .util import log_exceptions
@@ -74,6 +76,8 @@ def __init__(self, wallet):
74
76
self .wallet = wallet
75
77
self .config = wallet .config
76
78
self .db = wallet .db
79
+ self .lock = threading .Lock ()
80
+ # fixme: not robust to client restart, because we do not persist batch_payments
77
81
self .batch_payments = [] # list of payments we need to make
78
82
self .batch_inputs = {} # list of inputs we need to sweep
79
83
@@ -96,7 +100,8 @@ def __init__(self, wallet):
96
100
97
101
def add_batch_payment (self , output : 'PartialTxOutput' ):
98
102
# todo: maybe we should raise NotEnoughFunds here
99
- self .batch_payments .append (output )
103
+ with self .lock :
104
+ self .batch_payments .append (output )
100
105
101
106
def add_sweep_info (self , sweep_info : 'SweepInfo' ):
102
107
txin = sweep_info .txin
@@ -123,7 +128,6 @@ def add_sweep_info(self, sweep_info: 'SweepInfo'):
123
128
base_txin .witness_script = txin .witness_script
124
129
base_txin .script_sig = txin .script_sig
125
130
126
-
127
131
def get_base_tx (self ) -> Optional [Transaction ]:
128
132
if self ._base_tx :
129
133
return self ._base_tx
@@ -140,7 +144,7 @@ def get_base_tx(self) -> Optional[Transaction]:
140
144
base_tx .add_info_from_wallet (self .wallet ) # needed for txid
141
145
return base_tx
142
146
143
- def find_confirmed_base_tx (self ) -> Optional [Transaction ]:
147
+ def _find_confirmed_base_tx (self ) -> Optional [Transaction ]:
144
148
for txid in self .batch_txids :
145
149
tx_mined_status = self .wallet .adb .get_tx_height (txid )
146
150
if tx_mined_status .conf > 0 :
@@ -149,8 +153,7 @@ def find_confirmed_base_tx(self) -> Optional[Transaction]:
149
153
tx .add_info_from_wallet (self .wallet ) # needed for txid
150
154
return tx
151
155
152
- def to_pay_after (self , tx ):
153
- # fixme: not robust to client restart, because we do not persist batch_payments
156
+ def _to_pay_after (self , tx ) -> Sequence [PartialTxOutput ]:
154
157
if not tx :
155
158
return self .batch_payments
156
159
to_pay = []
@@ -162,7 +165,7 @@ def to_pay_after(self, tx):
162
165
outputs .remove (x )
163
166
return to_pay
164
167
165
- def to_sweep_after (self , tx ):
168
+ def _to_sweep_after (self , tx ) -> Dict [ str , SweepInfo ] :
166
169
tx_prevouts = set (txin .prevout for txin in tx .inputs ()) if tx else set ()
167
170
result = []
168
171
for k ,v in self .batch_inputs .items ():
@@ -179,7 +182,7 @@ def to_sweep_after(self, tx):
179
182
result .append ((k ,v ))
180
183
return dict (result )
181
184
182
- def should_bump_fee (self , base_tx ):
185
+ def _should_bump_fee (self , base_tx ) -> bool :
183
186
if base_tx is None :
184
187
return False
185
188
base_tx_fee = base_tx .get_fee ()
@@ -196,30 +199,30 @@ async def run(self):
196
199
password = self .wallet .get_unlocked_password ()
197
200
if self .wallet .has_keystore_encryption () and not password :
198
201
continue
199
- await self .maybe_broadcast_legacy_htlc_txs ()
200
- tx = self .find_confirmed_base_tx ()
202
+ await self ._maybe_broadcast_legacy_htlc_txs ()
203
+ tx = self ._find_confirmed_base_tx ()
201
204
if tx :
202
205
self .logger .info (f'base tx confirmed { tx .txid ()} ' )
203
- self .clear_batch_processing (tx )
204
- self .start_new_batch (tx )
206
+ self ._clear_batch_processing (tx )
207
+ self ._start_new_batch (tx )
205
208
base_tx = self .get_base_tx ()
206
- to_pay = self .to_pay_after (base_tx )
207
- to_sweep = self .to_sweep_after (base_tx )
209
+ to_pay = self ._to_pay_after (base_tx )
210
+ to_sweep = self ._to_sweep_after (base_tx )
208
211
to_sweep_now = {}
209
212
for k , v in to_sweep .items ():
210
- can_broadcast , wanted_height = self .can_broadcast (v , base_tx )
213
+ can_broadcast , wanted_height = self ._can_broadcast (v , base_tx )
211
214
if can_broadcast :
212
215
to_sweep_now [k ] = v
213
216
else :
214
217
self .wallet .add_future_tx (v , wanted_height )
215
- if not to_pay and not to_sweep_now and not self .should_bump_fee (base_tx ):
218
+ if not to_pay and not to_sweep_now and not self ._should_bump_fee (base_tx ):
216
219
continue
217
220
try :
218
- tx = self .create_batch_tx (base_tx , to_sweep_now , to_pay , password )
221
+ tx = self ._create_batch_tx (base_tx , to_sweep_now , to_pay , password )
219
222
except Exception as e :
220
223
self .logger .exception (f'Cannot create batch transaction: { repr (e )} ' )
221
224
if base_tx :
222
- self .start_new_batch (base_tx )
225
+ self ._start_new_batch (base_tx )
223
226
continue
224
227
await asyncio .sleep (self .RETRY_DELAY )
225
228
continue
@@ -242,7 +245,7 @@ async def run(self):
242
245
self .logger .info (f'starting new batch because could not broadcast' )
243
246
self .start_new_batch (base_tx )
244
247
245
- def create_batch_tx (self , base_tx , to_sweep , to_pay , password ):
248
+ def _create_batch_tx (self , base_tx , to_sweep , to_pay , password ):
246
249
self .logger .info (f'to_sweep: { list (to_sweep .keys ())} ' )
247
250
self .logger .info (f'to_pay: { to_pay } ' )
248
251
inputs = []
@@ -260,7 +263,6 @@ def create_batch_tx(self, base_tx, to_sweep, to_pay, password):
260
263
self .logger .info (f'locktime: { locktime } ' )
261
264
outputs += to_pay
262
265
inputs += self .get_change_inputs (self ._parent_tx ) if self ._parent_tx else []
263
-
264
266
tx = self .wallet .create_transaction (
265
267
base_tx = base_tx ,
266
268
inputs = inputs ,
@@ -275,18 +277,18 @@ def create_batch_tx(self, base_tx, to_sweep, to_pay, password):
275
277
assert tx .is_complete ()
276
278
return tx
277
279
278
- def clear_batch_processing (self , tx ):
280
+ def _clear_batch_processing (self , tx ):
279
281
# this ensure that we can accept an input again
280
282
# if the spending tx is removed from the blockchain
281
283
# fixme: what if there are several batches?
282
284
for txin in tx .inputs ():
283
285
if txin .prevout in self .batch_processing :
284
286
self .batch_processing .remove (txin .prevout )
285
287
286
- def start_new_batch (self , tx ):
288
+ def _start_new_batch (self , tx ):
287
289
use_change = tx and tx .has_change () and any ([txout in self .batch_payments for txout in tx .outputs ()])
288
- self .batch_payments = self .to_pay_after (tx )
289
- self .batch_inputs = self .to_sweep_after (tx )
290
+ self .batch_payments = self ._to_pay_after (tx )
291
+ self .batch_inputs = self ._to_sweep_after (tx )
290
292
self .batch_txids .clear ()
291
293
self ._base_tx = None
292
294
self ._parent_tx = tx if use_change else None
@@ -301,7 +303,7 @@ def get_change_inputs(self, parent_tx):
301
303
txin .nsequence = 0xffffffff - 2
302
304
return inputs
303
305
304
- def can_broadcast (self , sweep_info : 'SweepInfo' , base_tx ):
306
+ def _can_broadcast (self , sweep_info : 'SweepInfo' , base_tx ):
305
307
prevout = sweep_info .txin .prevout .to_str ()
306
308
name = sweep_info .name
307
309
prev_txid , index = prevout .split (':' )
@@ -331,7 +333,7 @@ def can_broadcast(self, sweep_info: 'SweepInfo', base_tx):
331
333
wanted_height = prev_height
332
334
return can_broadcast , wanted_height
333
335
334
- async def maybe_broadcast_legacy_htlc_txs (self ):
336
+ async def _maybe_broadcast_legacy_htlc_txs (self ):
335
337
""" pre-anchor htlc txs cannot be batched """
336
338
for sweep_info in list (self .batch_inputs .values ()):
337
339
if sweep_info .name == 'first-stage-htlc' :
0 commit comments