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

chore(core/types): Block libevm Body and RLP hooks #750

Draft
wants to merge 5 commits into
base: qdm12/core/types/body-libevm
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2179,7 +2179,7 @@ func golangBindings(t *testing.T, overload bool) {
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
}
replacer = exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ava-labs/[email protected]", "-replace", "github.com/ava-labs/libevm=github.com/ava-labs/[email protected]20250121162040-b9e5186290df")
replacer = exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ava-labs/[email protected]", "-replace", "github.com/ava-labs/libevm=github.com/ava-labs/[email protected]20250122094956-11c780f117f8")
replacer.Dir = pkg
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
Expand Down
6 changes: 3 additions & 3 deletions consensus/dummy/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ func (eng *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types
if extDataGasUsed == nil {
extDataGasUsed = new(big.Int).Set(common.Big0)
}
if blockExtDataGasUsed := block.ExtDataGasUsed(); blockExtDataGasUsed == nil || !blockExtDataGasUsed.IsUint64() || blockExtDataGasUsed.Cmp(extDataGasUsed) != 0 {
if blockExtDataGasUsed := types.BlockExtDataGasUsed(block); blockExtDataGasUsed == nil || !blockExtDataGasUsed.IsUint64() || blockExtDataGasUsed.Cmp(extDataGasUsed) != 0 {
return fmt.Errorf("invalid extDataGasUsed: have %d, want %d", blockExtDataGasUsed, extDataGasUsed)
}
blockGasCostStep := ApricotPhase4BlockGasCostStep
Expand All @@ -422,13 +422,13 @@ func (eng *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types
parent.Time, block.Time(),
)
// Verify the BlockGasCost set in the header matches the calculated value.
if blockBlockGasCost := block.BlockGasCost(); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 {
if blockBlockGasCost := types.BlockGasCost(block); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 {
return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost)
}
// Verify the block fee was paid.
if err := eng.verifyBlockFee(
block.BaseFee(),
block.BlockGasCost(),
types.BlockGasCost(block),
block.Transactions(),
receipts,
contribution,
Expand Down
2 changes: 1 addition & 1 deletion core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1367,7 +1367,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error {
"parentHash", block.ParentHash(),
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
"elapsed", common.PrettyDuration(time.Since(start)),
"root", block.Root(), "baseFeePerGas", block.BaseFee(), "blockGasCost", block.BlockGasCost(),
"root", block.Root(), "baseFeePerGas", block.BaseFee(), "blockGasCost", types.BlockGasCost(block),
)

processedBlockGasUsedCounter.Inc(int64(block.GasUsed()))
Expand Down
3 changes: 2 additions & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Blo
}

// Configure any stateful precompiles that should be enabled in the genesis.
err = ApplyPrecompileActivations(g.Config, nil, types.NewBlockWithHeader(head), statedb)
block := types.NewBlockWithHeader(head)
err = ApplyPrecompileActivations(g.Config, nil, types.WrapWithTimestamp(block), statedb)
if err != nil {
panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err))
}
Expand Down
5 changes: 4 additions & 1 deletion core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,10 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
if body == nil {
return nil
}
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithExtData(body.Version, body.ExtData)
block := types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
bodyExtra := types.GetBodyExtra(body)
block = types.WithBlockExtra(block, bodyExtra.Version, bodyExtra.ExtData, false)
return block
}

// WriteBlock serializes a block into the database, header and body separately.
Expand Down
2 changes: 1 addition & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state
)

// Configure any upgrades that should go into effect during this block.
err := ApplyUpgrades(p.config, &parent.Time, block, statedb)
err := ApplyUpgrades(p.config, &parent.Time, types.WrapWithTimestamp(block), statedb)
if err != nil {
log.Error("failed to configure precompiles processing block", "hash", block.Hash(), "number", block.NumberU64(), "timestamp", block.Time(), "err", err)
return nil, nil, 0, err
Expand Down
300 changes: 0 additions & 300 deletions core/types/block.go
Original file line number Diff line number Diff line change
@@ -1,301 +1 @@
// (c) 2019-2020, Ava Labs, Inc.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// Package types contains data types related to Ethereum consensus.
package types

import (
"encoding/binary"
"io"
"math/big"
"sync/atomic"

"github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/rlp"
)

// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
Transactions []*Transaction
Uncles []*Header
Version uint32
ExtData *[]byte `rlp:"nil"`
}

// Block represents an Ethereum block.
//
// Note the Block type tries to be 'immutable', and contains certain caches that rely
// on that. The rules around block immutability are as follows:
//
// - We copy all data when the block is constructed. This makes references held inside
// the block independent of whatever value was passed in.
//
// - We copy all header data on access. This is because any change to the header would mess
// up the cached hash and size values in the block. Calling code is expected to take
// advantage of this to avoid over-allocating!
//
// - When new body data is attached to the block, a shallow copy of the block is returned.
// This ensures block modifications are race-free.
//
// - We do not copy body data on access because it does not affect the caches, and also
// because it would be too expensive.
type Block struct {
header *Header
uncles []*Header
transactions Transactions

// Coreth specific data structures to support atomic transactions
version uint32
extdata *[]byte

// caches
hash atomic.Value
size atomic.Value
}

// "external" block encoding. used for eth protocol, etc.
type extblock struct {
Header *Header
Txs []*Transaction
Uncles []*Header
Version uint32
ExtData *[]byte `rlp:"nil"`
}

