Skip to content

Commit

Permalink
Merge pull request #6456 from onflow/ramtin/evm-transaction-execution…
Browse files Browse the repository at this point in the history
…-checksum

[Flow EVM] adding state update checksum to transaction execution events
  • Loading branch information
ramtinms authored Sep 18, 2024
2 parents 5c348ae + de7aa97 commit e5add94
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 7 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("e8b4b48a5c4eb510e28ecc9623271d53ef9915c98d333939b448516fa25e5a8f")
expectedStateCommitmentBytes, _ := hex.DecodeString("d4c956063e3d187b73cfe81bbecc75279e055ddd4741b79847faf407999888c0")
expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes)
require.NoError(t, err)

Expand Down
56 changes: 54 additions & 2 deletions fvm/evm/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ 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 [ChecksumLength]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),
Expand All @@ -95,6 +100,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
}

Expand Down Expand Up @@ -188,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 [ChecksumLength]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"`
Expand All @@ -202,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
Expand Down
3 changes: 3 additions & 0 deletions fvm/evm/events/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand All @@ -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[:events.ChecksumLength])
assert.Equal(
t,
tep.ContractAddress,
Expand Down
16 changes: 16 additions & 0 deletions fvm/evm/events/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 [ChecksumLength]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)
}
16 changes: 16 additions & 0 deletions fvm/evm/events/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@ func TestHashToCadenceArrayValue(t *testing.T) {
inCadence,
)
}

func TestHashToChecksumValue(t *testing.T) {
t.Parallel()

checksum := [4]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,
)
}
5 changes: 4 additions & 1 deletion fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
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 = "ba479ddabd34159a9d6326ea78c659aa6dd71db63791714bdccdbea859ba1b8e"
const GenesisStateCommitmentHex = "341a6372c961713e6ed9c975f3a61d036d65c2ff242a374c5b56cbf267a15b20"

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 "9485c620254319da8ea93978909d7ed8327e9dd1f4cb9ec74c816919166b5a2c"
return "441731faf7bf1d8d6217cf67ad7e4ab2cda9da614bae14fb5f0a590fe0ce23df"
}
if chainID == flow.Sandboxnet {
return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1"
}
return "66df5ceb8ca13532a5004bb6014677b0ff72199a5ea42388d5e0947368739c94"
return "15d74ad1359542b589138868c8c91a0ce5f0b6115171d68cdabf982c4b684553"
}

0 comments on commit e5add94

Please sign in to comment.