From 29c6a8fa48cc7e822d3a5df06cabb9dde969bb83 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 10 Sep 2024 11:29:29 -0700 Subject: [PATCH 1/6] add stateUpdateChecksum to tx executed events --- fvm/evm/events/events.go | 3 +++ fvm/evm/events/utils.go | 16 ++++++++++++++++ fvm/evm/events/utils_test.go | 16 ++++++++++++++++ fvm/evm/stdlib/contract.cdc | 5 ++++- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 8d3d800e7c4..8da345cd007 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -82,6 +82,8 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error eventType := stdlib.CadenceTypesForChain(chainID).TransactionExecuted + // the first 4 bytes of StateChangeCommitment is used as checksum + checksum := p.Result.StateChangeCommitment[:4] return cadence.NewEvent([]cadence.Value{ hashToCadenceArrayValue(p.Result.TxHash), cadence.NewUInt16(p.Result.Index), @@ -95,6 +97,7 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error cadence.NewUInt64(p.BlockHeight), bytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), + checksumToCadenceArrayValue(checksum), }).WithType(eventType), nil } diff --git a/fvm/evm/events/utils.go b/fvm/evm/events/utils.go index b75eff4fcfa..3df9d22de57 100644 --- a/fvm/evm/events/utils.go +++ b/fvm/evm/events/utils.go @@ -30,3 +30,19 @@ func hashToCadenceArrayValue(hash gethCommon.Hash) cadence.Array { return cadence.NewArray(values). WithType(cadenceHashType) } + +// ChecksumLength captures number of bytes a checksum uses +const ChecksumLength = 4 + +// checksumType is the Cadence type [UInt8;4] +var checksumType = cadence.NewConstantSizedArrayType(ChecksumLength, cadence.UInt8Type) + +// checksumToCadenceArrayValue converts a checksum ([4]byte) into a Cadence array of type [UInt8;4] +func checksumToCadenceArrayValue(checksum []byte) cadence.Array { + values := make([]cadence.Value, ChecksumLength) + for i := 0; i < ChecksumLength; i++ { + values[i] = cadence.NewUInt8(checksum[i]) + } + return cadence.NewArray(values). + WithType(checksumType) +} diff --git a/fvm/evm/events/utils_test.go b/fvm/evm/events/utils_test.go index 976e37c7d20..9f8ef5ae0a2 100644 --- a/fvm/evm/events/utils_test.go +++ b/fvm/evm/events/utils_test.go @@ -72,3 +72,19 @@ func TestHashToCadenceArrayValue(t *testing.T) { inCadence, ) } + +func TestHashToChecksumValue(t *testing.T) { + t.Parallel() + + checksum := []byte{1, 2, 3, 4} + inCadence := checksumToCadenceArrayValue(checksum) + require.Equal(t, + cadence.NewArray([]cadence.Value{ + cadence.UInt8(1), + cadence.UInt8(2), + cadence.UInt8(3), + cadence.UInt8(4), + }).WithType(checksumType), + inCadence, + ) +} diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 21ed5a0af78..226a58d2b3c 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -73,7 +73,10 @@ contract EVM { /// precompiled contracts (e.g. Cadence Arch) during the transaction execution. /// This data helps to replay the transactions without the need to /// have access to the full cadence state data. - precompiledCalls: [UInt8] + precompiledCalls: [UInt8], + /// stateUpdateChecksum provides a mean to validate + /// the updates to the storage when re-executing a transaction off-chain. + stateUpdateChecksum: [UInt8; 4] ) access(all) From ef3b2c1b23de47e06f4272bf9b3445b008ed50d9 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 10 Sep 2024 11:34:28 -0700 Subject: [PATCH 2/6] lint fix --- .../emulator/state/updateCommitter_test.go | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fvm/evm/emulator/state/updateCommitter_test.go b/fvm/evm/emulator/state/updateCommitter_test.go index 56325c292b5..ab0be67a08f 100644 --- a/fvm/evm/emulator/state/updateCommitter_test.go +++ b/fvm/evm/emulator/state/updateCommitter_test.go @@ -23,7 +23,8 @@ func TestChangeCommitter(t *testing.T) { t.Run("test create account", func(t *testing.T) { dc := state.NewUpdateCommitter() - dc.CreateAccount(addr, balance, nonce, randomHash) + err := dc.CreateAccount(addr, balance, nonce, randomHash) + require.NoError(t, err) hasher := hash.NewSHA3_256() @@ -45,7 +46,8 @@ func TestChangeCommitter(t *testing.T) { t.Run("test update account", func(t *testing.T) { dc := state.NewUpdateCommitter() - dc.UpdateAccount(addr, balance, nonce, randomHash) + err := dc.UpdateAccount(addr, balance, nonce, randomHash) + require.NoError(t, err) hasher := hash.NewSHA3_256() input := []byte{byte(state.AccountUpdateOpCode)} @@ -66,7 +68,8 @@ func TestChangeCommitter(t *testing.T) { t.Run("test delete account", func(t *testing.T) { dc := state.NewUpdateCommitter() - dc.DeleteAccount(addr) + err := dc.DeleteAccount(addr) + require.NoError(t, err) hasher := hash.NewSHA3_256() input := []byte{byte(state.AccountDeletionOpCode)} @@ -83,7 +86,8 @@ func TestChangeCommitter(t *testing.T) { t.Run("test update slot", func(t *testing.T) { dc := state.NewUpdateCommitter() - dc.UpdateSlot(addr, key, value) + err := dc.UpdateSlot(addr, key, value) + require.NoError(t, err) hasher := hash.NewSHA3_256() @@ -111,12 +115,15 @@ func BenchmarkDeltaCommitter(b *testing.B) { numberOfAccountUpdates := 10 for i := 0; i < numberOfAccountUpdates; i++ { - dc.UpdateAccount(addr.ToCommon(), balance, nonce, randomHash) + err := dc.UpdateAccount(addr.ToCommon(), balance, nonce, randomHash) + require.NoError(b, err) } numberOfSlotUpdates := 10 for i := 0; i < numberOfSlotUpdates; i++ { - dc.UpdateSlot(addr.ToCommon(), randomHash, randomHash) + err := dc.UpdateSlot(addr.ToCommon(), randomHash, randomHash) + require.NoError(b, err) } - dc.Commitment() + com := dc.Commitment() + require.NotEmpty(b, com) } From c6ad06034733cfb8b69035907e53da04e972ee36 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 10 Sep 2024 12:46:31 -0700 Subject: [PATCH 3/6] state commit update due to the evm contract change --- 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 6fa5ef66470..91e4c03002c 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("e8b4b48a5c4eb510e28ecc9623271d53ef9915c98d333939b448516fa25e5a8f") + expectedStateCommitmentBytes, _ := hex.DecodeString("d4c956063e3d187b73cfe81bbecc75279e055ddd4741b79847faf407999888c0") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index ee6da7b082d..221d62987e7 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 = "ba479ddabd34159a9d6326ea78c659aa6dd71db63791714bdccdbea859ba1b8e" +const GenesisStateCommitmentHex = "341a6372c961713e6ed9c975f3a61d036d65c2ff242a374c5b56cbf267a15b20" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "9485c620254319da8ea93978909d7ed8327e9dd1f4cb9ec74c816919166b5a2c" + return "441731faf7bf1d8d6217cf67ad7e4ab2cda9da614bae14fb5f0a590fe0ce23df" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "66df5ceb8ca13532a5004bb6014677b0ff72199a5ea42388d5e0947368739c94" + return "15d74ad1359542b589138868c8c91a0ce5f0b6115171d68cdabf982c4b684553" } From 3e207d48479f422e39cafa3d75049e64dd2736dc Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 10 Sep 2024 13:17:46 -0700 Subject: [PATCH 4/6] support legacy decoding --- fvm/evm/events/events.go | 55 +++++++++++++++++++++++++++++++++-- fvm/evm/events/events_test.go | 3 ++ fvm/evm/events/utils.go | 5 +++- fvm/evm/events/utils_test.go | 2 +- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 8da345cd007..00e55e89a9a 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -83,7 +83,10 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error eventType := stdlib.CadenceTypesForChain(chainID).TransactionExecuted // the first 4 bytes of StateChangeCommitment is used as checksum - checksum := p.Result.StateChangeCommitment[:4] + var checksum [4]byte + if len(p.Result.StateChangeCommitment) >= ChecksumLength { + copy(checksum[:ChecksumLength], p.Result.StateChangeCommitment[:ChecksumLength]) + } return cadence.NewEvent([]cadence.Value{ hashToCadenceArrayValue(p.Result.TxHash), cadence.NewUInt16(p.Result.Index), @@ -191,6 +194,23 @@ func DecodeBlockEventPayload(event cadence.Event) (*BlockEventPayload, error) { } type TransactionEventPayload struct { + Hash gethCommon.Hash `cadence:"hash"` + Index uint16 `cadence:"index"` + TransactionType uint8 `cadence:"type"` + Payload []byte `cadence:"payload"` + ErrorCode uint16 `cadence:"errorCode"` + GasConsumed uint64 `cadence:"gasConsumed"` + ContractAddress string `cadence:"contractAddress"` + Logs []byte `cadence:"logs"` + BlockHeight uint64 `cadence:"blockHeight"` + ErrorMessage string `cadence:"errorMessage"` + ReturnedData []byte `cadence:"returnedData"` + PrecompiledCalls []byte `cadence:"precompiledCalls"` + StateUpdateChecksum [4]byte `cadence:"stateUpdateChecksum"` +} + +// transactionEventPayloadV0 legacy format of the transaction event without stateUpdateChecksum field +type transactionEventPayloadV0 struct { Hash gethCommon.Hash `cadence:"hash"` Index uint16 `cadence:"index"` TransactionType uint8 `cadence:"type"` @@ -205,11 +225,40 @@ type TransactionEventPayload struct { PrecompiledCalls []byte `cadence:"precompiledCalls"` } +// decodeLegacyTransactionEventPayload decodes any legacy transaction formats into +// current version of the transaction event payload. +func decodeLegacyTransactionEventPayload(event cadence.Event) (*TransactionEventPayload, error) { + var tx transactionEventPayloadV0 + if err := cadence.DecodeFields(event, &tx); err != nil { + return nil, err + } + return &TransactionEventPayload{ + Hash: tx.Hash, + Index: tx.Index, + TransactionType: tx.TransactionType, + Payload: tx.Payload, + ErrorCode: tx.ErrorCode, + GasConsumed: tx.GasConsumed, + ContractAddress: tx.ContractAddress, + Logs: tx.Logs, + BlockHeight: tx.BlockHeight, + ErrorMessage: tx.ErrorMessage, + ReturnedData: tx.ReturnedData, + PrecompiledCalls: tx.PrecompiledCalls, + }, nil +} + // DecodeTransactionEventPayload decodes Cadence event into transaction event payload. func DecodeTransactionEventPayload(event cadence.Event) (*TransactionEventPayload, error) { var tx TransactionEventPayload - err := cadence.DecodeFields(event, &tx) - return &tx, err + if err := cadence.DecodeFields(event, &tx); err != nil { + if legTx, err := decodeLegacyTransactionEventPayload(event); err == nil { + return legTx, nil + } + return nil, err + } + return &tx, nil + } // FLOWTokensDepositedEventPayload captures payloads for a FlowTokenDeposited event diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 4b5d3420f6b..2531d0ea316 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -86,6 +86,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { txHash := testutils.RandomCommonHash(t) blockHash := testutils.RandomCommonHash(t) data := "000000000000000000000000000000000000000000000000000000000000002a" + stateUpdateCommit := testutils.RandomCommonHash(t).Bytes() dataBytes, err := hex.DecodeString(data) require.NoError(t, err) blockHeight := uint64(2) @@ -111,6 +112,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { ReturnedData: dataBytes, Logs: []*gethTypes.Log{log}, TxHash: txHash, + StateChangeCommitment: stateUpdateCommit, } t.Run("evm.TransactionExecuted with failed status", func(t *testing.T) { @@ -129,6 +131,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, tep.GasConsumed, txResult.GasConsumed) assert.Equal(t, tep.ErrorMessage, txResult.VMError.Error()) assert.Equal(t, tep.ReturnedData, txResult.ReturnedData) + assert.Equal(t, tep.StateUpdateChecksum[:], stateUpdateCommit[:4]) assert.Equal( t, tep.ContractAddress, diff --git a/fvm/evm/events/utils.go b/fvm/evm/events/utils.go index 3df9d22de57..f4641dd02be 100644 --- a/fvm/evm/events/utils.go +++ b/fvm/evm/events/utils.go @@ -1,6 +1,8 @@ package events import ( + "fmt" + "github.com/onflow/cadence" gethCommon "github.com/onflow/go-ethereum/common" ) @@ -38,11 +40,12 @@ const ChecksumLength = 4 var checksumType = cadence.NewConstantSizedArrayType(ChecksumLength, cadence.UInt8Type) // checksumToCadenceArrayValue converts a checksum ([4]byte) into a Cadence array of type [UInt8;4] -func checksumToCadenceArrayValue(checksum []byte) cadence.Array { +func checksumToCadenceArrayValue(checksum [4]byte) cadence.Array { values := make([]cadence.Value, ChecksumLength) for i := 0; i < ChecksumLength; i++ { values[i] = cadence.NewUInt8(checksum[i]) } + fmt.Println(values) return cadence.NewArray(values). WithType(checksumType) } diff --git a/fvm/evm/events/utils_test.go b/fvm/evm/events/utils_test.go index 9f8ef5ae0a2..6d59c18d4ce 100644 --- a/fvm/evm/events/utils_test.go +++ b/fvm/evm/events/utils_test.go @@ -76,7 +76,7 @@ func TestHashToCadenceArrayValue(t *testing.T) { func TestHashToChecksumValue(t *testing.T) { t.Parallel() - checksum := []byte{1, 2, 3, 4} + checksum := [4]byte{1, 2, 3, 4} inCadence := checksumToCadenceArrayValue(checksum) require.Equal(t, cadence.NewArray([]cadence.Value{ From 95fd8b0db29a132ab0688d5951ed6b07b9736be1 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 10 Sep 2024 13:37:19 -0700 Subject: [PATCH 5/6] clean up --- fvm/evm/events/utils.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/fvm/evm/events/utils.go b/fvm/evm/events/utils.go index f4641dd02be..bae39d9441c 100644 --- a/fvm/evm/events/utils.go +++ b/fvm/evm/events/utils.go @@ -1,8 +1,6 @@ package events import ( - "fmt" - "github.com/onflow/cadence" gethCommon "github.com/onflow/go-ethereum/common" ) @@ -45,7 +43,6 @@ func checksumToCadenceArrayValue(checksum [4]byte) cadence.Array { for i := 0; i < ChecksumLength; i++ { values[i] = cadence.NewUInt8(checksum[i]) } - fmt.Println(values) return cadence.NewArray(values). WithType(checksumType) } From 8ff0b86badbff94353187526e90a881c246cf6a8 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 18 Sep 2024 09:35:11 -0700 Subject: [PATCH 6/6] clean up --- fvm/evm/events/events.go | 28 ++++++++++++++-------------- fvm/evm/events/events_test.go | 2 +- fvm/evm/events/utils.go | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 00e55e89a9a..e9a50903124 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -83,7 +83,7 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error eventType := stdlib.CadenceTypesForChain(chainID).TransactionExecuted // the first 4 bytes of StateChangeCommitment is used as checksum - var checksum [4]byte + var checksum [ChecksumLength]byte if len(p.Result.StateChangeCommitment) >= ChecksumLength { copy(checksum[:ChecksumLength], p.Result.StateChangeCommitment[:ChecksumLength]) } @@ -194,19 +194,19 @@ func DecodeBlockEventPayload(event cadence.Event) (*BlockEventPayload, error) { } type TransactionEventPayload struct { - Hash gethCommon.Hash `cadence:"hash"` - Index uint16 `cadence:"index"` - TransactionType uint8 `cadence:"type"` - Payload []byte `cadence:"payload"` - ErrorCode uint16 `cadence:"errorCode"` - GasConsumed uint64 `cadence:"gasConsumed"` - ContractAddress string `cadence:"contractAddress"` - Logs []byte `cadence:"logs"` - BlockHeight uint64 `cadence:"blockHeight"` - ErrorMessage string `cadence:"errorMessage"` - ReturnedData []byte `cadence:"returnedData"` - PrecompiledCalls []byte `cadence:"precompiledCalls"` - StateUpdateChecksum [4]byte `cadence:"stateUpdateChecksum"` + Hash gethCommon.Hash `cadence:"hash"` + Index uint16 `cadence:"index"` + TransactionType uint8 `cadence:"type"` + Payload []byte `cadence:"payload"` + ErrorCode uint16 `cadence:"errorCode"` + GasConsumed uint64 `cadence:"gasConsumed"` + ContractAddress string `cadence:"contractAddress"` + Logs []byte `cadence:"logs"` + BlockHeight uint64 `cadence:"blockHeight"` + ErrorMessage string `cadence:"errorMessage"` + ReturnedData []byte `cadence:"returnedData"` + PrecompiledCalls []byte `cadence:"precompiledCalls"` + StateUpdateChecksum [ChecksumLength]byte `cadence:"stateUpdateChecksum"` } // transactionEventPayloadV0 legacy format of the transaction event without stateUpdateChecksum field diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 2531d0ea316..08f9f5abb01 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -131,7 +131,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, tep.GasConsumed, txResult.GasConsumed) assert.Equal(t, tep.ErrorMessage, txResult.VMError.Error()) assert.Equal(t, tep.ReturnedData, txResult.ReturnedData) - assert.Equal(t, tep.StateUpdateChecksum[:], stateUpdateCommit[:4]) + assert.Equal(t, tep.StateUpdateChecksum[:], stateUpdateCommit[:events.ChecksumLength]) assert.Equal( t, tep.ContractAddress, diff --git a/fvm/evm/events/utils.go b/fvm/evm/events/utils.go index bae39d9441c..3e54a6f4f05 100644 --- a/fvm/evm/events/utils.go +++ b/fvm/evm/events/utils.go @@ -38,7 +38,7 @@ const ChecksumLength = 4 var checksumType = cadence.NewConstantSizedArrayType(ChecksumLength, cadence.UInt8Type) // checksumToCadenceArrayValue converts a checksum ([4]byte) into a Cadence array of type [UInt8;4] -func checksumToCadenceArrayValue(checksum [4]byte) cadence.Array { +func checksumToCadenceArrayValue(checksum [ChecksumLength]byte) cadence.Array { values := make([]cadence.Value, ChecksumLength) for i := 0; i < ChecksumLength; i++ { values[i] = cadence.NewUInt8(checksum[i])