From d4568bbf72586f54de271f1465acf680eac6a936 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 15:06:30 -0700 Subject: [PATCH 01/11] replace tx hashes with tx root --- fvm/evm/handler/blockstore.go | 3 +- fvm/evm/handler/handler.go | 2 +- fvm/evm/stdlib/contract.cdc | 6 +- fvm/evm/types/block.go | 118 +++++++++++++++++++++++----------- fvm/evm/types/block_test.go | 28 ++++---- fvm/evm/types/events.go | 28 +++----- fvm/evm/types/events_test.go | 23 +++---- 7 files changed, 115 insertions(+), 93 deletions(-) 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 d41e5f591f7..f9931fda9e3 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -214,7 +214,7 @@ func (h *ContractHandler) batchRun(rlpEncodedTxs [][]byte, coinbase types.Addres // if there were no valid transactions skip emitting events // and commiting a new block - if len(bp.TransactionHashes) == 0 { + if len(bp.TxHashes) == 0 { return res, nil } diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 4856ada9537..33fb86d9257 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -30,10 +30,10 @@ contract EVM { totalGasUsed: UInt64, // parent block hash parentHash: String, - // hash of all the transaction receipts + // root hash of all the transaction receipts receiptRoot: String, - // all the transactions included in the block - transactionHashes: [String] + // root hash of all the transaction hashes + transactionHashRoot: String ) /// Transaction executed event is emitted everytime a transaction diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index cd0c232e54e..59c8ce28b61 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 @@ -97,6 +103,19 @@ 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 +} + +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()) } // AppendTransaction appends a transaction hash to the list of transaction hashes of the block @@ -105,7 +124,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,8 +133,8 @@ func (b *BlockProposal) AppendTransaction(res *Result) { b.TotalGasUsed = r.CumulativeGasUsed } -// PopulateReceiptRoot populates receipt root hash value -func (b *BlockProposal) PopulateReceiptRoot() { +// PopulateRoots populates receiptRoot and transactionHashRoot +func (b *BlockProposal) PopulateRoots() { if len(b.Receipts) == 0 { b.ReceiptRoot = gethTypes.EmptyReceiptsHash return @@ -126,6 +145,9 @@ func (b *BlockProposal) PopulateReceiptRoot() { } b.ReceiptRoot = gethTypes.DeriveSha(receipts, gethTrie.NewStackTrie(nil)) + + // TODO: we can make this concurrent if its + b.TransactionHashRoot = ComputeTransactionRootHash(b.TxHashes) } // ToBytes encodes the block proposal into bytes @@ -147,14 +169,14 @@ 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), } } @@ -229,6 +251,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,58 +280,68 @@ 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, } } return nil } + +func ComputeTransactionRootHash(txs TransactionHashes) gethCommon.Hash { + return gethTypes.DeriveSha(txs, gethTrie.NewStackTrie(nil)) +} diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index 3bbb237bd8c..053299e7ebc 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.EmptyReceiptsHash, bp.TxHashes) 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 bbf7b582a90..890185eea06 100644 --- a/fvm/evm/types/events.go +++ b/fvm/evm/types/events.go @@ -134,11 +134,6 @@ func NewBlockEvent(block *Block) *Event { } func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) { - hashes := make([]cadence.Value, len(p.TransactionHashes)) - for i, hash := range p.TransactionHashes { - hashes[i] = cadence.String(hash.String()) - } - blockHash, err := p.Hash() if err != nil { return cadence.Event{}, err @@ -155,10 +150,7 @@ func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) cadence.NewField("totalGasUsed", cadence.UInt64Type), cadence.NewField("parentHash", cadence.StringType), cadence.NewField("receiptRoot", cadence.StringType), - cadence.NewField( - "transactionHashes", - cadence.NewVariableSizedArrayType(cadence.StringType), - ), + cadence.NewField("transactionHashRoot", cadence.StringType), }, nil, ) @@ -171,19 +163,19 @@ func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) cadence.NewUInt64(p.TotalGasUsed), cadence.String(p.ParentBlockHash.String()), cadence.String(p.ReceiptRoot.String()), - cadence.NewArray(hashes).WithType(cadence.NewVariableSizedArrayType(cadence.StringType)), + cadence.String(p.TransactionHashRoot.String()), }).WithType(eventType), nil } type BlockEventPayload struct { - Height uint64 `cadence:"height"` - Hash string `cadence:"hash"` - Timestamp uint64 `cadence:"timestamp"` - TotalSupply cadence.Int `cadence:"totalSupply"` - TotalGasUsed uint64 `cadence:"totalGasUsed"` - ParentBlockHash string `cadence:"parentHash"` - ReceiptRoot string `cadence:"receiptRoot"` - TransactionHashes []cadence.String `cadence:"transactionHashes"` + Height uint64 `cadence:"height"` + Hash string `cadence:"hash"` + Timestamp uint64 `cadence:"timestamp"` + TotalSupply cadence.Int `cadence:"totalSupply"` + TotalGasUsed uint64 `cadence:"totalGasUsed"` + ParentBlockHash string `cadence:"parentHash"` + ReceiptRoot string `cadence:"receiptRoot"` + TransactionHashRoot string `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 9774203f801..458d5282786 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,12 +60,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, bep.TotalGasUsed, block.TotalGasUsed) assert.Equal(t, bep.ParentBlockHash, block.ParentBlockHash.Hex()) assert.Equal(t, bep.ReceiptRoot, block.ReceiptRoot.Hex()) - - hashes := make([]gethCommon.Hash, len(bep.TransactionHashes)) - for i, h := range bep.TransactionHashes { - hashes[i] = gethCommon.HexToHash(string(h)) - } - assert.Equal(t, hashes, block.TransactionHashes) + assert.Equal(t, bep.TransactionHashRoot, block.TransactionHashRoot.Hex()) v, err := ccf.Encode(ev) require.NoError(t, err) From 4efbf1d282be92039d4b9826c2c78ea59929042d Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 15:42:18 -0700 Subject: [PATCH 02/11] update handler test --- fvm/evm/handler/handler_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 79ddf82d23e..ddaaa33e999 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -91,7 +91,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { events := backend.Events() require.Len(t, events, 1) txEventPayload := testutils.TxEventToPayload(t, events[0], rootAddr) - + txHash := gethCommon.HexToHash(txEventPayload.Hash) // check logs encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(txEventPayload.Logs) require.NoError(t, err) @@ -110,14 +110,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().String(), - string(eventTxID), + types.ComputeTransactionRootHash([]gethCommon.Hash{txHash}).String(), + string(blockEventPayload.TransactionHashRoot), ) }) }) @@ -384,9 +381,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.Hex(), string(blockEventPayload.TransactionHashes[i])) - } + assert.Equal( + t, + types.ComputeTransactionRootHash(txHashes).String(), + string(blockEventPayload.TransactionHashRoot), + ) + require.Equal(t, totalGasUsed, blockEventPayload.TotalGasUsed) // check gas usage From b15ea6e801e2234c4c204aab50b5ac9a85c6289b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 15:47:31 -0700 Subject: [PATCH 03/11] update emulator tests --- fvm/evm/evm_test.go | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index e0866807afc..5813cb78a49 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/onflow/cadence/encoding/ccf" + gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" gethParams "github.com/onflow/go-ethereum/params" "github.com/onflow/go-ethereum/rlp" @@ -106,6 +107,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,11 +119,13 @@ 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) + txHash := gethCommon.HexToHash(txEventPayload.Hash) + require.Equal(t, + types.ComputeTransactionRootHash([]gethCommon.Hash{txHash}).String(), + string(blockEventPayload.TransactionHashRoot), + ) + require.NotEmpty(t, blockEventPayload.ReceiptRoot) txPayload, err := types.CadenceUInt8ArrayValueToBytes(txEventPayload.Payload) require.NoError(t, err) @@ -464,6 +469,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 @@ -477,6 +483,7 @@ func TestEVMBatchRun(t *testing.T) { event, err := types.DecodeTransactionEventPayload(cadenceEvent) require.NoError(t, err) + txHashes = append(txHashes, gethCommon.HexToHash(event.Hash)) encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(event.Logs) require.NoError(t, err) @@ -499,7 +506,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, + types.ComputeTransactionRootHash(txHashes).String(), + string(blockEventPayload.TransactionHashRoot), + ) // retrieve the values retrieveCode := []byte(fmt.Sprintf( @@ -982,8 +992,12 @@ func TestEVMAddressDeposit(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) - require.Len(t, blockEventPayload.TransactionHashes, 1) - require.Equal(t, txEventPayload.Hash, string(blockEventPayload.TransactionHashes[0])) + + txHash := gethCommon.HexToHash(txEventPayload.Hash) + require.Equal(t, + types.ComputeTransactionRootHash([]gethCommon.Hash{txHash}).String(), + string(blockEventPayload.TransactionHashRoot), + ) }) } From dec28c94ef736ef7deb9c9401f9f31783f4cbad3 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 18:38:26 -0700 Subject: [PATCH 04/11] clean up --- fvm/evm/evm_test.go | 9 +++++---- fvm/evm/handler/handler_test.go | 6 +++--- fvm/evm/types/block.go | 30 +++++++++++++++--------------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 5813cb78a49..f63ab8f6b1d 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -120,9 +120,9 @@ func TestEVMRun(t *testing.T) { require.Equal(t, uint64(43785), blockEventPayload.TotalGasUsed) require.NotEmpty(t, blockEventPayload.Hash) - txHash := gethCommon.HexToHash(txEventPayload.Hash) + txHashes := types.TransactionHashes{gethCommon.HexToHash(txEventPayload.Hash)} require.Equal(t, - types.ComputeTransactionRootHash([]gethCommon.Hash{txHash}).String(), + txHashes.RootHash().String(), string(blockEventPayload.TransactionHashRoot), ) require.NotEmpty(t, blockEventPayload.ReceiptRoot) @@ -507,7 +507,7 @@ func TestEVMBatchRun(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(155513), blockEventPayload.TotalGasUsed) require.Equal(t, - types.ComputeTransactionRootHash(txHashes).String(), + txHashes.RootHash().String(), string(blockEventPayload.TransactionHashRoot), ) @@ -994,8 +994,9 @@ func TestEVMAddressDeposit(t *testing.T) { require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) txHash := gethCommon.HexToHash(txEventPayload.Hash) + txHashes := types.TransactionHashes{txHash} require.Equal(t, - types.ComputeTransactionRootHash([]gethCommon.Hash{txHash}).String(), + txHashes.RootHash().String(), string(blockEventPayload.TransactionHashRoot), ) }) diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index ddaaa33e999..ea92fc84c2e 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -113,7 +113,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { // make sure the transaction id included in the block transaction list is the same as tx sumbmitted assert.Equal( t, - types.ComputeTransactionRootHash([]gethCommon.Hash{txHash}).String(), + types.TransactionHashes{txHash}.RootHash().String(), string(blockEventPayload.TransactionHashRoot), ) }) @@ -342,7 +342,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 @@ -383,7 +383,7 @@ func TestHandler_COA(t *testing.T) { blockEventPayload := testutils.BlockEventToPayload(t, events[3], rootAddr) assert.Equal( t, - types.ComputeTransactionRootHash(txHashes).String(), + txHashes.RootHash().String(), string(blockEventPayload.TransactionHashRoot), ) diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index 59c8ce28b61..0d74ead2c4b 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -108,16 +108,6 @@ type BlockProposal struct { TxHashes TransactionHashes } -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()) -} - // AppendTransaction appends a transaction hash to the list of transaction hashes of the block // and also update the receipts func (b *BlockProposal) AppendTransaction(res *Result) { @@ -147,7 +137,7 @@ func (b *BlockProposal) PopulateRoots() { b.ReceiptRoot = gethTypes.DeriveSha(receipts, gethTrie.NewStackTrie(nil)) // TODO: we can make this concurrent if its - b.TransactionHashRoot = ComputeTransactionRootHash(b.TxHashes) + b.TransactionHashRoot = b.TxHashes.RootHash() } // ToBytes encodes the block proposal into bytes @@ -180,6 +170,20 @@ func NewBlockProposal( } } +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, @@ -341,7 +345,3 @@ func decodeBlockBreakingChanges(encoded []byte) *Block { return nil } - -func ComputeTransactionRootHash(txs TransactionHashes) gethCommon.Hash { - return gethTypes.DeriveSha(txs, gethTrie.NewStackTrie(nil)) -} From c36f11f78a995159ab23ec6168075806e3b8c703 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 22:49:46 -0700 Subject: [PATCH 05/11] handle empty case --- fvm/evm/stdlib/contract.cdc | 2 +- fvm/evm/types/block.go | 21 +++++++++++++++++---- fvm/evm/types/events.go | 16 ++++++++-------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 7da6f7c60ae..9af31e0dc22 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -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 0d74ead2c4b..b3dbf568c12 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -125,6 +125,23 @@ func (b *BlockProposal) AppendTransaction(res *Result) { // 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 return @@ -133,11 +150,7 @@ func (b *BlockProposal) PopulateRoots() { for i, lr := range b.Receipts { receipts[i] = lr.ToReceipt() } - b.ReceiptRoot = gethTypes.DeriveSha(receipts, gethTrie.NewStackTrie(nil)) - - // TODO: we can make this concurrent if its - b.TransactionHashRoot = b.TxHashes.RootHash() } // ToBytes encodes the block proposal into bytes diff --git a/fvm/evm/types/events.go b/fvm/evm/types/events.go index 890185eea06..efe9c08d829 100644 --- a/fvm/evm/types/events.go +++ b/fvm/evm/types/events.go @@ -168,14 +168,14 @@ func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) } type BlockEventPayload struct { - Height uint64 `cadence:"height"` - Hash string `cadence:"hash"` - Timestamp uint64 `cadence:"timestamp"` - TotalSupply cadence.Int `cadence:"totalSupply"` - TotalGasUsed uint64 `cadence:"totalGasUsed"` - ParentBlockHash string `cadence:"parentHash"` - ReceiptRoot string `cadence:"receiptRoot"` - TransactionHashRoot string `cadence:"transactionHashRoot"` + Height uint64 `cadence:"height"` + Hash string `cadence:"hash"` + Timestamp uint64 `cadence:"timestamp"` + TotalSupply cadence.Int `cadence:"totalSupply"` + TotalGasUsed uint64 `cadence:"totalGasUsed"` + ParentBlockHash string `cadence:"parentHash"` + ReceiptRoot string `cadence:"receiptRoot"` + TransactionHashRoot cadence.Array `cadence:"transactionHashRoot"` } // DecodeBlockEventPayload decodes Cadence event into block event payload. From f22d9fcd200d4cb20327342cf89b357abcae6666 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 22:51:06 -0700 Subject: [PATCH 06/11] revert unwanted changeg --- fvm/evm/types/events.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fvm/evm/types/events.go b/fvm/evm/types/events.go index efe9c08d829..890185eea06 100644 --- a/fvm/evm/types/events.go +++ b/fvm/evm/types/events.go @@ -168,14 +168,14 @@ func (p *blockEvent) ToCadence(location common.Location) (cadence.Event, error) } type BlockEventPayload struct { - Height uint64 `cadence:"height"` - Hash string `cadence:"hash"` - Timestamp uint64 `cadence:"timestamp"` - TotalSupply cadence.Int `cadence:"totalSupply"` - TotalGasUsed uint64 `cadence:"totalGasUsed"` - ParentBlockHash string `cadence:"parentHash"` - ReceiptRoot string `cadence:"receiptRoot"` - TransactionHashRoot cadence.Array `cadence:"transactionHashRoot"` + Height uint64 `cadence:"height"` + Hash string `cadence:"hash"` + Timestamp uint64 `cadence:"timestamp"` + TotalSupply cadence.Int `cadence:"totalSupply"` + TotalGasUsed uint64 `cadence:"totalGasUsed"` + ParentBlockHash string `cadence:"parentHash"` + ReceiptRoot string `cadence:"receiptRoot"` + TransactionHashRoot string `cadence:"transactionHashRoot"` } // DecodeBlockEventPayload decodes Cadence event into block event payload. From 994284e44d33c8ec9fd292aa2d184a0c3f0e4f02 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 22:54:53 -0700 Subject: [PATCH 07/11] lint fix --- fvm/evm/types/events.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fvm/evm/types/events.go b/fvm/evm/types/events.go index 890185eea06..a309bef33fe 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" ) From fd8251fb576ea45de0b5c918a0b9a81d00069a9b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 17 Jul 2024 12:49:39 -0700 Subject: [PATCH 08/11] typos --- fvm/evm/stdlib/contract.cdc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index e374c7a94c8..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 @@ -36,7 +36,7 @@ contract EVM { 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( From 5cf561c616103910e9e37521b1d7f39fcbc68769 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 17 Jul 2024 13:21:25 -0700 Subject: [PATCH 09/11] state commit change due to 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 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/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" } From 92eed24863894fb051046a06cbd1b0483cc4c44b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 17 Jul 2024 14:52:29 -0700 Subject: [PATCH 10/11] fix test --- fvm/evm/types/block_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index 053299e7ebc..fc039334d9c 100644 --- a/fvm/evm/types/block_test.go +++ b/fvm/evm/types/block_test.go @@ -42,7 +42,7 @@ func Test_BlockProposal(t *testing.T) { bp.PopulateRoots() require.Equal(t, gethTypes.EmptyReceiptsHash, bp.ReceiptRoot) - require.Equal(t, gethTypes.EmptyReceiptsHash, bp.TxHashes) + require.Equal(t, gethTypes.EmptyRootHash, bp.TransactionHashRoot) res := &Result{ TxHash: gethCommon.Hash{2}, From 76ea3b8af139403411737476cef85ab9694157ac Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 17 Jul 2024 15:16:02 -0700 Subject: [PATCH 11/11] remove unused block height --- fvm/evm/types/block.go | 11 ++++++----- fvm/evm/types/events.go | 5 ----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index b3dbf568c12..7b5ab7b5c48 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -64,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, } } diff --git a/fvm/evm/types/events.go b/fvm/evm/types/events.go index a263a03f634..4f5bec27c43 100644 --- a/fvm/evm/types/events.go +++ b/fvm/evm/types/events.go @@ -33,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, @@ -67,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), } @@ -116,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