Skip to content

Commit

Permalink
Include human-friendly error messages on EVM transaction events
Browse files Browse the repository at this point in the history
  • Loading branch information
m-Peter committed Dec 29, 2024
1 parent 219660a commit 7fb6ff4
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 1 deletion.
12 changes: 11 additions & 1 deletion fvm/evm/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (

"github.com/onflow/cadence"
"github.com/onflow/cadence/encoding/ccf"
"github.com/onflow/go-ethereum/accounts/abi"
gethCommon "github.com/onflow/go-ethereum/common"
gethVM "github.com/onflow/go-ethereum/core/vm"

"github.com/onflow/flow-go/fvm/evm/stdlib"
"github.com/onflow/flow-go/fvm/evm/types"
Expand Down Expand Up @@ -62,13 +64,21 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error

eventType := stdlib.CadenceTypesForChain(chainID).TransactionExecuted

errorMessage := p.Result.ErrorMsg()
if p.Result.ResultSummary().ErrorCode == types.ExecutionErrCodeExecutionReverted {
reason, errUnpack := abi.UnpackRevert(p.Result.ReturnedData)
if errUnpack == nil {
errorMessage = fmt.Sprintf("%v: %v", gethVM.ErrExecutionReverted.Error(), reason)
}
}

return cadence.NewEvent([]cadence.Value{
hashToCadenceArrayValue(p.Result.TxHash),
cadence.NewUInt16(p.Result.Index),
cadence.NewUInt8(p.Result.TxType),
bytesToCadenceUInt8ArrayValue(p.Payload),
cadence.NewUInt16(uint16(p.Result.ResultSummary().ErrorCode)),
cadence.String(p.Result.ErrorMsg()),
cadence.String(errorMessage),
cadence.NewUInt64(p.Result.GasConsumed),
cadence.String(p.Result.DeployedContractAddressString()),
bytesToCadenceUInt8ArrayValue(encodedLogs),
Expand Down
168 changes: 168 additions & 0 deletions fvm/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,174 @@ func TestEVMRun(t *testing.T) {
assert.Equal(t, num, last.Big().Int64())
})
})

t.Run("testing EVM.run execution reverted with assert error", func(t *testing.T) {

t.Parallel()

RunWithNewEnvironment(t,
chain, func(
ctx fvm.Context,
vm fvm.VM,
snapshot snapshot.SnapshotTree,
testContract *TestContract,
testAccount *EOATestAccount,
) {
sc := systemcontracts.SystemContractsForChain(chain.ChainID())
code := []byte(fmt.Sprintf(
`
import EVM from %s
transaction(tx: [UInt8], coinbaseBytes: [UInt8; 20]){
prepare(account: &Account) {
let coinbase = EVM.EVMAddress(bytes: coinbaseBytes)
let res = EVM.run(tx: tx, coinbase: coinbase)
assert(res.status == EVM.Status.failed, message: "unexpected status")
assert(res.errorCode == 306, message: "unexpected error code")
assert(res.deployedContract == nil, message: "unexpected deployed contract")
}
}
`,
sc.EVMContract.Address.HexWithPrefix(),
))

coinbaseAddr := types.Address{1, 2, 3}
coinbaseBalance := getEVMAccountBalance(t, ctx, vm, snapshot, coinbaseAddr)
require.Zero(t, types.BalanceToBigInt(coinbaseBalance).Uint64())

innerTxBytes := testAccount.PrepareSignAndEncodeTx(t,
testContract.DeployedAt.ToCommon(),
testContract.MakeCallData(t, "assertError"),
big.NewInt(0),
uint64(100_000),
big.NewInt(1),
)

innerTx := cadence.NewArray(
ConvertToCadence(innerTxBytes),
).WithType(stdlib.EVMTransactionBytesCadenceType)

coinbase := cadence.NewArray(
ConvertToCadence(coinbaseAddr.Bytes()),
).WithType(stdlib.EVMAddressBytesCadenceType)

tx := fvm.Transaction(
flow.NewTransactionBody().
SetScript(code).
AddAuthorizer(sc.FlowServiceAccount.Address).
AddArgument(json.MustEncode(innerTx)).
AddArgument(json.MustEncode(coinbase)),
0)

state, output, err := vm.Run(
ctx,
tx,
snapshot,
)
require.NoError(t, err)
require.NoError(t, output.Err)
require.NotEmpty(t, state.WriteSet)

// assert event fields are correct
require.Len(t, output.Events, 2)
txEvent := output.Events[0]
txEventPayload := TxEventToPayload(t, txEvent, sc.EVMContract.Address)
require.NoError(t, err)

assert.Equal(
t,
"execution reverted: Assert Error Message",
txEventPayload.ErrorMessage,
)
},
)
})

t.Run("testing EVM.run execution reverted with custom error", func(t *testing.T) {

t.Parallel()

RunWithNewEnvironment(t,
chain, func(
ctx fvm.Context,
vm fvm.VM,
snapshot snapshot.SnapshotTree,
testContract *TestContract,
testAccount *EOATestAccount,
) {
sc := systemcontracts.SystemContractsForChain(chain.ChainID())
code := []byte(fmt.Sprintf(
`
import EVM from %s
transaction(tx: [UInt8], coinbaseBytes: [UInt8; 20]){
prepare(account: &Account) {
let coinbase = EVM.EVMAddress(bytes: coinbaseBytes)
let res = EVM.run(tx: tx, coinbase: coinbase)
assert(res.status == EVM.Status.failed, message: "unexpected status")
assert(res.errorCode == 306, message: "unexpected error code")
assert(res.deployedContract == nil, message: "unexpected deployed contract")
}
}
`,
sc.EVMContract.Address.HexWithPrefix(),
))

coinbaseAddr := types.Address{1, 2, 3}
coinbaseBalance := getEVMAccountBalance(t, ctx, vm, snapshot, coinbaseAddr)
require.Zero(t, types.BalanceToBigInt(coinbaseBalance).Uint64())

innerTxBytes := testAccount.PrepareSignAndEncodeTx(t,
testContract.DeployedAt.ToCommon(),
testContract.MakeCallData(t, "customError"),
big.NewInt(0),
uint64(100_000),
big.NewInt(1),
)

innerTx := cadence.NewArray(
ConvertToCadence(innerTxBytes),
).WithType(stdlib.EVMTransactionBytesCadenceType)

coinbase := cadence.NewArray(
ConvertToCadence(coinbaseAddr.Bytes()),
).WithType(stdlib.EVMAddressBytesCadenceType)

tx := fvm.Transaction(
flow.NewTransactionBody().
SetScript(code).
AddAuthorizer(sc.FlowServiceAccount.Address).
AddArgument(json.MustEncode(innerTx)).
AddArgument(json.MustEncode(coinbase)),
0)

state, output, err := vm.Run(
ctx,
tx,
snapshot,
)
require.NoError(t, err)
require.NoError(t, output.Err)
require.NotEmpty(t, state.WriteSet)

// assert event fields are correct
require.Len(t, output.Events, 2)
txEvent := output.Events[0]
txEventPayload := TxEventToPayload(t, txEvent, sc.EVMContract.Address)
require.NoError(t, err)

// Unlike assert errors, custom errors cannot be further examined
// or ABI decoded, as we do not have access to the contract's ABI.
assert.Equal(
t,
"execution reverted",
txEventPayload.ErrorMessage,
)
},
)
})
}

func TestEVMBatchRun(t *testing.T) {
Expand Down

0 comments on commit 7fb6ff4

Please sign in to comment.