Skip to content
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

Check receipt of sent transaction #323

Merged
merged 11 commits into from
Jun 11, 2024
Merged

Check receipt of sent transaction #323

merged 11 commits into from
Jun 11, 2024

Conversation

cam-schultz
Copy link
Collaborator

@cam-schultz cam-schultz commented Jun 6, 2024

Why this should be merged

Fixes #321
Fixes #236

Modifies the retry mechanism introduced in #317 to retry a generic function in a loop. It runs until the context provided by the caller is cancelled, or the call is successful. The utility uses the same retry pattern implemented here: https://github.com/ava-labs/teleporter/blob/main/tests/utils/utils.go#L519

How this works

The MessageHandler.SendMessage method waits until the API node returns the transaction receipt. It returns an error if either the receipt is not found within a time bound, or the receipt status is not successful. That error will be propagated to ApplicationRelayer.ProcessHeight, causing the height to not be committed to the database. The error will also be propagated to the Listener, which will mark the relayer as unhealthy.

Also cleans up some of the awm-relayer/ethclient usage. That package really only contains a custom subnet-evm/ethclient constructor that enables query parameters and http headers. Wrapping the subnet-evm type was overkill.

How this was tested

CI. In the AllowedAddresses E2E test, the wait is increased before killing the relayer application to allow time for receipt fetching + the database write interval.

How is this documented

N/A

utils/utils.go Outdated Show resolved Hide resolved
vms/destination_client.go Show resolved Hide resolved
@cam-schultz cam-schultz requested a review from geoff-vball June 7, 2024 14:51
geoff-vball
geoff-vball previously approved these changes Jun 7, 2024
@@ -15,10 +15,8 @@ import (

var ErrInvalidEndpoint = errors.New("invalid rpc endpoint")

type Client ethclient.Client
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're removing the Client type here, should we rename the package/file?

I think would better belong under utils now, since it is just a utility for creating a subnet-evm/ethclient instance with headers/query params.

Comment on lines 244 to 272
// Wait for the message to be included in a block before returning
callCtx, callCtxCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer callCtxCancel()
receipt, err := utils.CallWithRetry[*types.Receipt](
callCtx,
func() (*types.Receipt, error) {
return destinationClient.Client().(ethclient.Client).TransactionReceipt(callCtx, txHash)
},
)
if err != nil {
m.logger.Error(
"Failed to get transaction receipt",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
zap.Error(err),
)
return err
}
if receipt.Status != types.ReceiptStatusSuccessful {
m.logger.Error(
"Transaction failed",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
zap.String("txHash", txHash.String()),
)
return fmt.Errorf("transaction failed with status: %d", receipt.Status)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this section out to a helper? It also is close to duplicating the utility functions we have in Teleporter for use in the E2E such as WaitMined. Would it make sense to export those from a non-test package to use here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic should continue to live here for a couple reasons:

  • Moving WaitMined to a non-test utility in teleporter doesn't make a ton of sense, since there's nothing specific to Teleporter in that function. It's really a utility to support our current test framework
  • awm-relayer has slightly different semantics. In WaitMined, we also wait for receipt's block height to account for differences in network views among nodes behind the public API. This is necessary to avoid races in the tests when run on Fuji. In the relayer, we instead assume that a valid receipt indicates that the tx will eventually be seen by all nodes.

)
return fmt.Errorf("transaction failed with status: %d", receipt.Status)
}

m.logger.Info(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this message be kept directly after the SendTx call? Alternatively, we could re-word it to say "delivered" rather than "sent". Would be great to add the txHash to it now that it's available also.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded to "delivered". This log message is intended to signal successful delivery.

Copy link
Contributor

@bernard-avalabs bernard-avalabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just a minor comment about the relationship between f() and our chosen tick interval.

err error
)
for {
t, err = f()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we state in the comments that we assume the time to call f() is significant less than the ticker delay? This should generally be true unless f() itself blocks or has some sleep logic.

Otherwise the ticker will not have the intended effect, and we should only start the ticker after f() completes in each loop iteration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go's time.Ticker type "will adjust the time interval or drop ticks to make up for slow receivers" (docs). This is implemented using a buffered channel with a single element; if the client falls behind, new ticks are dropped.

I don't think we should make any assumptions on f, especially since f is usually a blocking network request. time.Ticker should account for arbitrarily expensive fs though.

Copy link
Collaborator

@michaelkaplan13 michaelkaplan13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@cam-schultz cam-schultz merged commit 7e18908 into main Jun 11, 2024
7 checks passed
@cam-schultz cam-schultz deleted the check-failed-tx branch June 11, 2024 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Check if transaction was mined Emit error on tx revert
4 participants