Skip to content

Commit

Permalink
Move atomic hash from Block to Header (#11513)
Browse files Browse the repository at this point in the history
Reason 1: `NewEVMBlockContext` -> `engine.Author(header)` -> `*Bor` -> 

```
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
   return address, nil
}
```	
it's hard and not so useful to pass `block` through this abstractions.

So, RPC does hash every requested header here. 

Reason 2: In E3: many RPC will read 1 transaction only. So, likely we
will have many places where no `Block` object.
  • Loading branch information
AskAlexSharov committed Aug 14, 2024
1 parent e9b7e7e commit f4322d9
Show file tree
Hide file tree
Showing 20 changed files with 113 additions and 99 deletions.
4 changes: 2 additions & 2 deletions cmd/devnet/blocks/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func BlockWaiter(ctx context.Context, handler BlockHandler) (Waiter, context.Can

var err error

headers := make(chan types.Header)
headers := make(chan *types.Header)
waiter.headersSub, err = node.Subscribe(ctx, requests.Methods.ETHNewHeads, headers)

if err != nil {
Expand All @@ -133,7 +133,7 @@ func BlockWaiter(ctx context.Context, handler BlockHandler) (Waiter, context.Can
return wait{waiter}, cancel
}

func (c *blockWaiter) receive(ctx context.Context, node devnet.Node, headers chan types.Header) {
func (c *blockWaiter) receive(ctx context.Context, node devnet.Node, headers chan *types.Header) {
blockMap := map[libcommon.Hash]*requests.Block{}

defer close(c.result)
Expand Down
2 changes: 1 addition & 1 deletion consensus/aura/aura.go
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ func (c *AuRa) FinalizeAndAssemble(config *chain.Config, header *types.Header, s
}

// Assemble and return the final block for sealing
return types.NewBlock(header, outTxs, uncles, outReceipts, withdrawals, requests), outTxs, outReceipts, nil
return types.NewBlockForAsembling(header, outTxs, uncles, outReceipts, withdrawals, requests), outTxs, outReceipts, nil
}

// Authorize injects a private key into the consensus engine to mint new blocks
Expand Down
9 changes: 2 additions & 7 deletions consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ var (
NonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.

uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
emptyUncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.

DiffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures
diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures
Expand Down Expand Up @@ -386,8 +386,6 @@ func (c *Clique) Finalize(config *chain.Config, header *types.Header, state *sta
txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal, requests types.Requests,
chain consensus.ChainReader, syscall consensus.SystemCall, logger log.Logger,
) (types.Transactions, types.Receipts, types.Requests, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.UncleHash = types.CalcUncleHash(nil)
return txs, r, nil, nil
}

Expand All @@ -396,11 +394,8 @@ func (c *Clique) Finalize(config *chain.Config, header *types.Header, state *sta
func (c *Clique) FinalizeAndAssemble(chainConfig *chain.Config, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal, requests types.Requests, chain consensus.ChainReader, syscall consensus.SystemCall, call consensus.Call, logger log.Logger,
) (*types.Block, types.Transactions, types.Receipts, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.UncleHash = types.CalcUncleHash(nil)

// Assemble and return the final block for sealing
return types.NewBlock(header, txs, nil, receipts, withdrawals, requests), txs, receipts, nil
return types.NewBlockForAsembling(header, txs, nil, receipts, withdrawals, requests), txs, receipts, nil
}

// Authorize injects a private key into the consensus engine to mint new blocks
Expand Down
2 changes: 1 addition & 1 deletion consensus/clique/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
return errInvalidMixDigest
}
// Ensure that the block doesn't contain any uncles which are meaningless in PoA
if header.UncleHash != uncleHash {
if header.UncleHash != emptyUncleHash {
return errInvalidUncleHash
}
// Ensure that the block's difficulty is meaningful (may not be correct at this point)
Expand Down
3 changes: 0 additions & 3 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,6 @@ type EngineWriter interface {

// Finalize runs any post-transaction state modifications (e.g. block rewards)
// but does not assemble the block.
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(config *chain.Config, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal, requests types.Requests, chain ChainReader, syscall SystemCall, logger log.Logger,
) (types.Transactions, types.Receipts, types.Requests, error)
Expand Down
2 changes: 1 addition & 1 deletion consensus/mainnet/mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func (b baseMainnet) FinalizeAndAssemble(chainConfig *chain.Config, header *type
return nil, nil, nil, err
}
// Header seems complete, assemble into a block and return
return types.NewBlock(header, outTxs, uncles, outR, withdrawals, requests), outTxs, outR, nil
return types.NewBlockForAsembling(header, outTxs, uncles, outR, withdrawals, requests), outTxs, outR, nil
}

// AccumulateRewards returns rewards for a given block. The mining reward consists
Expand Down
4 changes: 2 additions & 2 deletions consensus/merge/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func (s *Merge) FinalizeAndAssemble(config *chain.Config, header *types.Header,
rs = make(types.Requests, 0)
}
}
return types.NewBlock(header, outTxs, uncles, outReceipts, withdrawals, rs), outTxs, outReceipts, nil
return types.NewBlockForAsembling(header, outTxs, uncles, outReceipts, withdrawals, rs), outTxs, outReceipts, nil
}

func (s *Merge) SealHash(header *types.Header) (hash libcommon.Hash) {
Expand Down Expand Up @@ -331,7 +331,7 @@ func (s *Merge) verifyHeader(chain consensus.ChainHeaderReader, header, parent *
}

func (s *Merge) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
if !misc.IsPoSHeader(block.Header()) {
if !misc.IsPoSHeader(block.HeaderNoCopy()) {
return s.eth1Engine.Seal(chain, block, results, stop)
}
return nil
Expand Down
15 changes: 7 additions & 8 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ func GenerateChain(config *chain.Config, parent *types.Block, engine consensus.E
b.header.Root = libcommon.BytesToHash(stateRoot)

// Recreating block to make sure Root makes it into the header
block := types.NewBlock(b.header, b.txs, b.uncles, b.receipts, nil /* withdrawals */, nil /*requests*/)
block := types.NewBlockForAsembling(b.header, b.txs, b.uncles, b.receipts, nil /* withdrawals */, nil /*requests*/)
return block, b.receipts, nil
}
return nil, nil, errors.New("no engine to generate blocks")
Expand Down Expand Up @@ -564,13 +564,12 @@ func CalcHashRootForTests(tx kv.RwTx, header *types.Header, histV4, trace bool)
}

func MakeEmptyHeader(parent *types.Header, chainConfig *chain.Config, timestamp uint64, targetGasLimit *uint64) *types.Header {
header := &types.Header{
Root: parent.Root,
ParentHash: parent.Hash(),
Number: new(big.Int).Add(parent.Number, libcommon.Big1),
Difficulty: libcommon.Big0,
Time: timestamp,
}
header := types.NewEmptyHeaderForAssembling()
header.Root = parent.Root
header.ParentHash = parent.Hash()
header.Number = new(big.Int).Add(parent.Number, libcommon.Big1)
header.Difficulty = libcommon.Big0
header.Time = timestamp

parentGasLimit := parent.GasLimit
// Set baseFee and GasLimit if we are on an EIP-1559 chain
Expand Down
63 changes: 43 additions & 20 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ type Header struct {
Verkle bool
VerkleProof []byte
VerkleKeyVals []verkle.KeyValuePair

// by default all headers are immutable
// but assembling/mining may use `NewEmptyHeaderForAssembling` to create temporary mutable Header object
// then pass it to `block.WithSeal(header)` - to produce new block with immutable `Header`
mutable bool
hash atomic.Pointer[libcommon.Hash]
}

// NewEmptyHeaderForAssembling - returns mutable header object - for assembling/sealing/etc...
// when sealing done - `block.WithSeal(header)` called - which producing new block with immutable `Header`
// by default all headers are immutable
func NewEmptyHeaderForAssembling() *Header {
return &Header{mutable: true}
}

func (h *Header) EncodingSize() int {
Expand Down Expand Up @@ -567,8 +580,17 @@ type headerMarshaling struct {

// Hash returns the block hash of the header, which is simply the keccak256 hash of its
// RLP encoding.
func (h *Header) Hash() libcommon.Hash {
return rlpHash(h)
func (h *Header) Hash() (hash libcommon.Hash) {
if !h.mutable {
if hash := h.hash.Load(); hash != nil {
return *hash
}
}
hash = rlpHash(h)
if !h.mutable {
h.hash.Store(&hash)
}
return hash
}

var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())
Expand Down Expand Up @@ -719,7 +741,6 @@ type Block struct {
requests Requests

// caches
hash atomic.Pointer[libcommon.Hash]
size atomic.Uint64
}

Expand Down Expand Up @@ -1093,14 +1114,22 @@ func NewBlock(header *Header, txs []Transaction, uncles []*Header, receipts []*R
}
}

b.header.mutable = false //Force immutability of block and header. Use `NewBlockForAsembling` if you need mutable block
return b
}

// NewBlockForAsembling - creating new block - which allow mutation of fileds. Use it for block-assembly
func NewBlockForAsembling(header *Header, txs []Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, requests Requests) *Block {
b := NewBlock(header, txs, uncles, receipts, withdrawals, requests)
b.header.mutable = true
return b
}

// NewBlockFromStorage like NewBlock but used to create Block object when read it from DB
// in this case no reason to copy parts, or re-calculate headers fields - they are all stored in DB
func NewBlockFromStorage(hash libcommon.Hash, header *Header, txs []Transaction, uncles []*Header, withdrawals []*Withdrawal, requests Requests) *Block {
header.hash.Store(&hash)
b := &Block{header: header, transactions: txs, uncles: uncles, withdrawals: withdrawals, requests: requests}
b.hash.Store(&hash)
return b
}

Expand All @@ -1126,7 +1155,7 @@ func NewBlockFromNetwork(header *Header, body *Body) *Block {
// CopyHeader creates a deep copy of a block header to prevent side effects from
// modifying a header variable.
func CopyHeader(h *Header) *Header {
cpy := *h
cpy := *h //nolint
if cpy.Difficulty = new(big.Int); h.Difficulty != nil {
cpy.Difficulty.Set(h.Difficulty)
}
Expand Down Expand Up @@ -1165,6 +1194,11 @@ func CopyHeader(h *Header) *Header {
cpy.RequestsRoot = new(libcommon.Hash)
cpy.RequestsRoot.SetBytes(h.RequestsRoot.Bytes())
}
cpy.mutable = h.mutable
if hash := h.hash.Load(); hash != nil {
hashCopy := *hash
cpy.hash.Store(&hashCopy)
}
return &cpy
}

Expand Down Expand Up @@ -1492,10 +1526,6 @@ func (b *Block) Copy() *Block {
withdrawals: withdrawals,
requests: requests,
}
if h := b.hash.Load(); h != nil {
hashCopy := *h
newB.hash.Store(&hashCopy)
}
szCopy := b.size.Load()
newB.size.Store(szCopy)
return newB
Expand All @@ -1504,10 +1534,10 @@ func (b *Block) Copy() *Block {
// 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 {
cpy := *header

headerCopy := CopyHeader(header)
headerCopy.mutable = false
return &Block{
header: &cpy,
header: headerCopy,
transactions: b.transactions,
uncles: b.uncles,
withdrawals: b.withdrawals,
Expand All @@ -1517,14 +1547,7 @@ func (b *Block) WithSeal(header *Header) *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() libcommon.Hash {
if hash := b.hash.Load(); hash != nil {
return *hash
}
h := b.header.Hash()
b.hash.Store(&h)
return h
}
func (b *Block) Hash() libcommon.Hash { return b.header.Hash() }

type Blocks []*Block

Expand Down
4 changes: 3 additions & 1 deletion core/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"reflect"
"testing"

"github.com/go-test/deep"
"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -477,7 +478,8 @@ func TestAuRaHeaderEncoding(t *testing.T) {
var decoded Header
require.NoError(t, rlp.DecodeBytes(encoded, &decoded))

assert.Equal(t, header, decoded)
deep.CompareUnexportedFields = true
require.Nil(t, deep.Equal(&header, &decoded))
}

func TestWithdrawalsEncoding(t *testing.T) {
Expand Down
3 changes: 1 addition & 2 deletions core/vm/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
}

func fakeHeader(n uint64, parentHash libcommon.Hash) *types.Header {
header := types.Header{
return &types.Header{
Coinbase: libcommon.HexToAddress("0x00000000000000000000000000000000deadbeef"),
Number: new(big.Int).SetUint64(n),
ParentHash: parentHash,
Expand All @@ -318,7 +318,6 @@ func fakeHeader(n uint64, parentHash libcommon.Hash) *types.Header {
Difficulty: big.NewInt(0),
GasLimit: 100000,
}
return &header
}

// FakeChainHeaderReader implements consensus.ChainHeaderReader interface
Expand Down
3 changes: 2 additions & 1 deletion erigon-lib/commitment/hex_patricia_hashed_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ package commitment
import (
"context"
"encoding/hex"
"github.com/stretchr/testify/require"
"math/rand"
"testing"

"github.com/stretchr/testify/require"

"github.com/erigontech/erigon-lib/common/length"
)

Expand Down
3 changes: 2 additions & 1 deletion erigon-lib/commitment/hex_patricia_hashed_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import (
"context"
"encoding/binary"
"encoding/hex"
"github.com/holiman/uint256"
"math"
"math/rand"
"testing"

"github.com/holiman/uint256"

"github.com/erigontech/erigon-lib/common/length"
"github.com/stretchr/testify/require"
)
Expand Down
2 changes: 1 addition & 1 deletion eth/stagedsync/stage_mining_finish.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func SpawnMiningFinishStage(s *StageState, tx kv.RwTx, cfg MiningFinishCfg, quit
// continue
//}

block := types.NewBlock(current.Header, current.Txs, current.Uncles, current.Receipts, current.Withdrawals, current.Requests)
block := types.NewBlockForAsembling(current.Header, current.Txs, current.Uncles, current.Receipts, current.Withdrawals, current.Requests)
blockWithReceipts := &types.BlockWithReceipts{Block: block, Receipts: current.Receipts}
*current = MiningBlock{} // hack to clean global data

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1
github.com/go-echarts/go-echarts/v2 v2.3.3
github.com/go-test/deep v1.1.1
github.com/goccy/go-json v0.9.11
github.com/gofrs/flock v0.12.1
github.com/golang-jwt/jwt/v4 v4.5.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
Expand Down
Loading

0 comments on commit f4322d9

Please sign in to comment.