diff --git a/x/evm/keeper/attest.go b/x/evm/keeper/attest.go index 000ac6d2..18677000 100644 --- a/x/evm/keeper/attest.go +++ b/x/evm/keeper/attest.go @@ -45,7 +45,8 @@ func (k Keeper) attestMessageWrapper(ctx context.Context, q consensus.Queuer, ms defer func() { // If there is no error, or we can't verify this transaction, we flush // the context, so we never see it again - if retErr == nil || errors.Is(retErr, types.ErrEthTxNotVerified) { + if retErr == nil || errors.Is(retErr, types.ErrEthTxNotVerified) || + errors.Is(retErr, types.ErrEthTxFailed) { writeCache() } }() @@ -122,6 +123,25 @@ func (k Keeper) routerAttester(sdkCtx sdk.Context, q consensus.Queuer, msg conse rawEvidence: winner, msg: message, } + + switch winner := winner.(type) { + case *types.TxExecutedProof: + // If we have proof of a transaction, we need to check the status on + // the receipt + receipt, err := winner.GetReceipt() + if err != nil { + logger.WithFields("message-id", msg.GetId()). + WithError(err).Error("Failed to get transaction receipt") + return err + } + + if receipt.Status != ethtypes.ReceiptStatusSuccessful { + logger.WithFields("message-id", msg.GetId(), "status", receipt.Status). + Warn("Transaction execution failed") + return types.ErrEthTxFailed + } + } + switch rawAction.(type) { case *types.Message_UploadSmartContract: return newUploadSmartContractAttester(&k, logger, params).Execute(sdkCtx) diff --git a/x/evm/keeper/attest_compass_handover_test.go b/x/evm/keeper/attest_compass_handover_test.go index 0d71cc69..444d99b2 100644 --- a/x/evm/keeper/attest_compass_handover_test.go +++ b/x/evm/keeper/attest_compass_handover_test.go @@ -162,6 +162,7 @@ var _ = Describe("attest compass handover", func() { Expect(err).To(BeNil()) receipt := ethcoretypes.Receipt{ + Status: ethcoretypes.ReceiptStatusSuccessful, Logs: []*ethcoretypes.Log{ { Topics: []common.Hash{contractDeployedEvent}, diff --git a/x/evm/keeper/attest_submit_logic_call.go b/x/evm/keeper/attest_submit_logic_call.go index c04ec127..ca25f393 100644 --- a/x/evm/keeper/attest_submit_logic_call.go +++ b/x/evm/keeper/attest_submit_logic_call.go @@ -65,6 +65,8 @@ func (a *submitLogicCallAttester) attemptRetry(ctx sdk.Context, proof *types.Sma slc := a.action if slc.Retries < cMaxSubmitLogicCallRetries { slc.Retries++ + // We must clear fees before retry or the signature verification fails + slc.Fees = nil a.logger.Info("retrying failed SubmitLogicCall message", "message-id", a.msgID, "retries", slc.Retries, diff --git a/x/evm/keeper/attest_test.go b/x/evm/keeper/attest_test.go index 3a59240d..dcbaf80d 100644 --- a/x/evm/keeper/attest_test.go +++ b/x/evm/keeper/attest_test.go @@ -49,6 +49,10 @@ var ( valsetTx1 = ethcoretypes.NewTx(ðcoretypes.DynamicFeeTx{ Data: common.FromHex(string(whoops.Must(os.ReadFile("testdata/valset-tx-data.hex")))), }) + + serializedReceipt, _ = (ðcoretypes.Receipt{ + Status: ethcoretypes.ReceiptStatusSuccessful, + }).MarshalBinary() ) type record struct { @@ -359,7 +363,10 @@ var _ = g.Describe("attest router", func() { g.When("message is SubmitLogicCall", func() { g.BeforeEach(func() { execTx = slcTx1 - proof := whoops.Must(codectypes.NewAnyWithValue(&types.TxExecutedProof{SerializedTX: whoops.Must(execTx.MarshalBinary())})) + proof := whoops.Must(codectypes.NewAnyWithValue(&types.TxExecutedProof{ + SerializedTX: whoops.Must(execTx.MarshalBinary()), + SerializedReceipt: serializedReceipt, + })) evidence = []*consensustypes.Evidence{ { ValAddress: sdk.ValAddress("validator-1"), @@ -445,7 +452,10 @@ var _ = g.Describe("attest router", func() { g.When("message is UpdateValset", func() { g.BeforeEach(func() { execTx = valsetTx1 - proof := whoops.Must(codectypes.NewAnyWithValue(&types.TxExecutedProof{SerializedTX: whoops.Must(execTx.MarshalBinary())})) + proof := whoops.Must(codectypes.NewAnyWithValue(&types.TxExecutedProof{ + SerializedTX: whoops.Must(execTx.MarshalBinary()), + SerializedReceipt: serializedReceipt, + })) evidence = []*consensustypes.Evidence{ { ValAddress: sdk.ValAddress("validator-1"), @@ -494,7 +504,10 @@ var _ = g.Describe("attest router", func() { g.When("message is UploadSmartContract", func() { g.BeforeEach(func() { execTx = uscTx1 - proof := whoops.Must(codectypes.NewAnyWithValue(&types.TxExecutedProof{SerializedTX: whoops.Must(execTx.MarshalBinary())})) + proof := whoops.Must(codectypes.NewAnyWithValue(&types.TxExecutedProof{ + SerializedTX: whoops.Must(execTx.MarshalBinary()), + SerializedReceipt: serializedReceipt, + })) evidence = []*consensustypes.Evidence{ { ValAddress: sdk.ValAddress("validator-1"), diff --git a/x/evm/keeper/attest_upload_user_smart_contract.go b/x/evm/keeper/attest_upload_user_smart_contract.go index b769d8b1..786081c2 100644 --- a/x/evm/keeper/attest_upload_user_smart_contract.go +++ b/x/evm/keeper/attest_upload_user_smart_contract.go @@ -147,6 +147,8 @@ func (a *uploadUserSmartContractAttester) attemptRetry(ctx sdk.Context) { } a.action.Retries++ + // We must clear fees before retry or the signature verification fails + a.action.Fees = nil a.logger.Info("Retrying failed UploadUserSmartContract message", "message-id", a.msgID, diff --git a/x/evm/keeper/attest_upload_user_smart_contract_test.go b/x/evm/keeper/attest_upload_user_smart_contract_test.go index 12eb73ed..f7e0c7b4 100644 --- a/x/evm/keeper/attest_upload_user_smart_contract_test.go +++ b/x/evm/keeper/attest_upload_user_smart_contract_test.go @@ -162,6 +162,7 @@ var _ = Describe("attest upload user smart contract", func() { Expect(err).To(BeNil()) receipt := ethcoretypes.Receipt{ + Status: ethcoretypes.ReceiptStatusSuccessful, Logs: []*ethcoretypes.Log{ { Topics: []common.Hash{contractDeployedEvent}, diff --git a/x/evm/types/errors.go b/x/evm/types/errors.go index 0274a699..ffa46a1b 100644 --- a/x/evm/types/errors.go +++ b/x/evm/types/errors.go @@ -15,5 +15,6 @@ var ( var ( ErrEthTxNotVerified = whoops.String("transaction not verified") + ErrEthTxFailed = whoops.String("transaction failed to execute") ErrInvalidBalance = whoops.Errorf("invalid balance: %s") )