mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
Merge branch 'ethereum:master' into portal
This commit is contained in:
commit
66de3b2898
72 changed files with 580 additions and 234 deletions
|
|
@ -127,7 +127,7 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
|||
return arguments.copyAtomic(v, values[0])
|
||||
}
|
||||
|
||||
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
||||
// copyAtomic copies ( hexdata -> go ) a single value
|
||||
func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
|
||||
dst := reflect.ValueOf(v).Elem()
|
||||
src := reflect.ValueOf(marshalledValues)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// waitWatcherStarts waits up to 1s for the keystore watcher to start.
|
||||
// waitWatcherStart waits up to 1s for the keystore watcher to start.
|
||||
func waitWatcherStart(ks *KeyStore) bool {
|
||||
// On systems where file watch is not supported, just return "ok".
|
||||
if !ks.cache.watcher.enabled() {
|
||||
|
|
|
|||
|
|
@ -72,9 +72,9 @@ var (
|
|||
)
|
||||
|
||||
func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
||||
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.GoerliFlag, utils.SepoliaFlag)
|
||||
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name) || ctx.IsSet(utils.BeaconGenesisRootFlag.Name) || ctx.IsSet(utils.BeaconGenesisTimeFlag.Name)
|
||||
var config lightClientConfig
|
||||
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
||||
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.GoerliFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
||||
switch {
|
||||
case ctx.Bool(utils.MainnetFlag.Name):
|
||||
config = MainnetConfig
|
||||
|
|
@ -87,24 +87,37 @@ func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
|||
config = MainnetConfig
|
||||
}
|
||||
}
|
||||
if customConfig && config.Forks != nil {
|
||||
utils.Fatalf("Cannot use custom beacon chain config flags in combination with pre-defined network config")
|
||||
}
|
||||
if ctx.IsSet(utils.BeaconGenesisRootFlag.Name) {
|
||||
// Genesis root and time should always be specified together with custom chain config
|
||||
if customConfig {
|
||||
if !ctx.IsSet(utils.BeaconGenesisRootFlag.Name) {
|
||||
utils.Fatalf("Custom beacon chain config is specified but genesis root is missing")
|
||||
}
|
||||
if !ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) {
|
||||
utils.Fatalf("Custom beacon chain config is specified but genesis time is missing")
|
||||
}
|
||||
if !ctx.IsSet(utils.BeaconCheckpointFlag.Name) {
|
||||
utils.Fatalf("Custom beacon chain config is specified but checkpoint is missing")
|
||||
}
|
||||
config.ChainConfig = &types.ChainConfig{
|
||||
GenesisTime: ctx.Uint64(utils.BeaconGenesisTimeFlag.Name),
|
||||
}
|
||||
if c, err := hexutil.Decode(ctx.String(utils.BeaconGenesisRootFlag.Name)); err == nil && len(c) <= 32 {
|
||||
copy(config.GenesisValidatorsRoot[:len(c)], c)
|
||||
} else {
|
||||
utils.Fatalf("Invalid hex string", "beacon.genesis.gvroot", ctx.String(utils.BeaconGenesisRootFlag.Name), "error", err)
|
||||
}
|
||||
}
|
||||
if ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) {
|
||||
config.GenesisTime = ctx.Uint64(utils.BeaconGenesisTimeFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.BeaconConfigFlag.Name) {
|
||||
if err := config.ChainConfig.LoadForks(ctx.String(utils.BeaconConfigFlag.Name)); err != nil {
|
||||
utils.Fatalf("Could not load beacon chain config file", "file name", ctx.String(utils.BeaconConfigFlag.Name), "error", err)
|
||||
}
|
||||
} else {
|
||||
if ctx.IsSet(utils.BeaconGenesisRootFlag.Name) {
|
||||
utils.Fatalf("Genesis root is specified but custom beacon chain config is missing")
|
||||
}
|
||||
if ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) {
|
||||
utils.Fatalf("Genesis time is specified but custom beacon chain config is missing")
|
||||
}
|
||||
}
|
||||
// Checkpoint is required with custom chain config and is optional with pre-defined config
|
||||
if ctx.IsSet(utils.BeaconCheckpointFlag.Name) {
|
||||
if c, err := hexutil.Decode(ctx.String(utils.BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 {
|
||||
copy(config.Checkpoint[:len(c)], c)
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/donovanhide/eventsource"
|
||||
|
|
@ -287,7 +289,7 @@ func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// GetHead fetches and validates the beacon header with the given blockRoot.
|
||||
// GetHeader fetches and validates the beacon header with the given blockRoot.
|
||||
// If blockRoot is null hash then the latest head header is fetched.
|
||||
func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, error) {
|
||||
var blockId string
|
||||
|
|
@ -416,39 +418,34 @@ type HeadEventListener struct {
|
|||
// The callbacks are also called for the current head and optimistic head at startup.
|
||||
// They are never called concurrently.
|
||||
func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func() {
|
||||
closeCh := make(chan struct{}) // initiate closing the stream
|
||||
closedCh := make(chan struct{}) // stream closed (or failed to create)
|
||||
stoppedCh := make(chan struct{}) // sync loop stopped
|
||||
streamCh := make(chan *eventsource.Stream, 1)
|
||||
var (
|
||||
ctx, closeCtx = context.WithCancel(context.Background())
|
||||
streamCh = make(chan *eventsource.Stream, 1)
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
// When connected to a Lodestar node the subscription blocks until the first actual
|
||||
// event arrives; therefore we create the subscription in a separate goroutine while
|
||||
// letting the main goroutine sync up to the current head.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer close(closedCh)
|
||||
// when connected to a Lodestar node the subscription blocks until the
|
||||
// first actual event arrives; therefore we create the subscription in
|
||||
// a separate goroutine while letting the main goroutine sync up to the
|
||||
// current head
|
||||
req, err := http.NewRequest("GET", api.url+
|
||||
"/eth/v1/events?topics=head&topics=light_client_optimistic_update&topics=light_client_finality_update", nil)
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
||||
return
|
||||
}
|
||||
for k, v := range api.customHeaders {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
stream, err := eventsource.SubscribeWithRequest("", req)
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription: %v", err))
|
||||
close(streamCh)
|
||||
defer wg.Done()
|
||||
stream := api.startEventStream(ctx, &listener)
|
||||
if stream == nil {
|
||||
// This case happens when the context was closed.
|
||||
return
|
||||
}
|
||||
// Stream was opened, wait for close signal.
|
||||
streamCh <- stream
|
||||
<-closeCh
|
||||
<-ctx.Done()
|
||||
stream.Close()
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer close(stoppedCh)
|
||||
defer wg.Done()
|
||||
|
||||
// Request initial data.
|
||||
if head, err := api.GetHeader(common.Hash{}); err == nil {
|
||||
listener.OnNewHead(head.Slot, head.Hash())
|
||||
}
|
||||
|
|
@ -458,32 +455,42 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
if finalityUpdate, err := api.GetFinalityUpdate(); err == nil {
|
||||
listener.OnFinality(finalityUpdate)
|
||||
}
|
||||
stream := <-streamCh
|
||||
if stream == nil {
|
||||
|
||||
// Receive the stream.
|
||||
var stream *eventsource.Stream
|
||||
select {
|
||||
case stream = <-streamCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stream.Close()
|
||||
|
||||
case event, ok := <-stream.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch event.Event() {
|
||||
case "head":
|
||||
if slot, blockRoot, err := decodeHeadEvent([]byte(event.Data())); err == nil {
|
||||
slot, blockRoot, err := decodeHeadEvent([]byte(event.Data()))
|
||||
if err == nil {
|
||||
listener.OnNewHead(slot, blockRoot)
|
||||
} else {
|
||||
listener.OnError(fmt.Errorf("error decoding head event: %v", err))
|
||||
}
|
||||
case "light_client_optimistic_update":
|
||||
if signedHead, err := decodeOptimisticHeadUpdate([]byte(event.Data())); err == nil {
|
||||
signedHead, err := decodeOptimisticHeadUpdate([]byte(event.Data()))
|
||||
if err == nil {
|
||||
listener.OnSignedHead(signedHead)
|
||||
} else {
|
||||
listener.OnError(fmt.Errorf("error decoding optimistic update event: %v", err))
|
||||
}
|
||||
case "light_client_finality_update":
|
||||
if finalityUpdate, err := decodeFinalityUpdate([]byte(event.Data())); err == nil {
|
||||
finalityUpdate, err := decodeFinalityUpdate([]byte(event.Data()))
|
||||
if err == nil {
|
||||
listener.OnFinality(finalityUpdate)
|
||||
} else {
|
||||
listener.OnError(fmt.Errorf("error decoding finality update event: %v", err))
|
||||
|
|
@ -491,6 +498,7 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
default:
|
||||
listener.OnError(fmt.Errorf("unexpected event: %s", event.Event()))
|
||||
}
|
||||
|
||||
case err, ok := <-stream.Errors:
|
||||
if !ok {
|
||||
return
|
||||
|
|
@ -499,9 +507,43 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
|||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return func() {
|
||||
close(closeCh)
|
||||
<-closedCh
|
||||
<-stoppedCh
|
||||
closeCtx()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// startEventStream establishes an event stream. This will keep retrying until the stream has been
|
||||
// established. It can only return nil when the context is canceled.
|
||||
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
||||
for retry := true; retry; retry = ctxSleep(ctx, 5*time.Second) {
|
||||
path := "/eth/v1/events?topics=head&topics=light_client_optimistic_update&topics=light_client_finality_update"
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", api.url+path, nil)
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
||||
continue
|
||||
}
|
||||
for k, v := range api.customHeaders {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
stream, err := eventsource.SubscribeWithRequest("", req)
|
||||
if err != nil {
|
||||
listener.OnError(fmt.Errorf("error creating event subscription: %v", err))
|
||||
continue
|
||||
}
|
||||
return stream
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ctxSleep(ctx context.Context, timeout time.Duration) (ok bool) {
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-timer.C:
|
||||
return true
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func (h *HeadTracker) ValidatedHead() (types.SignedHeader, bool) {
|
|||
return h.signedHead, h.hasSignedHead
|
||||
}
|
||||
|
||||
// ValidatedHead returns the latest validated head.
|
||||
// ValidatedFinality returns the latest validated finality.
|
||||
func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
||||
h.lock.RLock()
|
||||
defer h.lock.RUnlock()
|
||||
|
|
@ -64,7 +64,7 @@ func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
|||
return h.finalityUpdate, h.hasFinalityUpdate
|
||||
}
|
||||
|
||||
// Validate validates the given signed head. If the head is successfully validated
|
||||
// ValidateHead validates the given signed head. If the head is successfully validated
|
||||
// and it is better than the old validated head (higher slot or same slot and more
|
||||
// signers) then ValidatedHead is updated. The boolean return flag signals if
|
||||
// ValidatedHead has been changed.
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ func (s *serverWithTimeout) startTimeout(reqData RequestResponse) {
|
|||
})
|
||||
}
|
||||
|
||||
// stop stops all goroutines associated with the server.
|
||||
// unsubscribe stops all goroutines associated with the server.
|
||||
func (s *serverWithTimeout) unsubscribe() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
|
@ -337,7 +337,7 @@ func (s *serverWithLimits) sendRequest(request Request) (reqId ID) {
|
|||
return s.serverWithTimeout.sendRequest(request)
|
||||
}
|
||||
|
||||
// stop stops all goroutines associated with the server.
|
||||
// unsubscribe stops all goroutines associated with the server.
|
||||
func (s *serverWithLimits) unsubscribe() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ func (s *HeadSync) newSignedHead(server request.Server, signedHead types.SignedH
|
|||
s.headTracker.ValidateHead(signedHead)
|
||||
}
|
||||
|
||||
// newSignedHead handles received signed head; either validates it if the chain
|
||||
// newFinalityUpdate handles received finality update; either validates it if the chain
|
||||
// is properly synced or stores it for further validation.
|
||||
func (s *HeadSync) newFinalityUpdate(server request.Server, finalityUpdate types.FinalityUpdate) {
|
||||
if !s.chainInit || types.SyncPeriod(finalityUpdate.SignatureSlot) > s.nextSyncPeriod {
|
||||
|
|
@ -111,7 +111,7 @@ func (s *HeadSync) newFinalityUpdate(server request.Server, finalityUpdate types
|
|||
s.headTracker.ValidateFinality(finalityUpdate)
|
||||
}
|
||||
|
||||
// processUnvalidatedHeads iterates the list of unvalidated heads and validates
|
||||
// processUnvalidated iterates the list of unvalidated heads and validates
|
||||
// those which can be validated.
|
||||
func (s *HeadSync) processUnvalidated() {
|
||||
if !s.chainInit {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ type ExecutionHeader struct {
|
|||
obj headerObject
|
||||
}
|
||||
|
||||
// HeaderFromJSON decodes an execution header from JSON data provided by
|
||||
// ExecutionHeaderFromJSON decodes an execution header from JSON data provided by
|
||||
// the beacon chain API.
|
||||
func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, error) {
|
||||
var obj headerObject
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ func TestAttachWithHeaders(t *testing.T) {
|
|||
// This is fixed in a follow-up PR.
|
||||
}
|
||||
|
||||
// TestAttachWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e
|
||||
// TestRemoteDbWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e
|
||||
// that custom headers are forwarded to the target.
|
||||
func TestRemoteDbWithHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func testExport(t *testing.T, f string) {
|
|||
}
|
||||
}
|
||||
|
||||
// testDeletion tests if the deletion markers can be exported/imported correctly
|
||||
// TestDeletionExport tests if the deletion markers can be exported/imported correctly
|
||||
func TestDeletionExport(t *testing.T) {
|
||||
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
||||
defer func() {
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ func (l *list[T]) init() {
|
|||
l.root.prev = &l.root
|
||||
}
|
||||
|
||||
// push adds an element to the front of the list.
|
||||
// pushElem adds an element to the front of the list.
|
||||
func (l *list[T]) pushElem(e *listElem[T]) {
|
||||
e.prev = &l.root
|
||||
e.next = l.root.next
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ var (
|
|||
u256_32 = uint256.NewInt(32)
|
||||
)
|
||||
|
||||
// AccumulateRewards credits the coinbase of the given block with the mining
|
||||
// accumulateRewards credits the coinbase of the given block with the mining
|
||||
// reward. The total reward consists of the static block reward and rewards for
|
||||
// included uncles. The coinbase of each uncle block is also rewarded.
|
||||
func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ func (l *lexer) ignore() {
|
|||
l.start = l.pos
|
||||
}
|
||||
|
||||
// Accepts checks whether the given input matches the next rune
|
||||
// accept checks whether the given input matches the next rune
|
||||
func (l *lexer) accept(valid string) bool {
|
||||
if strings.ContainsRune(valid, l.next()) {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -147,8 +147,11 @@ type CacheConfig struct {
|
|||
}
|
||||
|
||||
// triedbConfig derives the configures for trie database.
|
||||
func (c *CacheConfig) triedbConfig() *triedb.Config {
|
||||
config := &triedb.Config{Preimages: c.Preimages}
|
||||
func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
||||
config := &triedb.Config{
|
||||
Preimages: c.Preimages,
|
||||
IsVerkle: isVerkle,
|
||||
}
|
||||
if c.StateScheme == rawdb.HashScheme {
|
||||
config.HashDB = &hashdb.Config{
|
||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||
|
|
@ -265,7 +268,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
cacheConfig = defaultCacheConfig
|
||||
}
|
||||
// Open trie database with provided config
|
||||
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig())
|
||||
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(genesis != nil && genesis.IsVerkle()))
|
||||
|
||||
// Setup the genesis block, commit the provided genesis specification
|
||||
// to database if the genesis block is not present yet, or load the
|
||||
|
|
@ -639,7 +642,7 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
|
|||
}
|
||||
}
|
||||
|
||||
// rewindPathHead implements the logic of rewindHead in the context of hash scheme.
|
||||
// rewindHashHead implements the logic of rewindHead in the context of hash scheme.
|
||||
func (bc *BlockChain) rewindHashHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
||||
var (
|
||||
limit uint64 // The oldest block that will be searched for this rewinding
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/gballet/go-verkle"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -418,6 +419,112 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
|
|||
return db, blocks, receipts
|
||||
}
|
||||
|
||||
func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, trdb *triedb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
|
||||
if config == nil {
|
||||
config = params.TestChainConfig
|
||||
}
|
||||
proofs := make([]*verkle.VerkleProof, 0, n)
|
||||
keyvals := make([]verkle.StateDiff, 0, n)
|
||||
cm := newChainMaker(parent, config, engine)
|
||||
|
||||
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
||||
b.header = cm.makeHeader(parent, statedb, b.engine)
|
||||
|
||||
// TODO uncomment when proof generation is merged
|
||||
// Save pre state for proof generation
|
||||
// preState := statedb.Copy()
|
||||
|
||||
// TODO uncomment when the 2935 PR is merged
|
||||
// if config.IsPrague(b.header.Number, b.header.Time) {
|
||||
// if !config.IsPrague(b.parent.Number(), b.parent.Time()) {
|
||||
// Transition case: insert all 256 ancestors
|
||||
// InsertBlockHashHistoryAtEip2935Fork(statedb, b.header.Number.Uint64()-1, b.header.ParentHash, chainreader)
|
||||
// } else {
|
||||
// ProcessParentBlockHash(statedb, b.header.Number.Uint64()-1, b.header.ParentHash)
|
||||
// }
|
||||
// }
|
||||
// Execute any user modifications to the block
|
||||
if gen != nil {
|
||||
gen(i, b)
|
||||
}
|
||||
body := &types.Body{
|
||||
Transactions: b.txs,
|
||||
Uncles: b.uncles,
|
||||
Withdrawals: b.withdrawals,
|
||||
}
|
||||
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Write state changes to db
|
||||
root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
}
|
||||
if err = triedb.Commit(root, false); err != nil {
|
||||
panic(fmt.Sprintf("trie write error: %v", err))
|
||||
}
|
||||
|
||||
// TODO uncomment when proof generation is merged
|
||||
// proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
||||
// keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
||||
|
||||
return block, b.receipts
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
statedb, err := state.New(parent.Root(), state.NewDatabaseWithNodeDB(db, trdb), nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
block, receipts := genblock(i, parent, trdb, statedb)
|
||||
|
||||
// Post-process the receipts.
|
||||
// Here we assign the final block hash and other info into the receipt.
|
||||
// In order for DeriveFields to work, the transaction and receipt lists need to be
|
||||
// of equal length. If AddUncheckedTx or AddUncheckedReceipt are used, there will be
|
||||
// extra ones, so we just trim the lists here.
|
||||
receiptsCount := len(receipts)
|
||||
txs := block.Transactions()
|
||||
if len(receipts) > len(txs) {
|
||||
receipts = receipts[:len(txs)]
|
||||
} else if len(receipts) < len(txs) {
|
||||
txs = txs[:len(receipts)]
|
||||
}
|
||||
var blobGasPrice *big.Int
|
||||
if block.ExcessBlobGas() != nil {
|
||||
blobGasPrice = eip4844.CalcBlobFee(*block.ExcessBlobGas())
|
||||
}
|
||||
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Re-expand to ensure all receipts are returned.
|
||||
receipts = receipts[:receiptsCount]
|
||||
|
||||
// Advance the chain.
|
||||
cm.add(block, receipts)
|
||||
parent = block
|
||||
}
|
||||
return cm.chain, cm.receipts, proofs, keyvals
|
||||
}
|
||||
|
||||
func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme)
|
||||
cacheConfig.SnapshotLimit = 0
|
||||
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
|
||||
defer triedb.Close()
|
||||
genesisBlock, err := genesis.Commit(db, triedb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
blocks, receipts, proofs, keyvals := GenerateVerkleChain(genesis.Config, genesisBlock, engine, db, triedb, n, gen)
|
||||
return db, blocks, receipts, proofs, keyvals
|
||||
}
|
||||
|
||||
func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
|
||||
time := parent.Time() + 10 // block time is fixed at 10 seconds
|
||||
header := &types.Header{
|
||||
|
|
@ -482,7 +589,7 @@ func makeBlockChain(chainConfig *params.ChainConfig, parent *types.Block, n int,
|
|||
return blocks
|
||||
}
|
||||
|
||||
// makeBlockChain creates a deterministic chain of blocks from genesis
|
||||
// makeBlockChainWithGenesis creates a deterministic chain of blocks from genesis
|
||||
func makeBlockChainWithGenesis(genesis *Genesis, n int, engine consensus.Engine, seed int) (ethdb.Database, []*types.Block) {
|
||||
db, blocks, _ := GenerateChainWithGenesis(genesis, engine, n, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@ func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc.
|
||||
// deriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc.
|
||||
func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error {
|
||||
logIndex := uint(0)
|
||||
if len(txs) != len(receipts) {
|
||||
|
|
|
|||
|
|
@ -209,6 +209,8 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
|
|||
switch t := t.(type) {
|
||||
case *trie.StateTrie:
|
||||
return t.Copy()
|
||||
case *trie.VerkleTrie:
|
||||
return t.Copy()
|
||||
default:
|
||||
panic(fmt.Errorf("unknown trie type %T", t))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,6 +298,18 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
|||
}
|
||||
// Insert all the pending storage updates into the trie
|
||||
usedStorage := make([][]byte, 0, len(s.pendingStorage))
|
||||
|
||||
// Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes
|
||||
// in circumstances similar to the following:
|
||||
//
|
||||
// Consider nodes `A` and `B` who share the same full node parent `P` and have no other siblings.
|
||||
// During the execution of a block:
|
||||
// - `A` is deleted,
|
||||
// - `C` is created, and also shares the parent `P`.
|
||||
// If the deletion is handled first, then `P` would be left with only one child, thus collapsed
|
||||
// into a shortnode. This requires `B` to be resolved from disk.
|
||||
// Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved.
|
||||
var deletions []common.Hash
|
||||
for key, value := range s.pendingStorage {
|
||||
// Skip noop changes, persist actual changes
|
||||
if value == s.originStorage[key] {
|
||||
|
|
@ -307,13 +319,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
|||
s.originStorage[key] = value
|
||||
|
||||
var encoded []byte // rlp-encoded value to be used by the snapshot
|
||||
if (value == common.Hash{}) {
|
||||
if err := tr.DeleteStorage(s.address, key[:]); err != nil {
|
||||
s.db.setError(err)
|
||||
return nil, err
|
||||
}
|
||||
s.db.StorageDeleted += 1
|
||||
} else {
|
||||
if (value != common.Hash{}) {
|
||||
// Encoding []byte cannot fail, ok to ignore the error.
|
||||
trimmed := common.TrimLeftZeroes(value[:])
|
||||
encoded, _ = rlp.EncodeToBytes(trimmed)
|
||||
|
|
@ -322,6 +328,8 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
|||
return nil, err
|
||||
}
|
||||
s.db.StorageUpdated += 1
|
||||
} else {
|
||||
deletions = append(deletions, key)
|
||||
}
|
||||
// Cache the mutated storage slots until commit
|
||||
if storage == nil {
|
||||
|
|
@ -353,6 +361,13 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
|||
// Cache the items for preloading
|
||||
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
|
||||
}
|
||||
for _, key := range deletions {
|
||||
if err := tr.DeleteStorage(s.address, key[:]); err != nil {
|
||||
s.db.setError(err)
|
||||
return nil, err
|
||||
}
|
||||
s.db.StorageDeleted += 1
|
||||
}
|
||||
if s.db.prefetcher != nil {
|
||||
s.db.prefetcher.used(s.addrHash, s.data.Root, usedStorage)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -541,12 +541,11 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
|||
}
|
||||
|
||||
// deleteStateObject removes the given object from the state trie.
|
||||
func (s *StateDB) deleteStateObject(obj *stateObject) {
|
||||
func (s *StateDB) deleteStateObject(addr common.Address) {
|
||||
// Track the amount of time wasted on deleting the account from the trie
|
||||
defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
|
||||
|
||||
// Delete the account from the trie
|
||||
addr := obj.Address()
|
||||
if err := s.trie.DeleteAccount(addr); err != nil {
|
||||
s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err))
|
||||
}
|
||||
|
|
@ -917,16 +916,30 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
}
|
||||
}
|
||||
usedAddrs := make([][]byte, 0, len(s.stateObjectsPending))
|
||||
// Perform updates before deletions. This prevents resolution of unnecessary trie nodes
|
||||
// in circumstances similar to the following:
|
||||
//
|
||||
// Consider nodes `A` and `B` who share the same full node parent `P` and have no other siblings.
|
||||
// During the execution of a block:
|
||||
// - `A` self-destructs,
|
||||
// - `C` is created, and also shares the parent `P`.
|
||||
// If the self-destruct is handled first, then `P` would be left with only one child, thus collapsed
|
||||
// into a shortnode. This requires `B` to be resolved from disk.
|
||||
// Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved.
|
||||
var deletedAddrs []common.Address
|
||||
for addr := range s.stateObjectsPending {
|
||||
if obj := s.stateObjects[addr]; obj.deleted {
|
||||
s.deleteStateObject(obj)
|
||||
s.AccountDeleted += 1
|
||||
} else {
|
||||
if obj := s.stateObjects[addr]; !obj.deleted {
|
||||
s.updateStateObject(obj)
|
||||
s.AccountUpdated += 1
|
||||
} else {
|
||||
deletedAddrs = append(deletedAddrs, obj.address)
|
||||
}
|
||||
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
|
||||
}
|
||||
for _, deletedAddr := range deletedAddrs {
|
||||
s.deleteStateObject(deletedAddr)
|
||||
s.AccountDeleted += 1
|
||||
}
|
||||
if prefetcher != nil {
|
||||
prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs)
|
||||
}
|
||||
|
|
@ -1143,6 +1156,11 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetTrie returns the account trie.
|
||||
func (s *StateDB) GetTrie() Trie {
|
||||
return s.trie
|
||||
}
|
||||
|
||||
// Commit writes the state to the underlying in-memory trie database.
|
||||
// Once the state is committed, tries cached in stateDB (including account
|
||||
// trie, storage tries) will no longer be functional. A new state instance
|
||||
|
|
|
|||
|
|
@ -422,3 +422,108 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||
}
|
||||
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
|
||||
}
|
||||
|
||||
var (
|
||||
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true)
|
||||
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
|
||||
// will not contain that copied data.
|
||||
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
|
||||
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
|
||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, true, true, true, true)
|
||||
)
|
||||
|
||||
func TestProcessVerkle(t *testing.T) {
|
||||
var (
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
Ethash: new(params.EthashConfig),
|
||||
ShanghaiTime: u64(0),
|
||||
VerkleTime: u64(0),
|
||||
TerminalTotalDifficulty: common.Big0,
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
// TODO uncomment when proof generation is merged
|
||||
// ProofInBlocks: true,
|
||||
}
|
||||
signer = types.LatestSigner(config)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||
coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||
gspec = &Genesis{
|
||||
Config: config,
|
||||
Alloc: GenesisAlloc{
|
||||
coinbase: GenesisAccount{
|
||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||
Nonce: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
// Verkle trees use the snapshot, which must be enabled before the
|
||||
// data is saved into the tree+database.
|
||||
// genesis := gspec.MustCommit(bcdb, triedb)
|
||||
cacheConfig := DefaultCacheConfigWithScheme("path")
|
||||
cacheConfig.SnapshotLimit = 0
|
||||
blockchain, _ := NewBlockChain(bcdb, cacheConfig, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||
defer blockchain.Stop()
|
||||
|
||||
txCost1 := params.TxGas
|
||||
txCost2 := params.TxGas
|
||||
contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */)
|
||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(293644 /* execution costs */)
|
||||
blockGasUsagesExpected := []uint64{
|
||||
txCost1*2 + txCost2,
|
||||
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
||||
}
|
||||
_, chain, _, _, _ := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) {
|
||||
gen.SetPoS()
|
||||
|
||||
// TODO need to check that the tx cost provided is the exact amount used (no remaining left-over)
|
||||
tx, _ := types.SignTx(types.NewTransaction(uint64(i)*3, common.Address{byte(i), 2, 3}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey)
|
||||
gen.AddTx(tx)
|
||||
tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+1, common.Address{}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey)
|
||||
gen.AddTx(tx)
|
||||
tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+2, common.Address{}, big.NewInt(0), txCost2, big.NewInt(875000000), nil), signer, testKey)
|
||||
gen.AddTx(tx)
|
||||
|
||||
// Add two contract creations in block #2
|
||||
if i == 1 {
|
||||
tx, _ = types.SignTx(types.NewContractCreation(6, big.NewInt(16), 3000000, big.NewInt(875000000), code), signer, testKey)
|
||||
gen.AddTx(tx)
|
||||
|
||||
tx, _ = types.SignTx(types.NewContractCreation(7, big.NewInt(0), 3000000, big.NewInt(875000000), codeWithExtCodeCopy), signer, testKey)
|
||||
gen.AddTx(tx)
|
||||
}
|
||||
})
|
||||
|
||||
t.Log("inserting blocks into the chain")
|
||||
|
||||
endnum, err := blockchain.InsertChain(chain)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d imported with error: %v", endnum, err)
|
||||
}
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
b := blockchain.GetBlockByNumber(uint64(i) + 1)
|
||||
if b == nil {
|
||||
t.Fatalf("expected block %d to be present in chain", i+1)
|
||||
}
|
||||
if b.Hash() != chain[i].Hash() {
|
||||
t.Fatalf("block #%d not found at expected height", b.NumberU64())
|
||||
}
|
||||
if b.GasUsed() != blockGasUsagesExpected[i] {
|
||||
t.Fatalf("expected block #%d txs to use %d, got %d\n", b.NumberU64(), blockGasUsagesExpected[i], b.GasUsed())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1226,7 +1226,7 @@ func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error
|
|||
return errs
|
||||
}
|
||||
|
||||
// Add inserts a new blob transaction into the pool if it passes validation (both
|
||||
// add inserts a new blob transaction into the pool if it passes validation (both
|
||||
// consensus validity and pool restrictions).
|
||||
func (p *BlobPool) add(tx *types.Transaction) (err error) {
|
||||
// The blob pool blocks on adding a transaction. This is because blob txs are
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin
|
|||
return output, suppliedGas, err
|
||||
}
|
||||
|
||||
// ECRECOVER implemented as a native contract.
|
||||
// ecrecover implemented as a native contract.
|
||||
type ecrecover struct{}
|
||||
|
||||
func (c *ecrecover) RequiredGas(input []byte) uint64 {
|
||||
|
|
@ -457,7 +457,7 @@ func runBn256Add(input []byte) ([]byte, error) {
|
|||
return res.Marshal(), nil
|
||||
}
|
||||
|
||||
// bn256Add implements a native elliptic curve point addition conforming to
|
||||
// bn256AddIstanbul implements a native elliptic curve point addition conforming to
|
||||
// Istanbul consensus rules.
|
||||
type bn256AddIstanbul struct{}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ func (ctx *ScopeContext) MemoryData() []byte {
|
|||
return ctx.Memory.Data()
|
||||
}
|
||||
|
||||
// MemoryData returns the stack data. Callers must not modify the contents
|
||||
// StackData returns the stack data. Callers must not modify the contents
|
||||
// of the returned data.
|
||||
func (ctx *ScopeContext) StackData() []uint256.Int {
|
||||
if ctx.Stack == nil {
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ type btCurve struct {
|
|||
*btcec.KoblitzCurve
|
||||
}
|
||||
|
||||
// Marshall converts a point given as (x, y) into a byte slice.
|
||||
// Marshal converts a point given as (x, y) into a byte slice.
|
||||
func (curve btCurve) Marshal(x, y *big.Int) []byte {
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ type Ethereum struct {
|
|||
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
|
||||
}
|
||||
|
||||
// New creates a new Ethereum object (including the
|
||||
// initialisation of the common Ethereum object)
|
||||
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
|
||||
// whose lifecycle will be managed by the provided node.
|
||||
func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||
// Ensure configuration values are compatible and sane
|
||||
if config.SyncMode == downloader.LightSync {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func (w *withdrawalQueue) gatherPending(maxCount int) []*types.Withdrawal {
|
|||
case withdrawal := <-w.pending:
|
||||
withdrawals = append(withdrawals, withdrawal)
|
||||
if len(withdrawals) == maxCount {
|
||||
break
|
||||
return withdrawals
|
||||
}
|
||||
default:
|
||||
return withdrawals
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func newTester(t *testing.T) *downloadTester {
|
|||
return newTesterWithNotification(t, nil)
|
||||
}
|
||||
|
||||
// newTester creates a new downloader test mocker.
|
||||
// newTesterWithNotification creates a new downloader test mocker.
|
||||
func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
|
||||
freezer := t.TempDir()
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false)
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func newSkeletonTestPeer(id string, headers []*types.Header) *skeletonTestPeer {
|
|||
}
|
||||
}
|
||||
|
||||
// newSkeletonTestPeer creates a new mock peer to test the skeleton sync with,
|
||||
// newSkeletonTestPeerWithHook creates a new mock peer to test the skeleton sync with,
|
||||
// and sets an optional serve hook that can return headers for delivery instead
|
||||
// of the predefined chain. Useful for emulating malicious behavior that would
|
||||
// otherwise require dedicated peer types.
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ func TestInvalidLogFilterCreation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestLogFilterUninstall tests invalid getLogs requests
|
||||
// TestInvalidGetLogsRequest tests invalid getLogs requests
|
||||
func TestInvalidGetLogsRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func newTestBackend(blocks int) *testBackend {
|
|||
return newTestBackendWithGenerator(blocks, false, nil)
|
||||
}
|
||||
|
||||
// newTestBackend creates a chain with a number of explicitly defined blocks and
|
||||
// newTestBackendWithGenerator creates a chain with a number of explicitly defined blocks and
|
||||
// wraps it into a mock backend.
|
||||
func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -2201,7 +2201,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
|||
// If the chunk's root is an overflown but full delivery,
|
||||
// clear the heal request.
|
||||
accountHash := res.accounts[len(res.accounts)-1]
|
||||
if root == res.subTask.root && rawdb.HasStorageTrieNode(s.db, accountHash, nil, root) {
|
||||
if root == res.subTask.root && rawdb.HasTrieNode(s.db, accountHash, nil, root, s.scheme) {
|
||||
for i, account := range res.mainTask.res.hashes {
|
||||
if account == accountHash {
|
||||
res.mainTask.needHeal[i] = false
|
||||
|
|
|
|||
|
|
@ -839,7 +839,7 @@ func testMultiSyncManyUseless(t *testing.T, scheme string) {
|
|||
verifyTrie(scheme, syncer.db, sourceAccountTrie.Hash(), t)
|
||||
}
|
||||
|
||||
// TestMultiSyncManyUseless contains one good peer, and many which doesn't return anything valuable at all
|
||||
// TestMultiSyncManyUselessWithLowTimeout contains one good peer, and many which doesn't return anything valuable at all
|
||||
func TestMultiSyncManyUselessWithLowTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
@ -1378,7 +1378,7 @@ func testSyncWithStorageAndNonProvingPeer(t *testing.T, scheme string) {
|
|||
verifyTrie(scheme, syncer.db, sourceAccountTrie.Hash(), t)
|
||||
}
|
||||
|
||||
// TestSyncWithStorage tests basic sync using accounts + storage + code, against
|
||||
// TestSyncWithStorageMisbehavingProve tests basic sync using accounts + storage + code, against
|
||||
// a peer who insists on delivering full storage sets _and_ proofs. This triggered
|
||||
// an error, where the recipient erroneously clipped the boundary nodes, but
|
||||
// did not mark the account for healing.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ type testBackend struct {
|
|||
relHook func() // Hook is invoked when the requested state is released
|
||||
}
|
||||
|
||||
// testBackend creates a new test backend. OBS: After test is done, teardown must be
|
||||
// newTestBackend creates a new test backend. OBS: After test is done, teardown must be
|
||||
// invoked in order to release associated resources.
|
||||
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend {
|
||||
backend := &testBackend{
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ func testFlatCallTracer(tracerName string, dirPath string, t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
|
||||
// jsonEqualFlat is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
|
||||
// comparison
|
||||
func jsonEqualFlat(x, y interface{}) bool {
|
||||
xTrace := new([]flatCallTrace)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ func MemoryPtr(m []byte, offset, size int64) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Back returns the n'th item in stack
|
||||
// StackBack returns the n'th item in stack
|
||||
func StackBack(st []uint256.Int, n int) *uint256.Int {
|
||||
return &st[len(st)-n-1]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// testNoStepExec tests a regular value transfer (no exec), and accessing the statedb
|
||||
// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
|
||||
// in 'result'
|
||||
func TestNoStepExec(t *testing.T) {
|
||||
execTracer := func(code string) []byte {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (al accessList) equal(other accessList) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// accesslist converts the accesslist to a types.AccessList.
|
||||
// accessList converts the accesslist to a types.AccessList.
|
||||
func (al accessList) accessList() types.AccessList {
|
||||
acl := make(types.AccessList, 0, len(al))
|
||||
for addr, slots := range al {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package simulated
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
|
|
@ -62,7 +63,7 @@ type simClient struct {
|
|||
// Backend is a simulated blockchain. You can use it to test your contracts or
|
||||
// other code that interacts with the Ethereum chain.
|
||||
type Backend struct {
|
||||
eth *eth.Ethereum
|
||||
node *node.Node
|
||||
beacon *catalyst.SimulatedBeacon
|
||||
client simClient
|
||||
}
|
||||
|
|
@ -129,7 +130,7 @@ func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backe
|
|||
return nil, err
|
||||
}
|
||||
return &Backend{
|
||||
eth: backend,
|
||||
node: stack,
|
||||
beacon: beacon,
|
||||
client: simClient{ethclient.NewClient(stack.Attach())},
|
||||
}, nil
|
||||
|
|
@ -142,12 +143,16 @@ func (n *Backend) Close() error {
|
|||
n.client.Close()
|
||||
n.client = simClient{}
|
||||
}
|
||||
var err error
|
||||
if n.beacon != nil {
|
||||
err := n.beacon.Stop()
|
||||
err = n.beacon.Stop()
|
||||
n.beacon = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if n.node != nil {
|
||||
err = errors.Join(err, n.node.Close())
|
||||
n.node = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Commit seals a block and moves the chain forward to a new empty block.
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ func iterateKeys(it ethdb.Iterator) []string {
|
|||
return keys
|
||||
}
|
||||
|
||||
// randomHash generates a random blob of data and returns it as a hash.
|
||||
// randBytes generates a random blob of data.
|
||||
func randBytes(len int) []byte {
|
||||
buf := make([]byte, len)
|
||||
if n, err := rand.Read(buf); n != len || err != nil {
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ func (e *Era) readOffset(n uint64) (int64, error) {
|
|||
return blockIndexRecordOffset + int64(binary.LittleEndian.Uint64(e.buf[:])), nil
|
||||
}
|
||||
|
||||
// newReader returns a snappy.Reader for the e2store entry value at off.
|
||||
// newSnappyReader returns a snappy.Reader for the e2store entry value at off.
|
||||
func newSnappyReader(e *e2store.Reader, expectedType uint16, off int64) (io.Reader, int64, error) {
|
||||
r, n, err := e.ReaderAt(expectedType, off)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1186,9 +1186,6 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
|||
return 0, err
|
||||
}
|
||||
call := args.ToMessage(header.BaseFee)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Run the gas estimation andwrap any revertals into a custom return
|
||||
estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
|
||||
if err != nil {
|
||||
|
|
@ -1519,9 +1516,6 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||
// Set the accesslist to the last al
|
||||
args.AccessList = &accessList
|
||||
msg := args.ToMessage(header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
|
||||
// Apply the transaction with the access list tracer
|
||||
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func ClientName(clientIdentifier string) string {
|
|||
)
|
||||
}
|
||||
|
||||
// runtimeInfo returns build and platform information about the current binary.
|
||||
// Info returns build and platform information about the current binary.
|
||||
//
|
||||
// If the package that is currently executing is a prefixed by our go-ethereum
|
||||
// module path, it will print out commit and date VCS information. Otherwise,
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ func (l *logger) Handler() slog.Handler {
|
|||
return l.inner.Handler()
|
||||
}
|
||||
|
||||
// write logs a message at the specified level:
|
||||
// Write logs a message at the specified level:
|
||||
func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
|
||||
if !l.inner.Enabled(context.Background(), level) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ func (g *StandardGauge) Update(v int64) {
|
|||
g.value.Store(v)
|
||||
}
|
||||
|
||||
// Update updates the gauge's value if v is larger then the current value.
|
||||
// UpdateIfGt updates the gauge's value if v is larger then the current value.
|
||||
func (g *StandardGauge) UpdateIfGt(v int64) {
|
||||
for {
|
||||
exist := g.value.Load()
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ type meterArbiter struct {
|
|||
|
||||
var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})}
|
||||
|
||||
// Ticks meters on the scheduled interval
|
||||
// tick meters on the scheduled interval
|
||||
func (ma *meterArbiter) tick() {
|
||||
for range ma.ticker.C {
|
||||
ma.tickMeters()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ var enablerFlags = []string{"metrics"}
|
|||
// enablerEnvVars is the env var names to use to enable metrics collections.
|
||||
var enablerEnvVars = []string{"GETH_METRICS"}
|
||||
|
||||
// Init enables or disables the metrics system. Since we need this to run before
|
||||
// init enables or disables the metrics system. Since we need this to run before
|
||||
// any other code gets to create meters and timers, we'll actually do an ugly hack
|
||||
// and peek into the command line args for the metrics flag.
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -597,7 +597,7 @@ func newIPCServer(log log.Logger, endpoint string) *ipcServer {
|
|||
return &ipcServer{log: log, endpoint: endpoint}
|
||||
}
|
||||
|
||||
// Start starts the httpServer's http.Server
|
||||
// start starts the httpServer's http.Server
|
||||
func (is *ipcServer) start(apis []rpc.API) error {
|
||||
is.mu.Lock()
|
||||
defer is.mu.Unlock()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// meteredConn is a wrapper around a net.UDPConn that meters both the
|
||||
// meteredUdpConn is a wrapper around a net.UDPConn that meters both the
|
||||
// inbound and outbound network traffic.
|
||||
type meteredUdpConn struct {
|
||||
UDPConn
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ func TestGetSetID(t *testing.T) {
|
|||
assert.Equal(t, id, id2)
|
||||
}
|
||||
|
||||
// TestGetSetIP4 tests encoding/decoding and setting/getting of the IP key.
|
||||
// TestGetSetIPv4 tests encoding/decoding and setting/getting of the IP key.
|
||||
func TestGetSetIPv4(t *testing.T) {
|
||||
ip := IPv4{192, 168, 0, 3}
|
||||
var r Record
|
||||
|
|
@ -59,7 +59,7 @@ func TestGetSetIPv4(t *testing.T) {
|
|||
assert.Equal(t, ip, ip2)
|
||||
}
|
||||
|
||||
// TestGetSetIP6 tests encoding/decoding and setting/getting of the IP6 key.
|
||||
// TestGetSetIPv6 tests encoding/decoding and setting/getting of the IP6 key.
|
||||
func TestGetSetIPv6(t *testing.T) {
|
||||
ip := IPv6{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
||||
var r Record
|
||||
|
|
|
|||
|
|
@ -823,7 +823,7 @@ func (ns *NodeStateMachine) addTimeout(n *enode.Node, mask bitMask, timeout time
|
|||
}
|
||||
}
|
||||
|
||||
// removeTimeout removes node state timeouts associated to the given state flag(s).
|
||||
// removeTimeouts removes node state timeouts associated to the given state flag(s).
|
||||
// If a timeout was associated to multiple flags which are not all included in the
|
||||
// specified remove mask then only the included flags are de-associated and the timer
|
||||
// stays active.
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ func RegisterLifecycles(lifecycles LifecycleConstructors) {
|
|||
}
|
||||
|
||||
// adds the host part to the configuration's ENR, signs it
|
||||
// creates and the corresponding enode object to the configuration
|
||||
// creates and adds the corresponding enode object to the configuration
|
||||
func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error {
|
||||
enrIp := enr.IP(ip)
|
||||
n.Record.Set(&enrIp)
|
||||
|
|
|
|||
|
|
@ -838,7 +838,7 @@ func TestMsgFilterPassSingle(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// TestMsgFilterPassSingle tests streaming message events using an invalid
|
||||
// TestMsgFilterFailBadParams tests streaming message events using an invalid
|
||||
// filter
|
||||
func TestMsgFilterFailBadParams(t *testing.T) {
|
||||
// start the server
|
||||
|
|
|
|||
|
|
@ -535,7 +535,7 @@ func (net *Network) GetRandomUpNode(excludeIDs ...enode.ID) *Node {
|
|||
return net.getRandomUpNode(excludeIDs...)
|
||||
}
|
||||
|
||||
// GetRandomUpNode returns a random node on the network, which is running.
|
||||
// getRandomUpNode returns a random node on the network, which is running.
|
||||
func (net *Network) getRandomUpNode(excludeIDs ...enode.ID) *Node {
|
||||
return net.getRandomNode(net.getUpNodeIDs(), excludeIDs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ func (h *handler) startCallProc(fn func(*callProc)) {
|
|||
}()
|
||||
}
|
||||
|
||||
// handleResponse processes method call responses.
|
||||
// handleResponses processes method call responses.
|
||||
func (h *handler) handleResponses(batch []*jsonrpcMessage, handleCall func(*jsonrpcMessage)) {
|
||||
var resolvedops []*requestOp
|
||||
handleResp := func(msg *jsonrpcMessage) {
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ func (c *jsonCodec) close() {
|
|||
})
|
||||
}
|
||||
|
||||
// Closed returns a channel which will be closed when Close is called
|
||||
// closed returns a channel which will be closed when Close is called
|
||||
func (c *jsonCodec) closed() <-chan interface{} {
|
||||
return c.closeCh
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,10 +235,10 @@ func (c *mockConn) writeJSON(ctx context.Context, msg interface{}, isError bool)
|
|||
return c.enc.Encode(msg)
|
||||
}
|
||||
|
||||
// Closed returns a channel which is closed when the connection is closed.
|
||||
// closed returns a channel which is closed when the connection is closed.
|
||||
func (c *mockConn) closed() <-chan interface{} { return nil }
|
||||
|
||||
// RemoteAddr returns the peer address of the connection.
|
||||
// remoteAddr returns the peer address of the connection.
|
||||
func (c *mockConn) remoteAddr() string { return "" }
|
||||
|
||||
// BenchmarkNotify benchmarks the performance of notifying a subscription.
|
||||
|
|
|
|||
|
|
@ -704,7 +704,7 @@ func formatPrimitiveValue(encType string, encValue interface{}) (string, error)
|
|||
return "", fmt.Errorf("unhandled type %v", encType)
|
||||
}
|
||||
|
||||
// Validate checks if the types object is conformant to the specs
|
||||
// validate checks if the types object is conformant to the specs
|
||||
func (t Types) validate() error {
|
||||
for typeKey, typeArr := range t {
|
||||
if len(typeKey) == 0 {
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ func TestGnosisTypedDataWithChainId(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestGnosisCustomData tests the scenario where a user submits only the gnosis-safe
|
||||
// TestGnosisCustomDataWithChainId tests the scenario where a user submits only the gnosis-safe
|
||||
// specific data, and we fill the TypedData struct on our side
|
||||
func TestGnosisCustomDataWithChainId(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ type testFailure struct {
|
|||
reason string
|
||||
}
|
||||
|
||||
// skipShortMode skips tests matching when the -short flag is used.
|
||||
// slow adds expected slow tests matching the pattern.
|
||||
func (tm *testMatcher) slow(pattern string) {
|
||||
tm.slowpat = append(tm.slowpat, regexp.MustCompile(pattern))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,6 +295,14 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
|||
}
|
||||
evm := vm.NewEVM(context, txContext, st.StateDB, config, vmconfig)
|
||||
|
||||
if tracer := vmconfig.Tracer; tracer != nil && tracer.OnTxStart != nil {
|
||||
tracer.OnTxStart(evm.GetVMContext(), nil, msg.From)
|
||||
if evm.Config.Tracer.OnTxEnd != nil {
|
||||
defer func() {
|
||||
evm.Config.Tracer.OnTxEnd(nil, err)
|
||||
}()
|
||||
}
|
||||
}
|
||||
// Execute the message.
|
||||
snapshot := st.StateDB.Snapshot()
|
||||
gaspool := new(core.GasPool)
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ func TestRangeProof(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestRangeProof tests normal range proof with two non-existent proofs.
|
||||
// TestRangeProofWithNonExistentProof tests normal range proof with two non-existent proofs.
|
||||
// The test cases are generated randomly.
|
||||
func TestRangeProofWithNonExistentProof(t *testing.T) {
|
||||
trie, vals := randomTrie(4096)
|
||||
|
|
|
|||
|
|
@ -1066,7 +1066,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// BenchmarkCommitAfterHashFixedSize benchmarks the Commit (after Hash) of a fixed number of updates to a trie.
|
||||
// BenchmarkHashFixedSize benchmarks the hash of a fixed number of updates to a trie.
|
||||
// This benchmark is meant to capture the difference on efficiency of small versus large changes. Typically,
|
||||
// storage tries are small (a couple of entries), whereas the full post-block account trie update is large (a couple
|
||||
// of thousand entries)
|
||||
|
|
|
|||
|
|
@ -108,12 +108,12 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
|
|||
log.Crit("Both 'hash' and 'path' mode are configured")
|
||||
}
|
||||
if config.PathDB != nil {
|
||||
db.backend = pathdb.New(diskdb, config.PathDB)
|
||||
db.backend = pathdb.New(diskdb, config.PathDB, config.IsVerkle)
|
||||
} else {
|
||||
var resolver hashdb.ChildResolver
|
||||
if config.IsVerkle {
|
||||
// TODO define verkle resolver
|
||||
log.Crit("Verkle node resolver is not defined")
|
||||
log.Crit("verkle does not use a hash db")
|
||||
} else {
|
||||
resolver = trie.MerkleResolver{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,11 +59,12 @@ var (
|
|||
// layer is the interface implemented by all state layers which includes some
|
||||
// public methods and some additional methods for internal usage.
|
||||
type layer interface {
|
||||
// Node retrieves the trie node with the node info. An error will be returned
|
||||
// if the read operation exits abnormally. For example, if the layer is already
|
||||
// stale, or the associated state is regarded as corrupted. Notably, no error
|
||||
// will be returned if the requested node is not found in database.
|
||||
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
|
||||
// node retrieves the trie node with the node info. An error will be returned
|
||||
// if the read operation exits abnormally. Specifically, if the layer is
|
||||
// already stale.
|
||||
//
|
||||
// Note, no error will be returned if the requested node is not found in database.
|
||||
node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error)
|
||||
|
||||
// rootHash returns the root hash for which this layer was made.
|
||||
rootHash() common.Hash
|
||||
|
|
@ -132,6 +133,7 @@ type Database struct {
|
|||
// the shutdown to reject all following unexpected mutations.
|
||||
readOnly bool // Flag if database is opened in read only mode
|
||||
waitSync bool // Flag if database is deactivated due to initial state sync
|
||||
isVerkle bool // Flag if database is used for verkle tree
|
||||
bufferSize int // Memory allowance (in bytes) for caching dirty nodes
|
||||
config *Config // Configuration for database
|
||||
diskdb ethdb.Database // Persistent storage for matured trie nodes
|
||||
|
|
@ -143,7 +145,7 @@ type Database struct {
|
|||
// New attempts to load an already existing layer from a persistent key-value
|
||||
// store (with a number of memory layers from a journal). If the journal is not
|
||||
// matched with the base persistent layer, all the recorded diff layers are discarded.
|
||||
func New(diskdb ethdb.Database, config *Config) *Database {
|
||||
func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database {
|
||||
if config == nil {
|
||||
config = Defaults
|
||||
}
|
||||
|
|
@ -151,6 +153,7 @@ func New(diskdb ethdb.Database, config *Config) *Database {
|
|||
|
||||
db := &Database{
|
||||
readOnly: config.ReadOnly,
|
||||
isVerkle: isVerkle,
|
||||
bufferSize: config.DirtyCacheSize,
|
||||
config: config,
|
||||
diskdb: diskdb,
|
||||
|
|
@ -208,15 +211,6 @@ func New(diskdb ethdb.Database, config *Config) *Database {
|
|||
return db
|
||||
}
|
||||
|
||||
// Reader retrieves a layer belonging to the given state root.
|
||||
func (db *Database) Reader(root common.Hash) (layer, error) {
|
||||
l := db.tree.get(root)
|
||||
if l == nil {
|
||||
return nil, fmt.Errorf("state %#x is not available", root)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Update adds a new layer into the tree, if that can be linked to an existing
|
||||
// old parent. It is disallowed to insert a disk layer (the origin of all). Apart
|
||||
// from that this function will flatten the extra diff layers at bottom into disk
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func newTester(t *testing.T, historyLimit uint64) *tester {
|
|||
StateHistory: historyLimit,
|
||||
CleanCacheSize: 16 * 1024,
|
||||
DirtyCacheSize: 16 * 1024,
|
||||
})
|
||||
}, false)
|
||||
obj = &tester{
|
||||
db: db,
|
||||
preimages: make(map[common.Hash]common.Address),
|
||||
|
|
@ -305,7 +305,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode
|
|||
return root, ctx.nodes, triestate.New(ctx.accountOrigin, ctx.storageOrigin)
|
||||
}
|
||||
|
||||
// lastRoot returns the latest root hash, or empty if nothing is cached.
|
||||
// lastHash returns the latest root hash, or empty if nothing is cached.
|
||||
func (t *tester) lastHash() common.Hash {
|
||||
if len(t.roots) == 0 {
|
||||
return common.Hash{}
|
||||
|
|
@ -550,7 +550,7 @@ func TestJournal(t *testing.T) {
|
|||
t.Errorf("Failed to journal, err: %v", err)
|
||||
}
|
||||
tester.db.Close()
|
||||
tester.db = New(tester.db.diskdb, nil)
|
||||
tester.db = New(tester.db.diskdb, nil, false)
|
||||
|
||||
// Verify states including disk layer and all diff on top.
|
||||
for i := 0; i < len(tester.roots); i++ {
|
||||
|
|
@ -588,7 +588,7 @@ func TestCorruptedJournal(t *testing.T) {
|
|||
rawdb.WriteTrieJournal(tester.db.diskdb, blob)
|
||||
|
||||
// Verify states, all not-yet-written states should be discarded
|
||||
tester.db = New(tester.db.diskdb, nil)
|
||||
tester.db = New(tester.db.diskdb, nil, false)
|
||||
for i := 0; i < len(tester.roots); i++ {
|
||||
if tester.roots[i] == root {
|
||||
if err := tester.verifyState(root); err != nil {
|
||||
|
|
@ -625,7 +625,7 @@ func TestTailTruncateHistory(t *testing.T) {
|
|||
defer tester.release()
|
||||
|
||||
tester.db.Close()
|
||||
tester.db = New(tester.db.diskdb, &Config{StateHistory: 10})
|
||||
tester.db = New(tester.db.diskdb, &Config{StateHistory: 10}, false)
|
||||
|
||||
head, err := tester.db.freezer.Ancients()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -95,10 +95,9 @@ func (dl *diffLayer) parentLayer() layer {
|
|||
return dl.parent
|
||||
}
|
||||
|
||||
// node retrieves the node with provided node information. It's the internal
|
||||
// version of Node function with additional accessed layer tracked. No error
|
||||
// will be returned if node is not found.
|
||||
func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, error) {
|
||||
// node implements the layer interface, retrieving the trie node blob with the
|
||||
// provided node information. No error will be returned if the node is not found.
|
||||
func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
|
||||
// Hold the lock, ensure the parent won't be changed during the
|
||||
// state accessing.
|
||||
dl.lock.RLock()
|
||||
|
|
@ -109,31 +108,14 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, dept
|
|||
if ok {
|
||||
n, ok := subset[string(path)]
|
||||
if ok {
|
||||
// If the trie node is not hash matched, or marked as removed,
|
||||
// bubble up an error here. It shouldn't happen at all.
|
||||
if n.Hash != hash {
|
||||
dirtyFalseMeter.Mark(1)
|
||||
log.Error("Unexpected trie node in diff layer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
|
||||
return nil, newUnexpectedNodeError("diff", hash, n.Hash, owner, path, n.Blob)
|
||||
}
|
||||
dirtyHitMeter.Mark(1)
|
||||
dirtyNodeHitDepthHist.Update(int64(depth))
|
||||
dirtyReadMeter.Mark(int64(len(n.Blob)))
|
||||
return n.Blob, nil
|
||||
return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil
|
||||
}
|
||||
}
|
||||
// Trie node unknown to this layer, resolve from parent
|
||||
if diff, ok := dl.parent.(*diffLayer); ok {
|
||||
return diff.node(owner, path, hash, depth+1)
|
||||
}
|
||||
// Failed to resolve through diff layers, fallback to disk layer
|
||||
return dl.parent.Node(owner, path, hash)
|
||||
}
|
||||
|
||||
// Node implements the layer interface, retrieving the trie node blob with the
|
||||
// provided node information. No error will be returned if the node is not found.
|
||||
func (dl *diffLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
|
||||
return dl.node(owner, path, hash, 0)
|
||||
return dl.parent.node(owner, path, depth+1)
|
||||
}
|
||||
|
||||
// update implements the layer interface, creating a new layer on top of the
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
|
||||
func emptyLayer() *diskLayer {
|
||||
return &diskLayer{
|
||||
db: New(rawdb.NewMemoryDatabase(), nil),
|
||||
db: New(rawdb.NewMemoryDatabase(), nil, false),
|
||||
buffer: newNodeBuffer(DefaultBufferSize, nil, 0),
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,6 @@ func BenchmarkSearch1Layer(b *testing.B) { benchmarkSearch(b, 127, 128) }
|
|||
func benchmarkSearch(b *testing.B, depth int, total int) {
|
||||
var (
|
||||
npath []byte
|
||||
nhash common.Hash
|
||||
nblob []byte
|
||||
)
|
||||
// First, we set up 128 diff layers, with 3K items each
|
||||
|
|
@ -75,7 +74,6 @@ func benchmarkSearch(b *testing.B, depth int, total int) {
|
|||
if npath == nil && depth == index {
|
||||
npath = common.CopyBytes(path)
|
||||
nblob = common.CopyBytes(node.Blob)
|
||||
nhash = node.Hash
|
||||
}
|
||||
}
|
||||
return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil)
|
||||
|
|
@ -92,7 +90,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) {
|
|||
err error
|
||||
)
|
||||
for i := 0; i < b.N; i++ {
|
||||
have, err = layer.Node(common.Hash{}, npath, nhash)
|
||||
have, _, _, err = layer.node(common.Hash{}, npath, 0)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func newDiskLayer(root common.Hash, id uint64, db *Database, cleans *fastcache.C
|
|||
}
|
||||
}
|
||||
|
||||
// root implements the layer interface, returning root hash of corresponding state.
|
||||
// rootHash implements the layer interface, returning root hash of corresponding state.
|
||||
func (dl *diskLayer) rootHash() common.Hash {
|
||||
return dl.root
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@ func (dl *diskLayer) stateID() uint64 {
|
|||
return dl.id
|
||||
}
|
||||
|
||||
// parent implements the layer interface, returning nil as there's no layer
|
||||
// parentLayer implements the layer interface, returning nil as there's no layer
|
||||
// below the disk.
|
||||
func (dl *diskLayer) parentLayer() layer {
|
||||
return nil
|
||||
|
|
@ -94,27 +94,25 @@ func (dl *diskLayer) markStale() {
|
|||
dl.stale = true
|
||||
}
|
||||
|
||||
// Node implements the layer interface, retrieving the trie node with the
|
||||
// node implements the layer interface, retrieving the trie node with the
|
||||
// provided node info. No error will be returned if the node is not found.
|
||||
func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
|
||||
func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
|
||||
dl.lock.RLock()
|
||||
defer dl.lock.RUnlock()
|
||||
|
||||
if dl.stale {
|
||||
return nil, errSnapshotStale
|
||||
return nil, common.Hash{}, nil, errSnapshotStale
|
||||
}
|
||||
// Try to retrieve the trie node from the not-yet-written
|
||||
// node buffer first. Note the buffer is lock free since
|
||||
// it's impossible to mutate the buffer before tagging the
|
||||
// layer as stale.
|
||||
n, err := dl.buffer.node(owner, path, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != nil {
|
||||
n, found := dl.buffer.node(owner, path)
|
||||
if found {
|
||||
dirtyHitMeter.Mark(1)
|
||||
dirtyReadMeter.Mark(int64(len(n.Blob)))
|
||||
return n.Blob, nil
|
||||
dirtyNodeHitDepthHist.Update(int64(depth))
|
||||
return n.Blob, n.Hash, &nodeLoc{loc: locDirtyCache, depth: depth}, nil
|
||||
}
|
||||
dirtyMissMeter.Mark(1)
|
||||
|
||||
|
|
@ -125,14 +123,9 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b
|
|||
h := newHasher()
|
||||
defer h.release()
|
||||
|
||||
got := h.hash(blob)
|
||||
if got == hash {
|
||||
cleanHitMeter.Mark(1)
|
||||
cleanReadMeter.Mark(int64(len(blob)))
|
||||
return blob, nil
|
||||
}
|
||||
cleanFalseMeter.Mark(1)
|
||||
log.Error("Unexpected trie node in clean cache", "owner", owner, "path", path, "expect", hash, "got", got)
|
||||
cleanHitMeter.Mark(1)
|
||||
cleanReadMeter.Mark(int64(len(blob)))
|
||||
return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
|
||||
}
|
||||
cleanMissMeter.Mark(1)
|
||||
}
|
||||
|
|
@ -146,16 +139,11 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b
|
|||
} else {
|
||||
nBlob, nHash = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path)
|
||||
}
|
||||
if nHash != hash {
|
||||
diskFalseMeter.Mark(1)
|
||||
log.Error("Unexpected trie node in disk", "owner", owner, "path", path, "expect", hash, "got", nHash)
|
||||
return nil, newUnexpectedNodeError("disk", hash, nHash, owner, path, nBlob)
|
||||
}
|
||||
if dl.cleans != nil && len(nBlob) > 0 {
|
||||
dl.cleans.Set(key, nBlob)
|
||||
cleanWriteMeter.Mark(int64(len(nBlob)))
|
||||
}
|
||||
return nBlob, nil
|
||||
return nBlob, nHash, &nodeLoc{loc: locDiskLayer, depth: depth}, nil
|
||||
}
|
||||
|
||||
// update implements the layer interface, returning a new diff layer on top
|
||||
|
|
|
|||
|
|
@ -16,13 +16,7 @@
|
|||
|
||||
package pathdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// errDatabaseReadOnly is returned if the database is opened in read only mode
|
||||
|
|
@ -45,16 +39,4 @@ var (
|
|||
// errStateUnrecoverable is returned if state is required to be reverted to
|
||||
// a destination without associated state history available.
|
||||
errStateUnrecoverable = errors.New("state is unrecoverable")
|
||||
|
||||
// errUnexpectedNode is returned if the requested node with specified path is
|
||||
// not hash matched with expectation.
|
||||
errUnexpectedNode = errors.New("unexpected node")
|
||||
)
|
||||
|
||||
func newUnexpectedNodeError(loc string, expHash common.Hash, gotHash common.Hash, owner common.Hash, path []byte, blob []byte) error {
|
||||
blobHex := "nil"
|
||||
if len(blob) > 0 {
|
||||
blobHex = hexutil.Encode(blob)
|
||||
}
|
||||
return fmt.Errorf("%w, loc: %s, node: (%x %v), %x!=%x, blob: %s", errUnexpectedNode, loc, owner, path, expHash, gotHash, blobHex)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ var (
|
|||
cleanFalseMeter = metrics.NewRegisteredMeter("pathdb/clean/false", nil)
|
||||
dirtyFalseMeter = metrics.NewRegisteredMeter("pathdb/dirty/false", nil)
|
||||
diskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil)
|
||||
diffFalseMeter = metrics.NewRegisteredMeter("pathdb/diff/false", nil)
|
||||
|
||||
commitTimeTimer = metrics.NewRegisteredTimer("pathdb/commit/time", nil)
|
||||
commitNodesMeter = metrics.NewRegisteredMeter("pathdb/commit/nodes", nil)
|
||||
|
|
|
|||
|
|
@ -59,21 +59,16 @@ func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, l
|
|||
}
|
||||
|
||||
// node retrieves the trie node with given node info.
|
||||
func (b *nodebuffer) node(owner common.Hash, path []byte, hash common.Hash) (*trienode.Node, error) {
|
||||
func (b *nodebuffer) node(owner common.Hash, path []byte) (*trienode.Node, bool) {
|
||||
subset, ok := b.nodes[owner]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
return nil, false
|
||||
}
|
||||
n, ok := subset[string(path)]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
return nil, false
|
||||
}
|
||||
if n.Hash != hash {
|
||||
dirtyFalseMeter.Mark(1)
|
||||
log.Error("Unexpected trie node in node buffer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
|
||||
return nil, newUnexpectedNodeError("dirty", hash, n.Hash, owner, path, n.Blob)
|
||||
}
|
||||
return n, nil
|
||||
return n, true
|
||||
}
|
||||
|
||||
// commit merges the dirty nodes into the nodebuffer. This operation won't take
|
||||
|
|
|
|||
94
triedb/pathdb/reader.go
Normal file
94
triedb/pathdb/reader.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2024 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 pathdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/triedb/database"
|
||||
)
|
||||
|
||||
// The types of locations where the node is found.
|
||||
const (
|
||||
locDirtyCache = "dirty" // dirty cache
|
||||
locCleanCache = "clean" // clean cache
|
||||
locDiskLayer = "disk" // persistent state
|
||||
locDiffLayer = "diff" // diff layers
|
||||
)
|
||||
|
||||
// nodeLoc is a helpful structure that contains the location where the node
|
||||
// is found, as it's useful for debugging purposes.
|
||||
type nodeLoc struct {
|
||||
loc string
|
||||
depth int
|
||||
}
|
||||
|
||||
// string returns the string representation of node location.
|
||||
func (loc *nodeLoc) string() string {
|
||||
return fmt.Sprintf("loc: %s, depth: %d", loc.loc, loc.depth)
|
||||
}
|
||||
|
||||
// reader implements the database.Reader interface, providing the functionalities to
|
||||
// retrieve trie nodes by wrapping the internal state layer.
|
||||
type reader struct {
|
||||
layer layer
|
||||
noHashCheck bool
|
||||
}
|
||||
|
||||
// Node implements database.Reader interface, retrieving the node with specified
|
||||
// node info. Don't modify the returned byte slice since it's not deep-copied
|
||||
// and still be referenced by database.
|
||||
func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
|
||||
blob, got, loc, err := r.layer.node(owner, path, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Error out if the local one is inconsistent with the target.
|
||||
if !r.noHashCheck && got != hash {
|
||||
// Location is always available even if the node
|
||||
// is not found.
|
||||
switch loc.loc {
|
||||
case locCleanCache:
|
||||
cleanFalseMeter.Mark(1)
|
||||
case locDirtyCache:
|
||||
dirtyFalseMeter.Mark(1)
|
||||
case locDiffLayer:
|
||||
diffFalseMeter.Mark(1)
|
||||
case locDiskLayer:
|
||||
diskFalseMeter.Mark(1)
|
||||
}
|
||||
blobHex := "nil"
|
||||
if len(blob) > 0 {
|
||||
blobHex = hexutil.Encode(blob)
|
||||
}
|
||||
log.Error("Unexpected trie node", "location", loc.loc, "owner", owner, "path", path, "expect", hash, "got", got, "blob", blobHex)
|
||||
return nil, fmt.Errorf("unexpected node: (%x %v), %x!=%x, %s, blob: %s", owner, path, hash, got, loc.string(), blobHex)
|
||||
}
|
||||
return blob, nil
|
||||
}
|
||||
|
||||
// Reader retrieves a layer belonging to the given state root.
|
||||
func (db *Database) Reader(root common.Hash) (database.Reader, error) {
|
||||
layer := db.tree.get(root)
|
||||
if layer == nil {
|
||||
return nil, fmt.Errorf("state %#x is not available", root)
|
||||
}
|
||||
return &reader{layer: layer, noHashCheck: db.isVerkle}, nil
|
||||
}
|
||||
Loading…
Reference in a new issue