From 3e091566a3d9df430190605e4faaaeb4ae1716b0 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 8 Jul 2024 21:59:38 -0700 Subject: [PATCH 1/5] avoiding hex encoding decoding for data slices inside evm transaction executed event --- fvm/evm/evm_test.go | 11 +++++++---- fvm/evm/stdlib/contract.cdc | 10 +++++----- fvm/evm/types/events.go | 31 ++++++++++--------------------- fvm/evm/types/events_test.go | 12 ++++++------ fvm/evm/types/utils.go | 32 ++++++++++++++++++++++++++++++++ fvm/evm/types/utils_test.go | 24 ++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 36 deletions(-) create mode 100644 fvm/evm/types/utils.go create mode 100644 fvm/evm/types/utils_test.go diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index aa60bf231d8..3cd517cea8f 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -2,7 +2,6 @@ package evm_test import ( "encoding/binary" - "encoding/hex" "fmt" "math" "math/big" @@ -148,8 +147,12 @@ func TestEVMRun(t *testing.T) { txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent) require.NoError(t, err) + + txPayload, err := types.CadenceUInt8ArrayValueToBytes(txEventPayload.Payload) + require.NoError(t, err) + require.NotEmpty(t, txEventPayload.Hash) - require.Equal(t, hex.EncodeToString(innerTxBytes), txEventPayload.Payload) + require.Equal(t, innerTxBytes, txPayload) require.Equal(t, uint16(types.ErrCodeNoError), txEventPayload.ErrorCode) require.Equal(t, uint16(0), txEventPayload.Index) require.Equal(t, blockEventPayload.Hash, txEventPayload.BlockHash) @@ -395,7 +398,7 @@ func TestEVMRun(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, event.Hash) - encodedLogs, err := hex.DecodeString(event.Logs) + encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(event.Logs) require.NoError(t, err) var logs []*gethTypes.Log @@ -503,7 +506,7 @@ func TestEVMBatchRun(t *testing.T) { event, err := types.DecodeTransactionEventPayload(cadenceEvent) require.NoError(t, err) - encodedLogs, err := hex.DecodeString(event.Logs) + encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(event.Logs) require.NoError(t, err) var logs []*gethTypes.Log diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 26080e5c674..38d79c3b799 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -46,8 +46,8 @@ contract EVM { index: UInt16, // type of the transaction type: UInt8, - // RLP and hex encoded transaction payload - payload: String, + // RLP encoded transaction payload + payload: [UInt8], // code indicating a specific validation (201-300) or execution (301-400) error errorCode: UInt16, // a human-readable message about the error (if any) @@ -56,8 +56,8 @@ contract EVM { gasConsumed: UInt64, // if transaction was a deployment contains a newly deployed contract address contractAddress: String, - // RLP and hex encoded logs - logs: String, + // RLP encoded logs + logs: [UInt8], // block height in which transaction was inclued blockHeight: UInt64, // block hash in which transaction was included @@ -68,7 +68,7 @@ contract EVM { /// the address provided in the contractAddress field. /// in case of revert, the smart contract custom error message /// is also returned here (see EIP-140 for more details). - returnedData: String, + returnedData: [UInt8], /// captures the input and output of the calls (rlp encoded) to the extra /// precompiled contracts (e.g. Cadence Arch) during the transaction execution. /// This data helps to replay the transactions without the need to diff --git a/fvm/evm/types/events.go b/fvm/evm/types/events.go index 514917aae69..a766e05e997 100644 --- a/fvm/evm/types/events.go +++ b/fvm/evm/types/events.go @@ -1,7 +1,6 @@ package types import ( - "encoding/hex" "fmt" "github.com/onflow/cadence/encoding/ccf" @@ -92,16 +91,16 @@ func (p *transactionEvent) ToCadence(location common.Location) (cadence.Event, e cadence.NewField("hash", cadence.StringType), cadence.NewField("index", cadence.UInt16Type), cadence.NewField("type", cadence.UInt8Type), - cadence.NewField("payload", cadence.StringType), + cadence.NewField("payload", cadence.NewVariableSizedArrayType(cadence.UInt8Type)), cadence.NewField("errorCode", cadence.UInt16Type), cadence.NewField("errorMessage", cadence.StringType), cadence.NewField("gasConsumed", cadence.UInt64Type), cadence.NewField("contractAddress", cadence.StringType), - cadence.NewField("logs", cadence.StringType), + cadence.NewField("logs", cadence.NewVariableSizedArrayType(cadence.UInt8Type)), cadence.NewField("blockHeight", cadence.UInt64Type), // todo we can remove hash and just reference block by height (evm-gateway dependency) cadence.NewField("blockHash", cadence.StringType), - cadence.NewField("returnedData", cadence.StringType), + cadence.NewField("returnedData", cadence.NewVariableSizedArrayType(cadence.UInt8Type)), cadence.NewField("precompiledCalls", cadence.NewVariableSizedArrayType(cadence.UInt8Type)), }, nil, @@ -111,16 +110,16 @@ func (p *transactionEvent) ToCadence(location common.Location) (cadence.Event, e cadence.String(p.Result.TxHash.String()), cadence.NewUInt16(p.Result.Index), cadence.NewUInt8(p.Result.TxType), - cadence.String(hex.EncodeToString(p.Payload)), + BytesToCadenceUInt8ArrayValue(p.Payload), cadence.NewUInt16(uint16(p.Result.ResultSummary().ErrorCode)), cadence.String(errorMsg), cadence.NewUInt64(p.Result.GasConsumed), deployedAddress, - cadence.String(hex.EncodeToString(encodedLogs)), + BytesToCadenceUInt8ArrayValue(encodedLogs), cadence.NewUInt64(p.BlockHeight), cadence.String(p.BlockHash.String()), - cadence.String(hex.EncodeToString(p.Result.ReturnedData)), - bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), + BytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), + BytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), }).WithType(eventType), nil } @@ -200,15 +199,15 @@ type TransactionEventPayload struct { Hash string `cadence:"hash"` Index uint16 `cadence:"index"` TransactionType uint8 `cadence:"type"` - Payload string `cadence:"payload"` + Payload cadence.Array `cadence:"payload"` ErrorCode uint16 `cadence:"errorCode"` GasConsumed uint64 `cadence:"gasConsumed"` ContractAddress string `cadence:"contractAddress"` - Logs string `cadence:"logs"` + Logs cadence.Array `cadence:"logs"` BlockHeight uint64 `cadence:"blockHeight"` BlockHash string `cadence:"blockHash"` ErrorMessage string `cadence:"errorMessage"` - ReturnedData string `cadence:"returnedData"` + ReturnedData cadence.Array `cadence:"returnedData"` PrecompiledCalls cadence.Array `cadence:"precompiledCalls"` } @@ -219,16 +218,6 @@ func DecodeTransactionEventPayload(event cadence.Event) (*TransactionEventPayloa return &tx, err } -func bytesToCadenceUInt8ArrayValue(b []byte) cadence.Array { - values := make([]cadence.Value, len(b)) - for i, v := range b { - values[i] = cadence.NewUInt8(v) - } - return cadence.NewArray(values).WithType( - cadence.NewVariableSizedArrayType(cadence.UInt8Type), - ) -} - // FLOWTokensEventPayload captures payloads for a FlowTokenDeposited event type FLOWTokensDepositedEventPayload struct { Address string `cadence:"address"` diff --git a/fvm/evm/types/events_test.go b/fvm/evm/types/events_test.go index d65856527f1..87619ae0220 100644 --- a/fvm/evm/types/events_test.go +++ b/fvm/evm/types/events_test.go @@ -129,12 +129,12 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, tep.BlockHeight, blockHeight) assert.Equal(t, tep.BlockHash, blockHash.Hex()) assert.Equal(t, tep.Hash, txHash.Hex()) - assert.Equal(t, tep.Payload, txEncoded) + assert.Equal(t, tep.Payload, types.BytesToCadenceUInt8ArrayValue(txBytes)) assert.Equal(t, types.ErrorCode(tep.ErrorCode), types.ExecutionErrCodeOutOfGas) assert.Equal(t, tep.TransactionType, txResult.TxType) assert.Equal(t, tep.GasConsumed, txResult.GasConsumed) assert.Equal(t, tep.ErrorMessage, txResult.VMError.Error()) - assert.Equal(t, tep.ReturnedData, hex.EncodeToString(txResult.ReturnedData)) + assert.Equal(t, tep.ReturnedData, types.BytesToCadenceUInt8ArrayValue(txResult.ReturnedData)) assert.Equal( t, tep.ContractAddress, @@ -143,7 +143,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { encodedLogs, err := rlp.EncodeToBytes(txResult.Logs) require.NoError(t, err) - assert.Equal(t, tep.Logs, hex.EncodeToString(encodedLogs)) + assert.Equal(t, tep.Logs, types.BytesToCadenceUInt8ArrayValue(encodedLogs)) v, err := ccf.Encode(ev) require.NoError(t, err) @@ -171,12 +171,12 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, tep.BlockHeight, blockHeight) assert.Equal(t, tep.BlockHash, blockHash.Hex()) assert.Equal(t, tep.Hash, txHash.Hex()) - assert.Equal(t, tep.Payload, txEncoded) + assert.Equal(t, tep.Payload, types.BytesToCadenceUInt8ArrayValue(txBytes)) assert.Equal(t, types.ErrCodeNoError, types.ErrorCode(tep.ErrorCode)) assert.Equal(t, tep.TransactionType, txResult.TxType) assert.Equal(t, tep.GasConsumed, txResult.GasConsumed) assert.Empty(t, tep.ErrorMessage) - assert.Equal(t, tep.ReturnedData, hex.EncodeToString(txResult.ReturnedData)) + assert.Equal(t, tep.ReturnedData, types.BytesToCadenceUInt8ArrayValue(txResult.ReturnedData)) assert.NotNil(t, txResult.DeployedContractAddress) assert.Equal( t, @@ -186,7 +186,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { encodedLogs, err := rlp.EncodeToBytes(txResult.Logs) require.NoError(t, err) - assert.Equal(t, tep.Logs, hex.EncodeToString(encodedLogs)) + assert.Equal(t, tep.Logs, types.BytesToCadenceUInt8ArrayValue(encodedLogs)) v, err := ccf.Encode(ev) require.NoError(t, err) diff --git a/fvm/evm/types/utils.go b/fvm/evm/types/utils.go new file mode 100644 index 00000000000..e529f490e82 --- /dev/null +++ b/fvm/evm/types/utils.go @@ -0,0 +1,32 @@ +package types + +import ( + "fmt" + + "github.com/onflow/cadence" +) + +// BytesToCadenceUInt8ArrayValue converts bytes into a Cadence array of type UInt8 +func BytesToCadenceUInt8ArrayValue(b []byte) cadence.Array { + values := make([]cadence.Value, len(b)) + for i, v := range b { + values[i] = cadence.NewUInt8(v) + } + return cadence.NewArray(values).WithType( + cadence.NewVariableSizedArrayType(cadence.UInt8Type), + ) +} + +var cadenceArrayTypeOfUInt8 = cadence.NewVariableSizedArrayType(cadence.UInt8Type) + +// CadenceUInt8ArrayValueToBytes converts a Cadence array of type UInt8 into a byte slice +func CadenceUInt8ArrayValueToBytes(a cadence.Array) ([]byte, error) { + if !a.ArrayType.Equal(cadenceArrayTypeOfUInt8) { + return nil, fmt.Errorf("invalid array type") + } + values := make([]byte, len(a.Values)) + for i, v := range a.Values { + values[i] = byte(v.(cadence.UInt8)) + } + return values, nil +} diff --git a/fvm/evm/types/utils_test.go b/fvm/evm/types/utils_test.go new file mode 100644 index 00000000000..793fd7be860 --- /dev/null +++ b/fvm/evm/types/utils_test.go @@ -0,0 +1,24 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/flow-go/fvm/evm/types" +) + +func TestCadenceConversion(t *testing.T) { + input := []byte{1, 2, 3, 4, 5} + + inCadence := types.BytesToCadenceUInt8ArrayValue(input) + output, err := types.CadenceUInt8ArrayValueToBytes(inCadence) + require.NoError(t, err) + require.Equal(t, input, output) + + invalidTypeArray := cadence.NewArray(nil). + WithType(cadence.NewVariableSizedArrayType(cadence.UFix64Type)) + _, err = types.CadenceUInt8ArrayValueToBytes(invalidTypeArray) + require.Error(t, err) +} From 4192ee394591b8e996bab02967215b24978ccaed Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 8 Jul 2024 22:13:12 -0700 Subject: [PATCH 2/5] update test --- fvm/evm/handler/handler_test.go | 6 ++---- fvm/evm/types/utils.go | 15 +++++++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 5cdf7d709bf..53bc7062290 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -1,12 +1,10 @@ package handler_test import ( - "encoding/hex" "encoding/json" "fmt" "math" "math/big" - "strings" "testing" "time" @@ -112,7 +110,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { // TODO: add an event decoder in types.event cadenceLogs := cadence.SearchFieldByName(cadenceEvent, "logs") - encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", "")) + encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(cadenceLogs) require.NoError(t, err) var logs []*gethTypes.Log @@ -938,7 +936,7 @@ func TestHandler_TransactionRun(t *testing.T) { // TODO: add an event decoder in types.event cadenceLogs := cadence.SearchFieldByName(cadenceEvent, "logs") - encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", "")) + encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(cadenceLogs.(cadence.Array)) require.NoError(t, err) var logs []*gethTypes.Log diff --git a/fvm/evm/types/utils.go b/fvm/evm/types/utils.go index e529f490e82..cd2b1ab7700 100644 --- a/fvm/evm/types/utils.go +++ b/fvm/evm/types/utils.go @@ -20,12 +20,19 @@ func BytesToCadenceUInt8ArrayValue(b []byte) cadence.Array { var cadenceArrayTypeOfUInt8 = cadence.NewVariableSizedArrayType(cadence.UInt8Type) // CadenceUInt8ArrayValueToBytes converts a Cadence array of type UInt8 into a byte slice -func CadenceUInt8ArrayValueToBytes(a cadence.Array) ([]byte, error) { - if !a.ArrayType.Equal(cadenceArrayTypeOfUInt8) { +func CadenceUInt8ArrayValueToBytes(a cadence.Value) ([]byte, error) { + aa, ok := a.(cadence.Array) + if !ok { + return nil, fmt.Errorf("value is not an array") + } + + // if array type is empty, continue + if aa.Type() != nil && !aa.Type().Equal(cadenceArrayTypeOfUInt8) { return nil, fmt.Errorf("invalid array type") } - values := make([]byte, len(a.Values)) - for i, v := range a.Values { + + values := make([]byte, len(aa.Values)) + for i, v := range aa.Values { values[i] = byte(v.(cadence.UInt8)) } return values, nil From 035faa42588e3063c245e5e8c9f3c9e4a2089674 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 8 Jul 2024 22:14:55 -0700 Subject: [PATCH 3/5] state commit change due to evm contract changes --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index bb0a66dc17f..0500bbf2928 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("3c67d8e4d76dd71357deff581c44b849904f25317a7f3c4e87717abf1fc0827d") + expectedStateCommitmentBytes, _ := hex.DecodeString("719729e453d826640fafd769746604e0ee0a67efa6df43f87714689c45f7e228") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index e1eb99e07e0..86bc106a08e 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "415fbee237d355fea5d03c738dfe49273c74231adf8359b409eb077995251076" +const GenesisStateCommitmentHex = "1f38e32700ca6f4ad92834f1a086624f172868b0c58c249775305bb1b74735a4" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "6c686cd6d0dfc46e91c3bdb83716845aa989d1c369cea3b9171321a536e06c8a" + return "6f6069d94c283ae744a5bfdd3193cb31aa96efdd34d56eb0372a7f06918527df" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "e9667aad5542cd097d26278a77af059fd8d3d29da9a27ac7e2237887ec43dfc9" + return "31df4b53081ea3d8db029483b2e4eebc86ca5e9fb970e7b1fb6e299c43c90952" } From e293c583dc0d3296b419101c1e1f8d8b854289bf Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 8 Jul 2024 22:28:28 -0700 Subject: [PATCH 4/5] fix linter --- fvm/evm/types/utils_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fvm/evm/types/utils_test.go b/fvm/evm/types/utils_test.go index 793fd7be860..e3f36698f69 100644 --- a/fvm/evm/types/utils_test.go +++ b/fvm/evm/types/utils_test.go @@ -3,9 +3,9 @@ package types_test import ( "testing" + "github.com/onflow/cadence" "github.com/stretchr/testify/require" - "github.com/onflow/cadence" "github.com/onflow/flow-go/fvm/evm/types" ) From f890181a8021818c00283f3f22ebd9a9b43f742a Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 15 Jul 2024 19:14:29 -0700 Subject: [PATCH 5/5] apply PR feedback --- fvm/evm/types/utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fvm/evm/types/utils.go b/fvm/evm/types/utils.go index cd2b1ab7700..c351581bfc8 100644 --- a/fvm/evm/types/utils.go +++ b/fvm/evm/types/utils.go @@ -26,8 +26,9 @@ func CadenceUInt8ArrayValueToBytes(a cadence.Value) ([]byte, error) { return nil, fmt.Errorf("value is not an array") } + arrayType := aa.Type() // if array type is empty, continue - if aa.Type() != nil && !aa.Type().Equal(cadenceArrayTypeOfUInt8) { + if arrayType != nil && !arrayType.Equal(cadenceArrayTypeOfUInt8) { return nil, fmt.Errorf("invalid array type") }