sweep: add new state TxFatal for erroneous sweepings
Also updated the loggings. This new state will be used in the following commit.
This commit is contained in:
1 parent
1dfb5a0c20
commit
6c2e8b9a00
3 files changed
+67
-15
No files matched your search
+43
-14
@@ -84,6 +84,11 @@ const (
|
||||
// TxConfirmed is sent when the tx is confirmed.
|
||||
TxConfirmed
|
||||
|
||||
// TxFatal is sent when the inputs in this tx cannot be retried. Txns
|
||||
// will end up in this state if they have encountered a non-fee related
|
||||
// error, which means they cannot be retried with increased budget.
|
||||
TxFatal
|
||||
|
||||
// sentinalEvent is used to check if an event is unknown.
|
||||
sentinalEvent
|
||||
)
|
||||
@@ -99,6 +104,8 @@ func (e BumpEvent) String() string {
|
||||
return "Replaced"
|
||||
case TxConfirmed:
|
||||
return "Confirmed"
|
||||
case TxFatal:
|
||||
return "Fatal"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
@@ -246,10 +253,20 @@ type BumpResult struct {
|
||||
requestID uint64
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the result.
|
||||
func (b *BumpResult) String() string {
|
||||
desc := fmt.Sprintf("Event=%v", b.Event)
|
||||
if b.Tx != nil {
|
||||
desc += fmt.Sprintf(", Tx=%v", b.Tx.TxHash())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%s]", desc)
|
||||
}
|
||||
|
||||
// Validate validates the BumpResult so it's safe to use.
|
||||
func (b *BumpResult) Validate() error {
|
||||
// Every result must have a tx.
|
||||
if b.Tx == nil {
|
||||
// Every result must have a tx except the fatal or failed case.
|
||||
if b.Tx == nil && b.Event != TxFatal {
|
||||
return fmt.Errorf("%w: nil tx", ErrInvalidBumpResult)
|
||||
}
|
||||
|
||||
@@ -263,10 +280,12 @@ func (b *BumpResult) Validate() error {
|
||||
return fmt.Errorf("%w: nil replacing tx", ErrInvalidBumpResult)
|
||||
}
|
||||
|
||||
// If it's a failed event, it must have an error.
|
||||
if b.Event == TxFailed && b.Err == nil {
|
||||
// If it's a failed or fatal event, it must have an error.
|
||||
if b.Event == TxFatal || b.Event == TxFailed {
|
||||
if b.Err == nil {
|
||||
return fmt.Errorf("%w: nil error", ErrInvalidBumpResult)
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a confirmed event, it must have a fee rate and fee.
|
||||
if b.Event == TxConfirmed && (b.FeeRate == 0 || b.Fee == 0) {
|
||||
@@ -659,8 +678,7 @@ func (t *TxPublisher) notifyResult(result *BumpResult) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("Sending result for requestID=%v, tx=%v", id,
|
||||
result.Tx.TxHash())
|
||||
log.Debugf("Sending result %v for requestID=%v", result, id)
|
||||
|
||||
select {
|
||||
// Send the result to the subscriber.
|
||||
@@ -678,20 +696,31 @@ func (t *TxPublisher) notifyResult(result *BumpResult) {
|
||||
func (t *TxPublisher) removeResult(result *BumpResult) {
|
||||
id := result.requestID
|
||||
|
||||
// Remove the record from the maps if there's an error. This means this
|
||||
// tx has failed its broadcast and cannot be retried. There are two
|
||||
// cases,
|
||||
// - when the budget cannot cover the fee.
|
||||
// - when a non-RBF related error occurs.
|
||||
var txid chainhash.Hash
|
||||
if result.Tx != nil {
|
||||
txid = result.Tx.TxHash()
|
||||
}
|
||||
|
||||
// Remove the record from the maps if there's an error or the tx is
|
||||
// confirmed. When there's an error, it means this tx has failed its
|
||||
// broadcast and cannot be retried. There are two cases it may fail,
|
||||
// - when the budget cannot cover the increased fee calculated by the
|
||||
// fee function, hence the budget is used up.
|
||||
// - when a non-fee related error returned from PublishTransaction.
|
||||
switch result.Event {
|
||||
case TxFailed:
|
||||
log.Errorf("Removing monitor record=%v, tx=%v, due to err: %v",
|
||||
id, result.Tx.TxHash(), result.Err)
|
||||
id, txid, result.Err)
|
||||
|
||||
case TxConfirmed:
|
||||
// Remove the record is the tx is confirmed.
|
||||
// Remove the record if the tx is confirmed.
|
||||
log.Debugf("Removing confirmed monitor record=%v, tx=%v", id,
|
||||
result.Tx.TxHash())
|
||||
txid)
|
||||
|
||||
case TxFatal:
|
||||
// Remove the record if there's an error.
|
||||
log.Debugf("Removing monitor record=%v due to fatal err: %v",
|
||||
id, result.Err)
|
||||
|
||||
// Do nothing if it's neither failed or confirmed.
|
||||
default:
|
||||
|
||||
@@ -91,6 +91,19 @@ func TestBumpResultValidate(t *testing.T) {
|
||||
}
|
||||
require.ErrorIs(t, b.Validate(), ErrInvalidBumpResult)
|
||||
|
||||
// A failed event without a tx will give an error.
|
||||
b = BumpResult{
|
||||
Event: TxFailed,
|
||||
Err: errDummy,
|
||||
}
|
||||
require.ErrorIs(t, b.Validate(), ErrInvalidBumpResult)
|
||||
|
||||
// A fatal event without a failure reason will give an error.
|
||||
b = BumpResult{
|
||||
Event: TxFailed,
|
||||
}
|
||||
require.ErrorIs(t, b.Validate(), ErrInvalidBumpResult)
|
||||
|
||||
// A confirmed event without fee info will give an error.
|
||||
b = BumpResult{
|
||||
Tx: &wire.MsgTx{},
|
||||
@@ -104,6 +117,13 @@ func TestBumpResultValidate(t *testing.T) {
|
||||
Event: TxPublished,
|
||||
}
|
||||
require.NoError(t, b.Validate())
|
||||
|
||||
// Tx is allowed to be nil in a TxFatal event.
|
||||
b = BumpResult{
|
||||
Event: TxFatal,
|
||||
Err: errDummy,
|
||||
}
|
||||
require.NoError(t, b.Validate())
|
||||
}
|
||||
|
||||
// TestCalcSweepTxWeight checks that the weight of the sweep tx is calculated
|
||||
|
||||
+4
-1
@@ -1729,7 +1729,7 @@ func (s *UtxoSweeper) handleBumpEventTxPublished(r *BumpResult) error {
|
||||
// NOTE: TxConfirmed event is not handled, since we already subscribe to the
|
||||
// input's spending event, we don't need to do anything here.
|
||||
func (s *UtxoSweeper) handleBumpEvent(r *BumpResult) error {
|
||||
log.Debugf("Received bump event [%v] for tx %v", r.Event, r.Tx.TxHash())
|
||||
log.Debugf("Received bump result %v", r)
|
||||
|
||||
switch r.Event {
|
||||
// The tx has been published, we update the inputs' state and create a
|
||||
@@ -1745,6 +1745,9 @@ func (s *UtxoSweeper) handleBumpEvent(r *BumpResult) error {
|
||||
// with the new one.
|
||||
case TxReplaced:
|
||||
return s.handleBumpEventTxReplaced(r)
|
||||
|
||||
case TxFatal:
|
||||
// TODO(yy): create a method to remove this input.
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in new issue
Block a user