Skip to content

Commit

Permalink
Merge pull request onflow#6190 from onflow/ramtin/evm-avoid-hex-encod…
Browse files Browse the repository at this point in the history
…ing-for-event-bytes

[Flow EVM] Avoid hex encoding/decoding for event fields of type bytes
  • Loading branch information
ramtinms authored Jul 16, 2024
2 parents a9a235c + f890181 commit c24b664
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 44 deletions.
2 changes: 1 addition & 1 deletion engine/execution/state/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
11 changes: 7 additions & 4 deletions fvm/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package evm_test

import (
"encoding/binary"
"encoding/hex"
"fmt"
"math"
"math/big"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package handler_test

import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"math/big"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down
31 changes: 10 additions & 21 deletions fvm/evm/types/events.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package types

import (
"encoding/hex"
"fmt"

"github.com/onflow/cadence/encoding/ccf"
Expand Down Expand Up @@ -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,
Expand All @@ -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
}

Expand Down Expand Up @@ -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"`
}

Expand All @@ -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"`
Expand Down
12 changes: 6 additions & 6 deletions fvm/evm/types/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down
40 changes: 40 additions & 0 deletions fvm/evm/types/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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.Value) ([]byte, error) {
aa, ok := a.(cadence.Array)
if !ok {
return nil, fmt.Errorf("value is not an array")
}

arrayType := aa.Type()
// if array type is empty, continue
if arrayType != nil && !arrayType.Equal(cadenceArrayTypeOfUInt8) {
return nil, fmt.Errorf("invalid array type")
}

values := make([]byte, len(aa.Values))
for i, v := range aa.Values {
values[i] = byte(v.(cadence.UInt8))
}
return values, nil
}
24 changes: 24 additions & 0 deletions fvm/evm/types/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package types_test

import (
"testing"

"github.com/onflow/cadence"
"github.com/stretchr/testify/require"

"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)
}
6 changes: 3 additions & 3 deletions utils/unittest/execution_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
}

0 comments on commit c24b664

Please sign in to comment.