diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index f93288cea05..ee4eec1e1eb 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("e0cf0fd62dd620333d5ce07720231533dad07c84efaed76ad88cf4c55ee5344d") + expectedStateCommitmentBytes, _ := hex.DecodeString("8d634458df94d3a284d7363686269c31c45b5ddd017215ec0505ea03a5f547d1") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 618590b1703..01fb03e2697 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -106,6 +106,8 @@ func TestEVMRun(t *testing.T) { // assert event fields are correct require.Len(t, output.Events, 1) txEvent := output.Events[0] + txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) + require.NoError(t, err) // commit block blockEventPayload, snapshot := callEVMHeartBeat(t, @@ -116,13 +118,14 @@ func TestEVMRun(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(43785), blockEventPayload.TotalGasUsed) require.NotEmpty(t, blockEventPayload.Hash) - require.Len(t, blockEventPayload.TransactionHashes, 1) - require.NotEmpty(t, blockEventPayload.ReceiptRoot) - txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) - require.NoError(t, err) + txHashes := types.TransactionHashes{txEventPayload.Hash} + require.Equal(t, + txHashes.RootHash(), + blockEventPayload.TransactionHashRoot, + ) + require.NotEmpty(t, blockEventPayload.ReceiptRoot) - require.NotEmpty(t, txEventPayload.Hash) require.Equal(t, innerTxBytes, txEventPayload.Payload) require.Equal(t, uint16(types.ErrCodeNoError), txEventPayload.ErrorCode) require.Equal(t, uint16(0), txEventPayload.Index) @@ -458,6 +461,7 @@ func TestEVMBatchRun(t *testing.T) { snapshot = snapshot.Append(state) require.Len(t, output.Events, batchCount) + txHashes := make(types.TransactionHashes, 0) for i, event := range output.Events { if i == batchCount { // last one is block executed continue @@ -471,6 +475,7 @@ func TestEVMBatchRun(t *testing.T) { event, err := types.DecodeTransactionEventPayload(cadenceEvent) require.NoError(t, err) + txHashes = append(txHashes, event.Hash) var logs []*gethTypes.Log err = rlp.DecodeBytes(event.Logs, &logs) require.NoError(t, err) @@ -490,7 +495,10 @@ func TestEVMBatchRun(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(155513), blockEventPayload.TotalGasUsed) - require.Len(t, blockEventPayload.TransactionHashes, 5) + require.Equal(t, + txHashes.RootHash(), + blockEventPayload.TransactionHashRoot, + ) // retrieve the values retrieveCode := []byte(fmt.Sprintf( @@ -972,10 +980,11 @@ func TestEVMAddressDeposit(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) - require.Len(t, blockEventPayload.TransactionHashes, 1) + + txHashes := types.TransactionHashes{txEventPayload.Hash} require.Equal(t, - txEventPayload.Hash, - blockEventPayload.TransactionHashes[0], + txHashes.RootHash(), + blockEventPayload.TransactionHashRoot, ) }) } diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go index 3939ee4d7ee..5a4c8cad303 100644 --- a/fvm/evm/handler/blockstore.go +++ b/fvm/evm/handler/blockstore.go @@ -104,8 +104,7 @@ func (bs *BlockStore) ResetBlockProposal() error { // CommitBlockProposal commits the block proposal to the chain func (bs *BlockStore) CommitBlockProposal(bp *types.BlockProposal) error { - // populate receipt root hash - bp.PopulateReceiptRoot() + bp.PopulateRoots() blockBytes, err := bp.Block.ToBytes() if err != nil { diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index fa7e53d8ceb..6042a59419b 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -213,8 +213,8 @@ func (h *ContractHandler) batchRun(rlpEncodedTxs [][]byte, coinbase types.Addres } // if there were no valid transactions skip emitting events - // and committing a new block - if len(bp.TransactionHashes) == 0 { + // and update the block proposal + if len(bp.TxHashes) == 0 { return res, nil } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 1a75c76f345..89653508292 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -90,7 +90,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { events := backend.Events() require.Len(t, events, 1) txEventPayload := testutils.TxEventToPayload(t, events[0], rootAddr) - + require.NoError(t, err) // check logs var logs []*gethTypes.Log err = rlp.DecodeBytes(txEventPayload.Logs, &logs) @@ -106,15 +106,11 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { events = backend.Events() require.Len(t, events, 2) blockEventPayload := testutils.BlockEventToPayload(t, events[1], rootAddr) - // make sure block transaction list references the above transaction id - - require.Len(t, blockEventPayload.TransactionHashes, 1) - eventTxID := blockEventPayload.TransactionHashes[0] // only one hash in block // make sure the transaction id included in the block transaction list is the same as tx sumbmitted assert.Equal( t, - evmTx.Hash(), - eventTxID, + types.TransactionHashes{txEventPayload.Hash}.RootHash(), + blockEventPayload.TransactionHashRoot, ) }) }) @@ -342,7 +338,7 @@ func TestHandler_COA(t *testing.T) { require.Len(t, events, 3) // Block level expected values - txHashes := make([]gethCommon.Hash, 0) + txHashes := make(types.TransactionHashes, 0) totalGasUsed := uint64(0) // deploy COA transaction event @@ -375,12 +371,12 @@ func TestHandler_COA(t *testing.T) { events = backend.Events() require.Len(t, events, 4) blockEventPayload := testutils.BlockEventToPayload(t, events[3], rootAddr) - for i, txHash := range txHashes { - require.Equal(t, - txHash, - blockEventPayload.TransactionHashes[i], - ) - } + assert.Equal( + t, + txHashes.RootHash(), + blockEventPayload.TransactionHashRoot, + ) + require.Equal(t, totalGasUsed, blockEventPayload.TotalGasUsed) // check gas usage diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 9c7cc2f78cb..4d0bc6d1a31 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -6,7 +6,7 @@ import "FlowToken" access(all) contract EVM { - // Entitlements enabling finer-graned access control on a CadenceOwnedAccount + // Entitlements enabling finer-grained access control on a CadenceOwnedAccount access(all) entitlement Validate access(all) entitlement Withdraw access(all) entitlement Call @@ -30,13 +30,13 @@ contract EVM { totalGasUsed: UInt64, // parent block hash parentHash: [UInt8; 32], - // hash of all the transaction receipts + // root hash of all the transaction receipts receiptRoot: [UInt8; 32], - // all the transactions included in the block - transactionHashes: [[UInt8; 32]] + // root hash of all the transaction hashes + transactionHashRoot: [UInt8; 32], ) - /// Transaction executed event is emitted everytime a transaction + /// Transaction executed event is emitted every time a transaction /// is executed by the EVM (even if failed). access(all) event TransactionExecuted( @@ -58,7 +58,7 @@ contract EVM { contractAddress: String, // RLP encoded logs logs: [UInt8], - // block height in which transaction was inclued + // block height in which transaction was included blockHeight: UInt64, /// captures the hex encoded data that is returned from /// the evm. For contract deployments diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index cd0c232e54e..7b5ab7b5c48 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "math/big" gethCommon "github.com/onflow/go-ethereum/common" @@ -32,8 +33,13 @@ type Block struct { // the same receipt root would be reported for block. ReceiptRoot gethCommon.Hash - // transaction hashes - TransactionHashes []gethCommon.Hash + // TransactionHashRoot returns the root hash of the transaction hashes + // included in this block. + // Note that despite similar functionality this is a bit different than TransactionRoot + // provided by Ethereum. TransactionRoot constructs a Merkle proof with leafs holding + // encoded transactions as values. But TransactionHashRoot uses transaction hash + // values as node values. Proofs are still compatible but might require an extra hashing step. + TransactionHashRoot gethCommon.Hash // stores gas used by all transactions included in the block. TotalGasUsed uint64 @@ -58,11 +64,12 @@ func NewBlock( totalSupply *big.Int, ) *Block { return &Block{ - ParentBlockHash: parentBlockHash, - Height: height, - Timestamp: timestamp, - TotalSupply: totalSupply, - ReceiptRoot: gethTypes.EmptyReceiptsHash, + ParentBlockHash: parentBlockHash, + Height: height, + Timestamp: timestamp, + TotalSupply: totalSupply, + ReceiptRoot: gethTypes.EmptyReceiptsHash, + TransactionHashRoot: gethTypes.EmptyRootHash, } } @@ -97,6 +104,9 @@ type BlockProposal struct { // Receipts keeps a order list of light receipts generated during block execution Receipts []LightReceipt + + // TxHashes keeps transaction hashes included in this block proposal + TxHashes TransactionHashes } // AppendTransaction appends a transaction hash to the list of transaction hashes of the block @@ -105,7 +115,7 @@ func (b *BlockProposal) AppendTransaction(res *Result) { if res == nil { return } - b.TransactionHashes = append(b.TransactionHashes, res.TxHash) + b.TxHashes = append(b.TxHashes, res.TxHash) r := res.LightReceipt(b.TotalGasUsed) if r == nil { return @@ -114,7 +124,24 @@ func (b *BlockProposal) AppendTransaction(res *Result) { b.TotalGasUsed = r.CumulativeGasUsed } -// PopulateReceiptRoot populates receipt root hash value +// PopulateRoots populates receiptRoot and transactionHashRoot +func (b *BlockProposal) PopulateRoots() { + // TODO: we can make this concurrent if needed in the future + // to improve the block production speed + b.PopulateTransactionHashRoot() + b.PopulateReceiptRoot() +} + +// PopulateTransactionHashRoot sets the transactionHashRoot +func (b *BlockProposal) PopulateTransactionHashRoot() { + if len(b.TransactionHashRoot) == 0 { + b.TransactionHashRoot = gethTypes.EmptyRootHash + return + } + b.TransactionHashRoot = b.TxHashes.RootHash() +} + +// PopulateReceiptRoot sets the receiptRoot func (b *BlockProposal) PopulateReceiptRoot() { if len(b.Receipts) == 0 { b.ReceiptRoot = gethTypes.EmptyReceiptsHash @@ -124,7 +151,6 @@ func (b *BlockProposal) PopulateReceiptRoot() { for i, lr := range b.Receipts { receipts[i] = lr.ToReceipt() } - b.ReceiptRoot = gethTypes.DeriveSha(receipts, gethTrie.NewStackTrie(nil)) } @@ -147,17 +173,31 @@ func NewBlockProposal( ) *BlockProposal { return &BlockProposal{ Block: Block{ - ParentBlockHash: parentBlockHash, - Height: height, - Timestamp: timestamp, - TotalSupply: totalSupply, - ReceiptRoot: gethTypes.EmptyRootHash, - TransactionHashes: make([]gethCommon.Hash, 0), + ParentBlockHash: parentBlockHash, + Height: height, + Timestamp: timestamp, + TotalSupply: totalSupply, + ReceiptRoot: gethTypes.EmptyRootHash, }, Receipts: make([]LightReceipt, 0), + TxHashes: make([]gethCommon.Hash, 0), } } +type TransactionHashes []gethCommon.Hash + +func (th TransactionHashes) Len() int { + return len(th) +} + +func (th TransactionHashes) EncodeIndex(index int, buffer *bytes.Buffer) { + buffer.Write(th[index].Bytes()) +} + +func (txs TransactionHashes) RootHash() gethCommon.Hash { + return gethTypes.DeriveSha(txs, gethTrie.NewStackTrie(nil)) +} + // todo remove this if confirmed we no longer need it on testnet, mainnet and previewnet. // Below block type section, defines earlier block types, @@ -229,6 +269,18 @@ type blockV5 struct { TransactionHashes []gethCommon.Hash } +// adds total gas used + +type blockV6 struct { + ParentBlockHash gethCommon.Hash + Height uint64 + Timestamp uint64 + TotalSupply *big.Int + ReceiptRoot gethCommon.Hash + TransactionHashes []gethCommon.Hash + TotalGasUsed uint64 +} + // decodeBlockBreakingChanges will try to decode the bytes into all // previous versions of block type, if it succeeds it will return the // migrated block, otherwise it will return nil. @@ -246,56 +298,62 @@ func decodeBlockBreakingChanges(encoded []byte) *Block { b1 := &blockV1{} if err := gethRLP.DecodeBytes(encoded, b1); err == nil { return &Block{ - ParentBlockHash: b1.ParentBlockHash, - Height: b1.Height, - TotalSupply: big.NewInt(int64(b1.TotalSupply)), - ReceiptRoot: b1.ReceiptRoot, - TransactionHashes: b1.TransactionHashes, + ParentBlockHash: b1.ParentBlockHash, + Height: b1.Height, + TotalSupply: big.NewInt(int64(b1.TotalSupply)), + ReceiptRoot: b1.ReceiptRoot, } } b2 := &blockV2{} if err := gethRLP.DecodeBytes(encoded, b2); err == nil { return &Block{ - ParentBlockHash: b2.ParentBlockHash, - Height: b2.Height, - TotalSupply: big.NewInt(int64(b2.TotalSupply)), - ReceiptRoot: b2.ReceiptRoot, - TransactionHashes: b2.TransactionHashes, + ParentBlockHash: b2.ParentBlockHash, + Height: b2.Height, + TotalSupply: big.NewInt(int64(b2.TotalSupply)), + ReceiptRoot: b2.ReceiptRoot, } } b3 := &blockV3{} if err := gethRLP.DecodeBytes(encoded, b3); err == nil { return &Block{ - ParentBlockHash: b3.ParentBlockHash, - Height: b3.Height, - TotalSupply: big.NewInt(int64(b3.TotalSupply)), - ReceiptRoot: b3.ReceiptRoot, - TransactionHashes: b3.TransactionHashes, + ParentBlockHash: b3.ParentBlockHash, + Height: b3.Height, + TotalSupply: big.NewInt(int64(b3.TotalSupply)), + ReceiptRoot: b3.ReceiptRoot, } } b4 := &blockV4{} if err := gethRLP.DecodeBytes(encoded, b4); err == nil { return &Block{ - ParentBlockHash: b4.ParentBlockHash, - Height: b4.Height, - TotalSupply: b4.TotalSupply, - ReceiptRoot: b4.ReceiptRoot, - TransactionHashes: b4.TransactionHashes, + ParentBlockHash: b4.ParentBlockHash, + Height: b4.Height, + TotalSupply: b4.TotalSupply, + ReceiptRoot: b4.ReceiptRoot, } } b5 := &blockV5{} if err := gethRLP.DecodeBytes(encoded, b5); err == nil { return &Block{ - ParentBlockHash: b5.ParentBlockHash, - Height: b5.Height, - Timestamp: b5.Timestamp, - TotalSupply: b5.TotalSupply, - ReceiptRoot: b5.ReceiptRoot, - TransactionHashes: b5.TransactionHashes, + ParentBlockHash: b5.ParentBlockHash, + Height: b5.Height, + Timestamp: b5.Timestamp, + TotalSupply: b5.TotalSupply, + ReceiptRoot: b5.ReceiptRoot, + } + } + + b6 := &blockV6{} + if err := gethRLP.DecodeBytes(encoded, b6); err == nil { + return &Block{ + ParentBlockHash: b5.ParentBlockHash, + Height: b5.Height, + Timestamp: b5.Timestamp, + TotalSupply: b5.TotalSupply, + ReceiptRoot: b5.ReceiptRoot, } } diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index 3bbb237bd8c..fc039334d9c 100644 --- a/fvm/evm/types/block_test.go +++ b/fvm/evm/types/block_test.go @@ -13,14 +13,12 @@ import ( func Test_BlockHash(t *testing.T) { b := Block{ - ParentBlockHash: gethCommon.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - Height: 1, - TotalSupply: big.NewInt(1000), - ReceiptRoot: gethCommon.Hash{0x2, 0x3, 0x4}, - TotalGasUsed: 135, - TransactionHashes: []gethCommon.Hash{ - gethCommon.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - }, + ParentBlockHash: gethCommon.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + Height: 1, + TotalSupply: big.NewInt(1000), + ReceiptRoot: gethCommon.Hash{0x2, 0x3, 0x4}, + TotalGasUsed: 135, + TransactionHashRoot: gethCommon.Hash{0x5, 0x6, 0x7}, } h1, err := b.Hash() @@ -39,22 +37,23 @@ func Test_BlockProposal(t *testing.T) { bp := NewBlockProposal(gethCommon.Hash{1}, 1, 0, nil) bp.AppendTransaction(nil) - require.Empty(t, bp.TransactionHashes) + require.Empty(t, bp.TxHashes) require.Equal(t, uint64(0), bp.TotalGasUsed) - bp.PopulateReceiptRoot() + bp.PopulateRoots() require.Equal(t, gethTypes.EmptyReceiptsHash, bp.ReceiptRoot) + require.Equal(t, gethTypes.EmptyRootHash, bp.TransactionHashRoot) res := &Result{ TxHash: gethCommon.Hash{2}, GasConsumed: 10, } bp.AppendTransaction(res) - require.Equal(t, res.TxHash, bp.TransactionHashes[0]) + require.Equal(t, res.TxHash, bp.TxHashes[0]) require.Equal(t, res.GasConsumed, bp.TotalGasUsed) require.Equal(t, *res.LightReceipt(0), bp.Receipts[0]) - bp.PopulateReceiptRoot() + bp.PopulateRoots() require.NotEqual(t, gethTypes.EmptyReceiptsHash, bp.ReceiptRoot) } @@ -96,7 +95,6 @@ func Test_DecodeBlocks(t *testing.T) { require.Equal(t, b.TotalSupply.Uint64(), bv1.TotalSupply) require.Equal(t, b.Height, bv1.Height) require.Equal(t, b.ParentBlockHash, bv1.ParentBlockHash) - require.Equal(t, b.TransactionHashes, bv1.TransactionHashes) require.Empty(t, b.Timestamp) require.Empty(t, b.TotalGasUsed) @@ -117,7 +115,6 @@ func Test_DecodeBlocks(t *testing.T) { require.Equal(t, b.TotalSupply.Uint64(), bv2.TotalSupply) require.Equal(t, b.Height, bv2.Height) require.Equal(t, b.ParentBlockHash, bv2.ParentBlockHash) - require.Equal(t, b.TransactionHashes, bv2.TransactionHashes) require.Empty(t, b.Timestamp) require.Empty(t, b.TotalGasUsed) @@ -137,7 +134,6 @@ func Test_DecodeBlocks(t *testing.T) { require.Equal(t, b.TotalSupply.Uint64(), bv3.TotalSupply) require.Equal(t, b.Height, bv3.Height) require.Equal(t, b.ParentBlockHash, bv3.ParentBlockHash) - require.Equal(t, b.TransactionHashes, bv3.TransactionHashes) require.Empty(t, b.Timestamp) require.Empty(t, b.TotalGasUsed) @@ -157,7 +153,6 @@ func Test_DecodeBlocks(t *testing.T) { require.Equal(t, b.TotalSupply, bv4.TotalSupply) require.Equal(t, b.Height, bv4.Height) require.Equal(t, b.ParentBlockHash, bv4.ParentBlockHash) - require.Equal(t, b.TransactionHashes, bv4.TransactionHashes) require.Empty(t, b.Timestamp) require.Empty(t, b.TotalGasUsed) @@ -179,6 +174,5 @@ func Test_DecodeBlocks(t *testing.T) { require.Equal(t, b.TotalSupply, bv5.TotalSupply) require.Equal(t, b.Height, bv5.Height) require.Equal(t, b.ParentBlockHash, bv5.ParentBlockHash) - require.Equal(t, b.TransactionHashes, bv5.TransactionHashes) require.Empty(t, b.TotalGasUsed) } diff --git a/fvm/evm/types/events.go b/fvm/evm/types/events.go index 92d133d0d99..4f5bec27c43 100644 --- a/fvm/evm/types/events.go +++ b/fvm/evm/types/events.go @@ -3,13 +3,12 @@ package types import ( "fmt" + "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/runtime/common" gethCommon "github.com/onflow/go-ethereum/common" "github.com/onflow/go-ethereum/rlp" - "github.com/onflow/cadence" - "github.com/onflow/flow-go/model/flow" ) @@ -34,14 +33,12 @@ type transactionEvent struct { Payload []byte // transaction RLP-encoded payload Result *Result // transaction execution result BlockHeight uint64 - BlockHash gethCommon.Hash } // NewTransactionEvent creates a new transaction event with the given parameters // - result: the result of the transaction execution // - payload: the RLP-encoded payload of the transaction // - blockHeight: the height of the block where the transaction is included -// - blockHash: the hash of the block where the transaction is included func NewTransactionEvent( result *Result, payload []byte, @@ -68,8 +65,6 @@ var transactionEventFields = []cadence.Field{ cadence.NewField("contractAddress", cadence.StringType), cadence.NewField("logs", cadenceArrayTypeOfUInt8), cadence.NewField("blockHeight", cadence.UInt64Type), - // todo we can remove hash and just reference block by height (evm-gateway dependency) - cadence.NewField("blockHash", cadenceHashType), cadence.NewField("returnedData", cadenceArrayTypeOfUInt8), cadence.NewField("precompiledCalls", cadenceArrayTypeOfUInt8), } @@ -117,7 +112,6 @@ func (p *transactionEvent) ToCadence(location common.Location) (cadence.Event, e deployedAddress, BytesToCadenceUInt8ArrayValue(encodedLogs), cadence.NewUInt64(p.BlockHeight), - HashToCadenceArrayValue(p.BlockHash), BytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), BytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), }).WithType(eventType), nil @@ -143,16 +137,10 @@ var blockEventFields = []cadence.Field{ cadence.NewField("totalGasUsed", cadence.UInt64Type), cadence.NewField("parentHash", cadenceHashType), cadence.NewField("receiptRoot", cadenceHashType), - cadence.NewField("transactionHashes", cadence.NewVariableSizedArrayType(cadenceHashType)), + cadence.NewField("transactionHashRoot", cadenceHashType), } func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) { - - transactionHashes := make([]cadence.Value, len(p.TransactionHashes)) - for i, hash := range p.TransactionHashes { - transactionHashes[i] = HashToCadenceArrayValue(hash) - } - blockHash, err := p.Hash() if err != nil { return cadence.Event{}, err @@ -173,20 +161,19 @@ func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) cadence.NewUInt64(p.TotalGasUsed), HashToCadenceArrayValue(p.ParentBlockHash), HashToCadenceArrayValue(p.ReceiptRoot), - cadence.NewArray(transactionHashes). - WithType(cadence.NewVariableSizedArrayType(cadenceHashType)), + HashToCadenceArrayValue(p.TransactionHashRoot), }).WithType(eventType), nil } type BlockEventPayload struct { - Height uint64 `cadence:"height"` - Hash gethCommon.Hash `cadence:"hash"` - Timestamp uint64 `cadence:"timestamp"` - TotalSupply cadence.Int `cadence:"totalSupply"` - TotalGasUsed uint64 `cadence:"totalGasUsed"` - ParentBlockHash gethCommon.Hash `cadence:"parentHash"` - ReceiptRoot gethCommon.Hash `cadence:"receiptRoot"` - TransactionHashes []gethCommon.Hash `cadence:"transactionHashes"` + Height uint64 `cadence:"height"` + Hash gethCommon.Hash `cadence:"hash"` + Timestamp uint64 `cadence:"timestamp"` + TotalSupply cadence.Int `cadence:"totalSupply"` + TotalGasUsed uint64 `cadence:"totalGasUsed"` + ParentBlockHash gethCommon.Hash `cadence:"parentHash"` + ReceiptRoot gethCommon.Hash `cadence:"receiptRoot"` + TransactionHashRoot gethCommon.Hash `cadence:"transactionHashRoot"` } // DecodeBlockEventPayload decodes Cadence event into block event payload. diff --git a/fvm/evm/types/events_test.go b/fvm/evm/types/events_test.go index a2f0eb5c066..1e74e93a26d 100644 --- a/fvm/evm/types/events_test.go +++ b/fvm/evm/types/events_test.go @@ -33,15 +33,13 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { t.Parallel() block := &types.Block{ - Height: 2, - Timestamp: 100, - TotalSupply: big.NewInt(1500), - ParentBlockHash: gethCommon.HexToHash("0x2813452cff514c3054ac9f40cd7ce1b016cc78ab7f99f1c6d49708837f6e06d1"), - ReceiptRoot: gethCommon.Hash{}, - TotalGasUsed: 15, - TransactionHashes: []gethCommon.Hash{ - gethCommon.HexToHash("0x70b67ce6710355acf8d69b2ea013d34e212bc4824926c5d26f189c1ca9667246"), - }, + Height: 2, + Timestamp: 100, + TotalSupply: big.NewInt(1500), + ParentBlockHash: gethCommon.HexToHash("0x2813452cff514c3054ac9f40cd7ce1b016cc78ab7f99f1c6d49708837f6e06d1"), + ReceiptRoot: gethCommon.Hash{}, + TotalGasUsed: 15, + TransactionHashRoot: gethCommon.HexToHash("0x70b67ce6710355acf8d69b2ea013d34e212bc4824926c5d26f189c1ca9667246"), } event := types.NewBlockEvent(block) @@ -62,11 +60,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, bep.TotalGasUsed, block.TotalGasUsed) assert.Equal(t, bep.ParentBlockHash, block.ParentBlockHash) assert.Equal(t, bep.ReceiptRoot, block.ReceiptRoot) - - assert.Equal(t, - bep.TransactionHashes, - block.TransactionHashes, - ) + assert.Equal(t, bep.TransactionHashRoot, block.TransactionHashRoot) v, err := ccf.Encode(ev) require.NoError(t, err) diff --git a/fvm/evm/types/utils.go b/fvm/evm/types/utils.go index 74af2548150..3ddb9073986 100644 --- a/fvm/evm/types/utils.go +++ b/fvm/evm/types/utils.go @@ -33,8 +33,22 @@ func HashToCadenceArrayValue(hash gethCommon.Hash) cadence.Array { WithType(cadenceHashType) } +// CadenceArrayValueToHash converts a Cadence array of type [UInt8;32] into EVM hash +func CadenceArrayValueToHash(value cadence.Array) (gethCommon.Hash, error) { + bytes, err := cadenceUInt8ArrayToBytes(value, cadenceHashType) + if err != nil { + return gethCommon.Hash{}, err + } + + return gethCommon.BytesToHash(bytes), nil +} + // CadenceUInt8ArrayValueToBytes converts a Cadence array of type [UInt8] into a byte slice ([]byte) func CadenceUInt8ArrayValueToBytes(a cadence.Value) ([]byte, error) { + return cadenceUInt8ArrayToBytes(a, cadenceArrayTypeOfUInt8) +} + +func cadenceUInt8ArrayToBytes(a cadence.Value, expectedType cadence.Type) ([]byte, error) { aa, ok := a.(cadence.Array) if !ok { return nil, fmt.Errorf("value is not an array") @@ -42,7 +56,7 @@ func CadenceUInt8ArrayValueToBytes(a cadence.Value) ([]byte, error) { arrayType := aa.Type() // if array type is empty, continue - if arrayType != nil && !arrayType.Equal(cadenceArrayTypeOfUInt8) { + if arrayType != nil && !arrayType.Equal(expectedType) { return nil, fmt.Errorf("invalid array type") } diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index 8887bb7cfac..e1fb925cdf1 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 = "9fcc5aba09e5432425082f0d847cdd9bcc007ecf8913a4858155911f4e37f532" +const GenesisStateCommitmentHex = "a9e1cdb1870f90ed3b9b7844e0f4f6b48c1a7439041f454785e42eaf6e2a6621" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "1bd8bb5766a37f0b9ee35946b44eca62f4c2e59184d7f961316014e78f86ad82" + return "69f5c914c2220dcfcc7a267c740e296cf34e50d59878d8ca99c682e65b092857" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "0f93d29eb9b876b5a687e6c8acd0de0730fec5fdbf2f0cce5c55074356821c6f" + return "b65ae8f791ef1775ad27c3705622a3e788bce3b053edc20365d7d90e48bebe60" }