// NewBlock creates a new block. The input data is copied, changes to header and to the
// field values will not affect the block.
//
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles
// and receipts.
func NewBlock(
header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher,
) *Block {
b := &Block{header: CopyHeader(header)}

// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
b.header.TxHash = EmptyTxsHash
} else {
b.header.TxHash = DeriveSha(Transactions(txs), hasher)
b.transactions = make(Transactions, len(txs))
copy(b.transactions, txs)
}

if len(receipts) == 0 {
b.header.ReceiptHash = EmptyReceiptsHash
} else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
b.header.Bloom = CreateBloom(receipts)
}

if len(uncles) == 0 {
b.header.UncleHash = EmptyUncleHash
} else {
b.header.UncleHash = CalcUncleHash(uncles)
b.uncles = make([]*Header, len(uncles))
for i := range uncles {
b.uncles[i] = CopyHeader(uncles[i])
}
}

return b
}

// DecodeRLP decodes a block from RLP.
func (b *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock
_, size, _ := s.Kind()
if err := s.Decode(&eb); err != nil {
return err
}
b.header, b.uncles, b.transactions, b.version, b.extdata = eb.Header, eb.Uncles, eb.Txs, eb.Version, eb.ExtData
b.size.Store(rlp.ListSize(size))
return nil
}

// EncodeRLP serializes a block as RLP.
func (b *Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &extblock{
Header: b.header,
Txs: b.transactions,
Uncles: b.uncles,
Version: b.version,
ExtData: b.extdata,
})
}

// Body returns the non-header content of the block.
// Note the returned data is not an independent copy.
func (b *Block) Body() *Body {
return &Body{b.transactions, b.uncles, b.version, b.extdata}
}

// Accessors for body data. These do not return a copy because the content
// of the body slices does not affect the cached hash/size in block.

func (b *Block) Uncles() []*Header { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }

func (b *Block) Transaction(hash common.Hash) *Transaction {
for _, transaction := range b.transactions {
if transaction.Hash() == hash {
return transaction
}
}
return nil
}

// Header returns the block header (as a copy).
func (b *Block) Header() *Header {
return CopyHeader(b.header)
}

// Header value accessors. These do copy!

func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
func (b *Block) GasLimit() uint64 { return b.header.GasLimit }
func (b *Block) GasUsed() uint64 { return b.header.GasUsed }
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
func (b *Block) Time() uint64 { return b.header.Time }
func (b *Block) Timestamp() uint64 { return b.header.Time }

func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() }
func (b *Block) MixDigest() common.Hash { return b.header.MixDigest }
func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
func (b *Block) Bloom() Bloom { return b.header.Bloom }
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
func (b *Block) Root() common.Hash { return b.header.Root }
func (b *Block) ParentHash() common.Hash { return b.header.ParentHash }
func (b *Block) TxHash() common.Hash { return b.header.TxHash }
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
func (b *Block) UncleHash() common.Hash { return b.header.UncleHash }
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }

func (b *Block) BaseFee() *big.Int {
if b.header.BaseFee == nil {
return nil
}
return new(big.Int).Set(b.header.BaseFee)
}

func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot }

func (b *Block) ExcessBlobGas() *uint64 {
var excessBlobGas *uint64
if b.header.ExcessBlobGas != nil {
excessBlobGas = new(uint64)
*excessBlobGas = *b.header.ExcessBlobGas
}
return excessBlobGas
}

func (b *Block) BlobGasUsed() *uint64 {
var blobGasUsed *uint64
if b.header.BlobGasUsed != nil {
blobGasUsed = new(uint64)
*blobGasUsed = *b.header.BlobGasUsed
}
return blobGasUsed
}

func (b *Block) BlockGasCost() *big.Int {
if HeaderExtras(b.header).BlockGasCost == nil {
return nil
}
return new(big.Int).Set(HeaderExtras(b.header).BlockGasCost)
}

// Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previously cached value.
func (b *Block) Size() uint64 {
if size := b.size.Load(); size != nil {
return size.(uint64)
}
c := writeCounter(0)
rlp.Encode(&c, b)
b.size.Store(uint64(c))
return uint64(c)
}

type writeCounter uint64

func (c *writeCounter) Write(b []byte) (int, error) {
*c += writeCounter(len(b))
return len(b), nil
}

func CalcUncleHash(uncles []*Header) common.Hash {
if len(uncles) == 0 {
return EmptyUncleHash
}
return rlpHash(uncles)
}

// NewBlockWithHeader creates a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
func NewBlockWithHeader(header *Header) *Block {
return &Block{header: CopyHeader(header)}
}

// WithSeal returns a new block with the data from b but the header replaced with
// the sealed one.
func (b *Block) WithSeal(header *Header) *Block {
return &Block{
header: CopyHeader(header),
transactions: b.transactions,
uncles: b.uncles,
}
}

// WithBody returns a copy of the block with the given transaction and uncle contents.
func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
block := &Block{
header: b.header,
transactions: make([]*Transaction, len(transactions)),
uncles: make([]*Header, len(uncles)),
}
copy(block.transactions, transactions)
for i := range uncles {
block.uncles[i] = CopyHeader(uncles[i])
}
return block
}

// Hash returns the keccak256 hash of b's header.
// The hash is computed on the first call and cached thereafter.
func (b *Block) Hash() common.Hash {
if hash := b.hash.Load(); hash != nil {
return hash.(common.Hash)
}
v := b.header.Hash()
b.hash.Store(v)
return v
}

type Blocks []*Block
Loading
Loading