@@ -7,12 +7,14 @@ import (
7
7
"fmt"
8
8
"log/slog"
9
9
10
+ "github.com/babylonchain/babylon/types"
10
11
"github.com/babylonchain/cli-tools/internal/btcclient"
11
12
"github.com/babylonchain/cli-tools/internal/config"
12
13
"github.com/babylonchain/cli-tools/internal/db"
13
14
"github.com/btcsuite/btcd/btcec/v2"
14
15
"github.com/btcsuite/btcd/btcec/v2/schnorr"
15
16
"github.com/btcsuite/btcd/chaincfg"
17
+ "github.com/btcsuite/btcd/chaincfg/chainhash"
16
18
"github.com/btcsuite/btcd/wire"
17
19
"github.com/prometheus/client_golang/prometheus/push"
18
20
)
@@ -22,11 +24,26 @@ var (
22
24
// code, or we allowed some invalid data into database.
23
25
// When this happend we stop processing pipeline and return immediately, without
24
26
// changing status of any unbonding transaction.
25
- ErrCriticalError = fmt .Errorf ("critical error encountered " )
27
+ ErrCriticalError = fmt .Errorf ("critical error" )
26
28
)
27
29
28
- func wrapCrititical (err error ) error {
29
- return fmt .Errorf ("%s:%w" , err .Error (), ErrCriticalError )
30
+ func mustBtcTxToHex (tx * wire.MsgTx ) string {
31
+ bytes , err := types .SerializeBTCTx (tx )
32
+ if err != nil {
33
+ panic (err )
34
+ }
35
+ return hex .EncodeToString (bytes )
36
+ }
37
+
38
+ func wrapCrititical (
39
+ stakingTx * wire.MsgTx ,
40
+ stakingTxHash * chainhash.Hash ,
41
+ err error ,
42
+ ) error {
43
+ stakingTxHex := mustBtcTxToHex (stakingTx )
44
+ return fmt .Errorf (
45
+ "err: %s, staking_tx_haxh:%s, staking_tx:%s: %w" , err .Error (), stakingTxHash .String (), stakingTxHex , ErrCriticalError ,
46
+ )
30
47
}
31
48
32
49
func pubKeyToStringSchnorr (pubKey * btcec.PublicKey ) string {
@@ -247,6 +264,7 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
247
264
utx := tx
248
265
249
266
stakingOutputFromDb := utx .StakingOutput ()
267
+
250
268
stakingTxHash := utx .UnbondingTransaction .TxIn [0 ].PreviousOutPoint .Hash
251
269
252
270
stakingTxInfo , err := up .sender .TxByHash (
@@ -257,14 +275,22 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
257
275
// if the staking transaction is not found in btc chain, it means something is wrong
258
276
// as staking service should not allow to create unbonding transaction without staking transaction
259
277
if err != nil {
260
- return wrapCrititical (err )
278
+ return wrapCrititical (
279
+ utx .StakingTransactionData .StakingTransaction ,
280
+ & stakingTxHash ,
281
+ err ,
282
+ )
261
283
}
262
284
263
285
params , err := up .retriever .ParamsByHeight (ctx , uint64 (stakingTxInfo .TxInclusionHeight ))
264
286
265
287
// we should always be able to retrieve params for the height of the staking transaction
266
288
if err != nil {
267
- return wrapCrititical (err )
289
+ return wrapCrititical (
290
+ utx .StakingTransactionData .StakingTransaction ,
291
+ & stakingTxHash ,
292
+ err ,
293
+ )
268
294
}
269
295
270
296
stakingOutputRecovered , unbondingPathSpendInfo , err := CreateUnbondingPathSpendInfo (
@@ -274,7 +300,11 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
274
300
)
275
301
276
302
if err != nil {
277
- return wrapCrititical (err )
303
+ return wrapCrititical (
304
+ utx .StakingTransactionData .StakingTransaction ,
305
+ & stakingTxHash ,
306
+ err ,
307
+ )
278
308
}
279
309
280
310
// This the last line check before sending unbonding transaction for signing. It checks
@@ -285,7 +315,11 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
285
315
// - pipeline is run on bad BTC network
286
316
// - stakingApi service has a bug
287
317
if ! outputsAreEqual (stakingOutputRecovered , stakingOutputFromDb ) {
288
- return wrapCrititical (fmt .Errorf ("staking output from staking tx and staking output re-build from params are different" ))
318
+ return wrapCrititical (
319
+ utx .StakingTransactionData .StakingTransaction ,
320
+ & stakingTxHash ,
321
+ fmt .Errorf ("staking output from staking tx and staking output re-build from params are different" ),
322
+ )
289
323
}
290
324
291
325
sigs , err := up .signUnbondingTransaction (
@@ -297,7 +331,26 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
297
331
)
298
332
299
333
if err != nil {
300
- return wrapCrititical (err )
334
+ up .Metrics .RecordFailedCovenantQuorum ()
335
+
336
+ unbondingTxHex := mustBtcTxToHex (utx .UnbondingTransaction )
337
+
338
+ up .logger .Error ("Failed to get quorum of covenant signatures to unbond" ,
339
+ "staking_tx_hash" , tx .StakingTransactionData .StakingTransaction .TxHash ().String (),
340
+ "unbonding_tx_hash" , tx .UnbondingTransactionHash .String (),
341
+ "unbonding_tx" , unbondingTxHex ,
342
+ )
343
+
344
+ // Note that we failed to get signatures from covenant members
345
+ if err := up .store .SetUnbondingTransactionFailedToGetCovenantSignatures (ctx , utx ); err != nil {
346
+ return wrapCrititical (
347
+ utx .StakingTransactionData .StakingTransaction ,
348
+ & stakingTxHash ,
349
+ err ,
350
+ )
351
+ }
352
+
353
+ continue
301
354
}
302
355
303
356
up .logger .Info ("Successfully collected quorum of covenant signatures to unbond" ,
@@ -315,7 +368,11 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
315
368
)
316
369
317
370
if err != nil {
318
- return wrapCrititical (err )
371
+ return wrapCrititical (
372
+ utx .StakingTransactionData .StakingTransaction ,
373
+ & stakingTxHash ,
374
+ err ,
375
+ )
319
376
}
320
377
321
378
// We assume that this is valid unbodning transaction, with 1 input
@@ -331,7 +388,11 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
331
388
up .logger .Info ("The input of the unbonding transaction has already been spent" ,
332
389
slog .String ("staking_tx_hash" , stakingTxHash .String ()))
333
390
if err := up .store .SetUnbondingTransactionInputAlreadySpent (ctx , utx ); err != nil {
334
- return wrapCrititical (err )
391
+ return wrapCrititical (
392
+ utx .StakingTransactionData .StakingTransaction ,
393
+ & stakingTxHash ,
394
+ err ,
395
+ )
335
396
}
336
397
continue
337
398
}
@@ -341,7 +402,11 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
341
402
if err != nil {
342
403
up .logger .Error ("Failed to send unbonding transaction" , "error" , err )
343
404
if err := up .store .SetUnbondingTransactionProcessingFailed (ctx , utx ); err != nil {
344
- return wrapCrititical (err )
405
+ return wrapCrititical (
406
+ utx .StakingTransactionData .StakingTransaction ,
407
+ & stakingTxHash ,
408
+ err ,
409
+ )
345
410
}
346
411
up .Metrics .RecordFailedUnbodingTransaction ()
347
412
} else {
@@ -350,7 +415,11 @@ func (up *UnbondingPipeline) processUnbondingTransactions(
350
415
slog .String ("tx_hash" , hash .String ()),
351
416
)
352
417
if err := up .store .SetUnbondingTransactionProcessed (ctx , utx ); err != nil {
353
- return wrapCrititical (err )
418
+ return wrapCrititical (
419
+ utx .StakingTransactionData .StakingTransaction ,
420
+ & stakingTxHash ,
421
+ err ,
422
+ )
354
423
}
355
424
up .Metrics .RecordSentUnbondingTransaction ()
356
425
}
@@ -398,12 +467,6 @@ func (up *UnbondingPipeline) ProcessNewTransactions(ctx context.Context) error {
398
467
func (up * UnbondingPipeline ) ProcessFailedTransactions (ctx context.Context ) error {
399
468
up .logger .Info ("Running unbonding pipeline for failed transactions" )
400
469
401
- unbondingTransactions , err := up .store .GetFailedUnbondingTransactions (ctx )
402
-
403
- if err != nil {
404
- return err
405
- }
406
-
407
470
defer func () {
408
471
if up .Metrics .Config .Enabled {
409
472
if err := up .pushMetrics (); err != nil {
@@ -412,9 +475,22 @@ func (up *UnbondingPipeline) ProcessFailedTransactions(ctx context.Context) erro
412
475
}
413
476
}()
414
477
415
- if len (unbondingTransactions ) == 0 {
416
- up .logger .Info ("No failed unbonding transactions to process" )
417
- return nil
478
+ // 1. First process transactions that failed to get quorum of covenant signatures
479
+ unbondingTxWithNoQuorum , err := up .store .GetUnbondingTransactionsWithNoQuorum (ctx )
480
+
481
+ if err != nil {
482
+ return err
483
+ }
484
+
485
+ if err := up .processUnbondingTransactions (ctx , unbondingTxWithNoQuorum ); err != nil {
486
+ return err
487
+ }
488
+
489
+ // 2. Second process other failed unbonding transactions
490
+ unbondingTransactions , err := up .store .GetFailedUnbondingTransactions (ctx )
491
+
492
+ if err != nil {
493
+ return err
418
494
}
419
495
420
496
if err := up .processUnbondingTransactions (ctx , unbondingTransactions ); err != nil {
0 commit comments