-
Notifications
You must be signed in to change notification settings - Fork 20
Upgrade error handler #408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,8 +7,10 @@ import ( | |
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| "go.uber.org/zap" | ||
|
|
||
| "github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
| "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" | ||
| "github.com/smartcontractkit/chainlink-evm/pkg/assets" | ||
| "github.com/smartcontractkit/chainlink-evm/pkg/gas" | ||
| "github.com/smartcontractkit/chainlink-evm/pkg/testutils" | ||
|
|
@@ -21,6 +23,7 @@ func TestMetaErrorHandler(t *testing.T) { | |
| require.NotNil(t, errorHandler) | ||
|
|
||
| t.Run("handles no bids error for first attempt", func(t *testing.T) { | ||
| lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) | ||
| nonce := uint64(1) | ||
| address := testutils.NewAddress() | ||
| txRequest := &types.TxRequest{ | ||
|
|
@@ -29,7 +32,7 @@ func TestMetaErrorHandler(t *testing.T) { | |
| ToAddress: testutils.NewAddress(), | ||
| } | ||
| setNonce := func(address common.Address, nonce uint64) {} | ||
| txStoreManager := storage.NewInMemoryStoreManager(logger.Test(t), testutils.FixtureChainID) | ||
| txStoreManager := storage.NewInMemoryStoreManager(lggr, testutils.FixtureChainID) | ||
| require.NoError(t, txStoreManager.Add(address)) | ||
| txStore := txStoreManager.InMemoryStoreMap[address] | ||
| _ = txStore.CreateTransaction(txRequest) | ||
|
|
@@ -44,9 +47,10 @@ func TestMetaErrorHandler(t *testing.T) { | |
| _, err = txStore.AppendAttemptToTransaction(*tx.Nonce, attempt) | ||
| require.NoError(t, err) | ||
| tx, _ = txStore.FetchUnconfirmedTransactionAtNonceWithCount(nonce) | ||
| err = errorHandler.HandleError(t.Context(), tx, ErrNoBids, txStoreManager, setNonce, false) | ||
| require.Error(t, err) | ||
| require.ErrorContains(t, err, "transaction with txID: 0 marked as fatal") | ||
| noTransmission, err := errorHandler.HandleError(t.Context(), lggr, tx, ErrNoBids, txStoreManager, setNonce) | ||
| require.NoError(t, err) | ||
| require.True(t, noTransmission) | ||
| tests.AssertLogEventually(t, observedLogs, "transaction marked as fatal") | ||
| _, unconfirmedCount := txStore.FetchUnconfirmedTransactionAtNonceWithCount(nonce) | ||
| assert.Equal(t, 0, unconfirmedCount) | ||
| }) | ||
|
|
@@ -78,14 +82,15 @@ func TestMetaErrorHandler(t *testing.T) { | |
| _, err = txStore.AppendAttemptToTransaction(*tx.Nonce, attempt) | ||
| require.NoError(t, err) | ||
| tx, _ = txStore.FetchUnconfirmedTransactionAtNonceWithCount(nonce) | ||
| err = errorHandler.HandleError(t.Context(), tx, txErr, txStoreManager, setNonce, false) | ||
| require.Error(t, err) | ||
| require.ErrorIs(t, err, txErr) | ||
| noTransmission, err := errorHandler.HandleError(t.Context(), logger.Test(t), tx, txErr, txStoreManager, setNonce) | ||
| require.NoError(t, err) | ||
| require.False(t, noTransmission) | ||
| _, unconfirmedCount := txStore.FetchUnconfirmedTransactionAtNonceWithCount(nonce) | ||
|
Comment on lines
+85
to
88
|
||
| assert.Equal(t, 1, unconfirmedCount) | ||
| }) | ||
|
|
||
| t.Run("handles auction error for first attempt", func(t *testing.T) { | ||
| lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) | ||
| nonce := uint64(1) | ||
| address := testutils.NewAddress() | ||
| txRequest := &types.TxRequest{ | ||
|
|
@@ -95,7 +100,7 @@ func TestMetaErrorHandler(t *testing.T) { | |
| } | ||
| txErr := ErrAuction | ||
| setNonce := func(address common.Address, nonce uint64) {} | ||
| txStoreManager := storage.NewInMemoryStoreManager(logger.Test(t), testutils.FixtureChainID) | ||
| txStoreManager := storage.NewInMemoryStoreManager(lggr, testutils.FixtureChainID) | ||
| require.NoError(t, txStoreManager.Add(address)) | ||
| txStore := txStoreManager.InMemoryStoreMap[address] | ||
| _ = txStore.CreateTransaction(txRequest) | ||
|
|
@@ -110,9 +115,10 @@ func TestMetaErrorHandler(t *testing.T) { | |
| _, err = txStore.AppendAttemptToTransaction(*tx.Nonce, attempt) | ||
| require.NoError(t, err) | ||
| tx, _ = txStore.FetchUnconfirmedTransactionAtNonceWithCount(nonce) | ||
| err = errorHandler.HandleError(t.Context(), tx, txErr, txStoreManager, setNonce, false) | ||
| require.Error(t, err) | ||
| require.ErrorContains(t, err, "transaction with txID: 0 marked as fatal") | ||
| noTransmission, err := errorHandler.HandleError(t.Context(), lggr, tx, txErr, txStoreManager, setNonce) | ||
| require.NoError(t, err) | ||
| require.True(t, noTransmission) | ||
| tests.AssertLogEventually(t, observedLogs, "transaction marked as fatal") | ||
| _, unconfirmedCount := txStore.FetchUnconfirmedTransactionAtNonceWithCount(nonce) | ||
| assert.Equal(t, 0, unconfirmedCount) | ||
| }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,7 +57,9 @@ type AttemptBuilder interface { | |
| } | ||
|
|
||
| type ErrorHandler interface { | ||
| HandleError(context.Context, *types.Transaction, error, TxStore, func(common.Address, uint64), bool) (err error) | ||
| // HandleError tries to decide if there was a successful transmission by parsing the error message. If it can't decide, it returns control to | ||
| // the standard execution path. | ||
| HandleError(context.Context, logger.Logger, *types.Transaction, error, TxStore, func(common.Address, uint64)) (noTransmission bool, err error) | ||
| } | ||
|
|
||
| type StuckTxDetector interface { | ||
|
|
@@ -337,11 +339,17 @@ func (t *Txm) sendTransactionWithError(ctx context.Context, tx *types.Transactio | |
| start := time.Now() | ||
| txErr := t.client.SendTransaction(ctx, tx, attempt) | ||
| t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "transactionLifecycleID", tx.GetTransactionLifecycleID(t.lggr), "duration", time.Since(start), "txErr", txErr) | ||
| if txErr != nil && t.errorHandler != nil { | ||
| if err = t.errorHandler.HandleError(ctx, tx, txErr, t.txStore, t.SetNonce, false); err != nil { | ||
| return | ||
| if txErr != nil { | ||
| // If ErrorHandler is set, try to decide if there was a successful transmission by parsing the error message. If there wasn't, we return early. | ||
| if t.errorHandler != nil { | ||
| noTransmission, hErr := t.errorHandler.HandleError(ctx, t.lggr, tx, txErr, t.txStore, t.SetNonce) | ||
| if hErr != nil { | ||
| return hErr | ||
| } | ||
| if noTransmission { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be helpful to add a debug log here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now the only ErrorHandler we have implemented is the MetaErrorHandler which already logs a message if |
||
| return nil | ||
| } | ||
| } | ||
| } else if txErr != nil { | ||
| pendingNonce, pErr := t.client.PendingNonceAt(ctx, fromAddress) | ||
| if pErr != nil { | ||
| return pErr | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: I somewhat dislike negations in var names, since it will lead to double negations, which can lead to confusion.
Maybe flip it around and use something like
transmissionHappened?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable to me, but the issue with this particular case is that there is guarantee a transmission happened and we can only be sure about the
noTransmission. If we flip the name totransmissionHappenedortransmissionMayHappenedand we check for the inversed result, the reader might assume that the error handler can deterministically returned if a transaction was broadcasted. It feels likenoTransmissionprovides better conviction as to what happened.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, so otherwise it would be
transmissionMightHaveHappened, which is not great 😅Let's leave it like this then. Might be worthwhile to add a comment to the code about that we can only be 100% sure a transmission has not happened
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I included one on the interface level here: https://github.com/smartcontractkit/chainlink-evm/pull/408/changes#diff-b9fe8b4b005efc03661d7777aebac2846a0ff01a18ffff7d1cb715d6766736f3R60-R61
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!