Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flow EVM] populate receipt root hash in blocks #5542

Merged
merged 6 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions fvm/evm/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ func (h *ContractHandler) run(

bp.AppendTxHash(res.TxHash)

// TODO: in the future we might update the receipt hash here
// Populate receipt root
bp.PopulateReceiptRoot([]types.Result{*res})

blockHash, err := bp.Hash()
if err != nil {
Expand Down Expand Up @@ -307,7 +308,9 @@ func (h *ContractHandler) executeAndHandleCall(
}

bp.AppendTxHash(res.TxHash)
// TODO: in the future we might update the receipt hash here

// Populate receipt root
bp.PopulateReceiptRoot([]types.Result{*res})

if totalSupplyDiff != nil {
if deductSupplyDiff {
Expand Down
24 changes: 21 additions & 3 deletions fvm/evm/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
gethCommon "github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
gethCrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
gethRLP "github.com/ethereum/go-ethereum/rlp"
gethTrie "github.com/ethereum/go-ethereum/trie"
)

// Block represents a evm block.
Expand All @@ -22,6 +23,9 @@ type Block struct {
TotalSupply *big.Int

// ReceiptRoot returns the root hash of the receipts emitted in this block
// Note that this value won't be unique to each block, for example for the
// case of empty trie of receipts or a single receipt with no logs and failed state
// the same receipt root would be reported for block.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this relevant? what kind of transactions cause same receipt root? direct calls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't have any state root and based on the definition of the receipt hash its going to use the tx result (failed or not failed) as part of the computation. and for example, if two blocks both have a single transaction with failed status and same gas consumption (no logs, etc). they would reference the same receipt root hash.

ReceiptRoot gethCommon.Hash

// transaction hashes
Expand All @@ -30,7 +34,7 @@ type Block struct {

// ToBytes encodes the block into bytes
func (b *Block) ToBytes() ([]byte, error) {
return rlp.EncodeToBytes(b)
return gethRLP.EncodeToBytes(b)
}

// Hash returns the hash of the block
Expand All @@ -39,6 +43,20 @@ func (b *Block) Hash() (gethCommon.Hash, error) {
return gethCrypto.Keccak256Hash(data), err
}

// PopulateReceiptRoot populates receipt root with the given results
func (b *Block) PopulateReceiptRoot(results []Result) {
if len(results) == 0 {
b.ReceiptRoot = gethTypes.EmptyReceiptsHash
return
}

receipts := make(gethTypes.Receipts, len(results))
for i, res := range results {
receipts[i] = res.Receipt()
}
b.ReceiptRoot = gethTypes.DeriveSha(receipts, gethTrie.NewStackTrie(nil))
}

// AppendTxHash appends a transaction hash to the list of transaction hashes of the block
func (b *Block) AppendTxHash(txHash gethCommon.Hash) {
b.TransactionHashes = append(b.TransactionHashes, txHash)
Expand All @@ -64,7 +82,7 @@ func NewBlock(
// NewBlockFromBytes constructs a new block from encoded data
func NewBlockFromBytes(encoded []byte) (*Block, error) {
res := &Block{}
err := rlp.DecodeBytes(encoded, res)
err := gethRLP.DecodeBytes(encoded, res)
return res, err
}

Expand Down
10 changes: 10 additions & 0 deletions fvm/evm/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

gethCommon "github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -30,4 +31,13 @@ func Test_BlockHash(t *testing.T) {

// hashes should not equal if any data is changed
assert.NotEqual(t, h1, h2)

b.PopulateReceiptRoot(nil)
require.Equal(t, gethTypes.EmptyReceiptsHash, b.ReceiptRoot)

res := Result{
GasConsumed: 10,
}
b.PopulateReceiptRoot([]Result{res})
require.NotEqual(t, gethTypes.EmptyReceiptsHash, b.ReceiptRoot)
}
10 changes: 8 additions & 2 deletions fvm/evm/types/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ func (res *Result) VMErrorString() string {

// Receipt constructs an EVM-style receipt
// can be used by json-rpc and other integration to be returned.
func (res *Result) Receipt() *gethTypes.ReceiptForStorage {
//
// This is method is also used to construct block receipt root hash
// which requires the return receipt satisfy RLP encoding and cover these feilds
// Type (txType), PostState or Status, CumulativeGasUsed, Logs and Logs Bloom
// and for each log, Address, Topics, Data (consensus fields)
// During execution we also do fill in BlockNumber, TxIndex, Index (event index)
func (res *Result) Receipt() *gethTypes.Receipt {
receipt := &gethTypes.Receipt{
Type: res.TxType,
CumulativeGasUsed: res.GasConsumed, // TODO: update to capture cumulative
Expand All @@ -56,7 +62,7 @@ func (res *Result) Receipt() *gethTypes.ReceiptForStorage {
}

receipt.Bloom = gethTypes.CreateBloom(gethTypes.Receipts{receipt})
return (*gethTypes.ReceiptForStorage)(receipt)
return receipt
}

// Status captures the status of an interaction to the emulator
Expand Down
Loading