mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-11 06:23:47 +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])
|
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 {
|
func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
|
||||||
dst := reflect.ValueOf(v).Elem()
|
dst := reflect.ValueOf(v).Elem()
|
||||||
src := reflect.ValueOf(marshalledValues)
|
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 {
|
func waitWatcherStart(ks *KeyStore) bool {
|
||||||
// On systems where file watch is not supported, just return "ok".
|
// On systems where file watch is not supported, just return "ok".
|
||||||
if !ks.cache.watcher.enabled() {
|
if !ks.cache.watcher.enabled() {
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,9 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
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
|
var config lightClientConfig
|
||||||
|
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
||||||
|
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.GoerliFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(utils.MainnetFlag.Name):
|
case ctx.Bool(utils.MainnetFlag.Name):
|
||||||
config = MainnetConfig
|
config = MainnetConfig
|
||||||
|
|
@ -87,24 +87,37 @@ func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
||||||
config = MainnetConfig
|
config = MainnetConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if customConfig && config.Forks != nil {
|
// Genesis root and time should always be specified together with custom chain config
|
||||||
utils.Fatalf("Cannot use custom beacon chain config flags in combination with pre-defined network config")
|
if customConfig {
|
||||||
}
|
if !ctx.IsSet(utils.BeaconGenesisRootFlag.Name) {
|
||||||
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 {
|
if c, err := hexutil.Decode(ctx.String(utils.BeaconGenesisRootFlag.Name)); err == nil && len(c) <= 32 {
|
||||||
copy(config.GenesisValidatorsRoot[:len(c)], c)
|
copy(config.GenesisValidatorsRoot[:len(c)], c)
|
||||||
} else {
|
} else {
|
||||||
utils.Fatalf("Invalid hex string", "beacon.genesis.gvroot", ctx.String(utils.BeaconGenesisRootFlag.Name), "error", err)
|
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 {
|
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)
|
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 ctx.IsSet(utils.BeaconCheckpointFlag.Name) {
|
||||||
if c, err := hexutil.Decode(ctx.String(utils.BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 {
|
if c, err := hexutil.Decode(ctx.String(utils.BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 {
|
||||||
copy(config.Checkpoint[:len(c)], c)
|
copy(config.Checkpoint[:len(c)], c)
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,13 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/donovanhide/eventsource"
|
"github.com/donovanhide/eventsource"
|
||||||
|
|
@ -287,7 +289,7 @@ func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
|
||||||
}, nil
|
}, 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.
|
// If blockRoot is null hash then the latest head header is fetched.
|
||||||
func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, error) {
|
func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, error) {
|
||||||
var blockId string
|
var blockId string
|
||||||
|
|
@ -416,39 +418,34 @@ type HeadEventListener struct {
|
||||||
// The callbacks are also called for the current head and optimistic head at startup.
|
// The callbacks are also called for the current head and optimistic head at startup.
|
||||||
// They are never called concurrently.
|
// They are never called concurrently.
|
||||||
func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func() {
|
func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func() {
|
||||||
closeCh := make(chan struct{}) // initiate closing the stream
|
var (
|
||||||
closedCh := make(chan struct{}) // stream closed (or failed to create)
|
ctx, closeCtx = context.WithCancel(context.Background())
|
||||||
stoppedCh := make(chan struct{}) // sync loop stopped
|
streamCh = make(chan *eventsource.Stream, 1)
|
||||||
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() {
|
go func() {
|
||||||
defer close(closedCh)
|
defer wg.Done()
|
||||||
// when connected to a Lodestar node the subscription blocks until the
|
stream := api.startEventStream(ctx, &listener)
|
||||||
// first actual event arrives; therefore we create the subscription in
|
if stream == nil {
|
||||||
// a separate goroutine while letting the main goroutine sync up to the
|
// This case happens when the context was closed.
|
||||||
// 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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Stream was opened, wait for close signal.
|
||||||
streamCh <- stream
|
streamCh <- stream
|
||||||
<-closeCh
|
<-ctx.Done()
|
||||||
stream.Close()
|
stream.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(stoppedCh)
|
defer wg.Done()
|
||||||
|
|
||||||
|
// Request initial data.
|
||||||
if head, err := api.GetHeader(common.Hash{}); err == nil {
|
if head, err := api.GetHeader(common.Hash{}); err == nil {
|
||||||
listener.OnNewHead(head.Slot, head.Hash())
|
listener.OnNewHead(head.Slot, head.Hash())
|
||||||
}
|
}
|
||||||
|
|
@ -458,32 +455,42 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
||||||
if finalityUpdate, err := api.GetFinalityUpdate(); err == nil {
|
if finalityUpdate, err := api.GetFinalityUpdate(); err == nil {
|
||||||
listener.OnFinality(finalityUpdate)
|
listener.OnFinality(finalityUpdate)
|
||||||
}
|
}
|
||||||
stream := <-streamCh
|
|
||||||
if stream == nil {
|
// Receive the stream.
|
||||||
|
var stream *eventsource.Stream
|
||||||
|
select {
|
||||||
|
case stream = <-streamCh:
|
||||||
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
stream.Close()
|
||||||
|
|
||||||
case event, ok := <-stream.Events:
|
case event, ok := <-stream.Events:
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch event.Event() {
|
switch event.Event() {
|
||||||
case "head":
|
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)
|
listener.OnNewHead(slot, blockRoot)
|
||||||
} else {
|
} else {
|
||||||
listener.OnError(fmt.Errorf("error decoding head event: %v", err))
|
listener.OnError(fmt.Errorf("error decoding head event: %v", err))
|
||||||
}
|
}
|
||||||
case "light_client_optimistic_update":
|
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)
|
listener.OnSignedHead(signedHead)
|
||||||
} else {
|
} else {
|
||||||
listener.OnError(fmt.Errorf("error decoding optimistic update event: %v", err))
|
listener.OnError(fmt.Errorf("error decoding optimistic update event: %v", err))
|
||||||
}
|
}
|
||||||
case "light_client_finality_update":
|
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)
|
listener.OnFinality(finalityUpdate)
|
||||||
} else {
|
} else {
|
||||||
listener.OnError(fmt.Errorf("error decoding finality update event: %v", err))
|
listener.OnError(fmt.Errorf("error decoding finality update event: %v", err))
|
||||||
|
|
@ -491,6 +498,7 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
||||||
default:
|
default:
|
||||||
listener.OnError(fmt.Errorf("unexpected event: %s", event.Event()))
|
listener.OnError(fmt.Errorf("unexpected event: %s", event.Event()))
|
||||||
}
|
}
|
||||||
|
|
||||||
case err, ok := <-stream.Errors:
|
case err, ok := <-stream.Errors:
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
|
@ -499,9 +507,43 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
close(closeCh)
|
closeCtx()
|
||||||
<-closedCh
|
wg.Wait()
|
||||||
<-stoppedCh
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
return h.signedHead, h.hasSignedHead
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatedHead returns the latest validated head.
|
// ValidatedFinality returns the latest validated finality.
|
||||||
func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
||||||
h.lock.RLock()
|
h.lock.RLock()
|
||||||
defer h.lock.RUnlock()
|
defer h.lock.RUnlock()
|
||||||
|
|
@ -64,7 +64,7 @@ func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
||||||
return h.finalityUpdate, h.hasFinalityUpdate
|
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
|
// 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
|
// signers) then ValidatedHead is updated. The boolean return flag signals if
|
||||||
// ValidatedHead has been changed.
|
// 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() {
|
func (s *serverWithTimeout) unsubscribe() {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
@ -337,7 +337,7 @@ func (s *serverWithLimits) sendRequest(request Request) (reqId ID) {
|
||||||
return s.serverWithTimeout.sendRequest(request)
|
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() {
|
func (s *serverWithLimits) unsubscribe() {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ func (s *HeadSync) newSignedHead(server request.Server, signedHead types.SignedH
|
||||||
s.headTracker.ValidateHead(signedHead)
|
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.
|
// is properly synced or stores it for further validation.
|
||||||
func (s *HeadSync) newFinalityUpdate(server request.Server, finalityUpdate types.FinalityUpdate) {
|
func (s *HeadSync) newFinalityUpdate(server request.Server, finalityUpdate types.FinalityUpdate) {
|
||||||
if !s.chainInit || types.SyncPeriod(finalityUpdate.SignatureSlot) > s.nextSyncPeriod {
|
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)
|
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.
|
// those which can be validated.
|
||||||
func (s *HeadSync) processUnvalidated() {
|
func (s *HeadSync) processUnvalidated() {
|
||||||
if !s.chainInit {
|
if !s.chainInit {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type ExecutionHeader struct {
|
||||||
obj headerObject
|
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.
|
// the beacon chain API.
|
||||||
func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, error) {
|
func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, error) {
|
||||||
var obj headerObject
|
var obj headerObject
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestAttachWithHeaders(t *testing.T) {
|
||||||
// This is fixed in a follow-up PR.
|
// 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.
|
// that custom headers are forwarded to the target.
|
||||||
func TestRemoteDbWithHeaders(t *testing.T) {
|
func TestRemoteDbWithHeaders(t *testing.T) {
|
||||||
t.Parallel()
|
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) {
|
func TestDeletionExport(t *testing.T) {
|
||||||
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ func (l *list[T]) init() {
|
||||||
l.root.prev = &l.root
|
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]) {
|
func (l *list[T]) pushElem(e *listElem[T]) {
|
||||||
e.prev = &l.root
|
e.prev = &l.root
|
||||||
e.next = l.root.next
|
e.next = l.root.next
|
||||||
|
|
|
||||||
|
|
@ -568,7 +568,7 @@ var (
|
||||||
u256_32 = uint256.NewInt(32)
|
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
|
// reward. The total reward consists of the static block reward and rewards for
|
||||||
// included uncles. The coinbase of each uncle block is also rewarded.
|
// 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) {
|
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
|
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 {
|
func (l *lexer) accept(valid string) bool {
|
||||||
if strings.ContainsRune(valid, l.next()) {
|
if strings.ContainsRune(valid, l.next()) {
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,11 @@ type CacheConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// triedbConfig derives the configures for trie database.
|
// triedbConfig derives the configures for trie database.
|
||||||
func (c *CacheConfig) triedbConfig() *triedb.Config {
|
func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
||||||
config := &triedb.Config{Preimages: c.Preimages}
|
config := &triedb.Config{
|
||||||
|
Preimages: c.Preimages,
|
||||||
|
IsVerkle: isVerkle,
|
||||||
|
}
|
||||||
if c.StateScheme == rawdb.HashScheme {
|
if c.StateScheme == rawdb.HashScheme {
|
||||||
config.HashDB = &hashdb.Config{
|
config.HashDB = &hashdb.Config{
|
||||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||||
|
|
@ -265,7 +268,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
cacheConfig = defaultCacheConfig
|
cacheConfig = defaultCacheConfig
|
||||||
}
|
}
|
||||||
// Open trie database with provided config
|
// 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
|
// Setup the genesis block, commit the provided genesis specification
|
||||||
// to database if the genesis block is not present yet, or load the
|
// 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) {
|
func (bc *BlockChain) rewindHashHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
||||||
var (
|
var (
|
||||||
limit uint64 // The oldest block that will be searched for this rewinding
|
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/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/gballet/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -418,6 +419,112 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
|
||||||
return db, blocks, receipts
|
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 {
|
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
|
time := parent.Time() + 10 // block time is fixed at 10 seconds
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
|
|
@ -482,7 +589,7 @@ func makeBlockChain(chainConfig *params.ChainConfig, parent *types.Block, n int,
|
||||||
return blocks
|
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) {
|
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) {
|
db, blocks, _ := GenerateChainWithGenesis(genesis, engine, n, func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
||||||
|
|
|
||||||
|
|
@ -695,7 +695,7 @@ func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
|
||||||
return nil
|
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 {
|
func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error {
|
||||||
logIndex := uint(0)
|
logIndex := uint(0)
|
||||||
if len(txs) != len(receipts) {
|
if len(txs) != len(receipts) {
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,8 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
case *trie.StateTrie:
|
case *trie.StateTrie:
|
||||||
return t.Copy()
|
return t.Copy()
|
||||||
|
case *trie.VerkleTrie:
|
||||||
|
return t.Copy()
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unknown trie type %T", t))
|
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
|
// Insert all the pending storage updates into the trie
|
||||||
usedStorage := make([][]byte, 0, len(s.pendingStorage))
|
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 {
|
for key, value := range s.pendingStorage {
|
||||||
// Skip noop changes, persist actual changes
|
// Skip noop changes, persist actual changes
|
||||||
if value == s.originStorage[key] {
|
if value == s.originStorage[key] {
|
||||||
|
|
@ -307,13 +319,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
||||||
s.originStorage[key] = value
|
s.originStorage[key] = value
|
||||||
|
|
||||||
var encoded []byte // rlp-encoded value to be used by the snapshot
|
var encoded []byte // rlp-encoded value to be used by the snapshot
|
||||||
if (value == common.Hash{}) {
|
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 {
|
|
||||||
// Encoding []byte cannot fail, ok to ignore the error.
|
// Encoding []byte cannot fail, ok to ignore the error.
|
||||||
trimmed := common.TrimLeftZeroes(value[:])
|
trimmed := common.TrimLeftZeroes(value[:])
|
||||||
encoded, _ = rlp.EncodeToBytes(trimmed)
|
encoded, _ = rlp.EncodeToBytes(trimmed)
|
||||||
|
|
@ -322,6 +328,8 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.db.StorageUpdated += 1
|
s.db.StorageUpdated += 1
|
||||||
|
} else {
|
||||||
|
deletions = append(deletions, key)
|
||||||
}
|
}
|
||||||
// Cache the mutated storage slots until commit
|
// Cache the mutated storage slots until commit
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
|
|
@ -353,6 +361,13 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
||||||
// Cache the items for preloading
|
// Cache the items for preloading
|
||||||
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
|
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 {
|
if s.db.prefetcher != nil {
|
||||||
s.db.prefetcher.used(s.addrHash, s.data.Root, usedStorage)
|
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.
|
// 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
|
// 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())
|
defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
|
||||||
|
|
||||||
// Delete the account from the trie
|
// Delete the account from the trie
|
||||||
addr := obj.Address()
|
|
||||||
if err := s.trie.DeleteAccount(addr); err != nil {
|
if err := s.trie.DeleteAccount(addr); err != nil {
|
||||||
s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err))
|
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))
|
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 {
|
for addr := range s.stateObjectsPending {
|
||||||
if obj := s.stateObjects[addr]; obj.deleted {
|
if obj := s.stateObjects[addr]; !obj.deleted {
|
||||||
s.deleteStateObject(obj)
|
|
||||||
s.AccountDeleted += 1
|
|
||||||
} else {
|
|
||||||
s.updateStateObject(obj)
|
s.updateStateObject(obj)
|
||||||
s.AccountUpdated += 1
|
s.AccountUpdated += 1
|
||||||
|
} else {
|
||||||
|
deletedAddrs = append(deletedAddrs, obj.address)
|
||||||
}
|
}
|
||||||
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
|
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
|
||||||
}
|
}
|
||||||
|
for _, deletedAddr := range deletedAddrs {
|
||||||
|
s.deleteStateObject(deletedAddr)
|
||||||
|
s.AccountDeleted += 1
|
||||||
|
}
|
||||||
if prefetcher != nil {
|
if prefetcher != nil {
|
||||||
prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs)
|
prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs)
|
||||||
}
|
}
|
||||||
|
|
@ -1143,6 +1156,11 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) error {
|
||||||
return nil
|
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.
|
// Commit writes the state to the underlying in-memory trie database.
|
||||||
// Once the state is committed, tries cached in stateDB (including account
|
// Once the state is committed, tries cached in stateDB (including account
|
||||||
// trie, storage tries) will no longer be functional. A new state instance
|
// 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))
|
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
|
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).
|
// consensus validity and pool restrictions).
|
||||||
func (p *BlobPool) add(tx *types.Transaction) (err error) {
|
func (p *BlobPool) add(tx *types.Transaction) (err error) {
|
||||||
// The blob pool blocks on adding a transaction. This is because blob txs are
|
// 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
|
return output, suppliedGas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECRECOVER implemented as a native contract.
|
// ecrecover implemented as a native contract.
|
||||||
type ecrecover struct{}
|
type ecrecover struct{}
|
||||||
|
|
||||||
func (c *ecrecover) RequiredGas(input []byte) uint64 {
|
func (c *ecrecover) RequiredGas(input []byte) uint64 {
|
||||||
|
|
@ -457,7 +457,7 @@ func runBn256Add(input []byte) ([]byte, error) {
|
||||||
return res.Marshal(), nil
|
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.
|
// Istanbul consensus rules.
|
||||||
type bn256AddIstanbul struct{}
|
type bn256AddIstanbul struct{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func (ctx *ScopeContext) MemoryData() []byte {
|
||||||
return ctx.Memory.Data()
|
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.
|
// of the returned data.
|
||||||
func (ctx *ScopeContext) StackData() []uint256.Int {
|
func (ctx *ScopeContext) StackData() []uint256.Int {
|
||||||
if ctx.Stack == nil {
|
if ctx.Stack == nil {
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ type btCurve struct {
|
||||||
*btcec.KoblitzCurve
|
*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 {
|
func (curve btCurve) Marshal(x, y *big.Int) []byte {
|
||||||
byteLen := (curve.Params().BitSize + 7) / 8
|
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
|
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Ethereum object (including the
|
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
|
||||||
// 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) {
|
func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
// Ensure configuration values are compatible and sane
|
// Ensure configuration values are compatible and sane
|
||||||
if config.SyncMode == downloader.LightSync {
|
if config.SyncMode == downloader.LightSync {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func (w *withdrawalQueue) gatherPending(maxCount int) []*types.Withdrawal {
|
||||||
case withdrawal := <-w.pending:
|
case withdrawal := <-w.pending:
|
||||||
withdrawals = append(withdrawals, withdrawal)
|
withdrawals = append(withdrawals, withdrawal)
|
||||||
if len(withdrawals) == maxCount {
|
if len(withdrawals) == maxCount {
|
||||||
break
|
return withdrawals
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return withdrawals
|
return withdrawals
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func newTester(t *testing.T) *downloadTester {
|
||||||
return newTesterWithNotification(t, nil)
|
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 {
|
func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
|
||||||
freezer := t.TempDir()
|
freezer := t.TempDir()
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false)
|
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
|
// and sets an optional serve hook that can return headers for delivery instead
|
||||||
// of the predefined chain. Useful for emulating malicious behavior that would
|
// of the predefined chain. Useful for emulating malicious behavior that would
|
||||||
// otherwise require dedicated peer types.
|
// 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) {
|
func TestInvalidGetLogsRequest(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func newTestBackend(blocks int) *testBackend {
|
||||||
return newTestBackendWithGenerator(blocks, false, nil)
|
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.
|
// wraps it into a mock backend.
|
||||||
func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
|
func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -2201,7 +2201,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
// If the chunk's root is an overflown but full delivery,
|
// If the chunk's root is an overflown but full delivery,
|
||||||
// clear the heal request.
|
// clear the heal request.
|
||||||
accountHash := res.accounts[len(res.accounts)-1]
|
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 {
|
for i, account := range res.mainTask.res.hashes {
|
||||||
if account == accountHash {
|
if account == accountHash {
|
||||||
res.mainTask.needHeal[i] = false
|
res.mainTask.needHeal[i] = false
|
||||||
|
|
|
||||||
|
|
@ -839,7 +839,7 @@ func testMultiSyncManyUseless(t *testing.T, scheme string) {
|
||||||
verifyTrie(scheme, syncer.db, sourceAccountTrie.Hash(), t)
|
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) {
|
func TestMultiSyncManyUselessWithLowTimeout(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
@ -1378,7 +1378,7 @@ func testSyncWithStorageAndNonProvingPeer(t *testing.T, scheme string) {
|
||||||
verifyTrie(scheme, syncer.db, sourceAccountTrie.Hash(), t)
|
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
|
// a peer who insists on delivering full storage sets _and_ proofs. This triggered
|
||||||
// an error, where the recipient erroneously clipped the boundary nodes, but
|
// an error, where the recipient erroneously clipped the boundary nodes, but
|
||||||
// did not mark the account for healing.
|
// 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
|
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.
|
// 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 {
|
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend {
|
||||||
backend := &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
|
// comparison
|
||||||
func jsonEqualFlat(x, y interface{}) bool {
|
func jsonEqualFlat(x, y interface{}) bool {
|
||||||
xTrace := new([]flatCallTrace)
|
xTrace := new([]flatCallTrace)
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ func MemoryPtr(m []byte, offset, size int64) []byte {
|
||||||
return nil
|
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 {
|
func StackBack(st []uint256.Int, n int) *uint256.Int {
|
||||||
return &st[len(st)-n-1]
|
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'
|
// in 'result'
|
||||||
func TestNoStepExec(t *testing.T) {
|
func TestNoStepExec(t *testing.T) {
|
||||||
execTracer := func(code string) []byte {
|
execTracer := func(code string) []byte {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ func (al accessList) equal(other accessList) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// accesslist converts the accesslist to a types.AccessList.
|
// accessList converts the accesslist to a types.AccessList.
|
||||||
func (al accessList) accessList() types.AccessList {
|
func (al accessList) accessList() types.AccessList {
|
||||||
acl := make(types.AccessList, 0, len(al))
|
acl := make(types.AccessList, 0, len(al))
|
||||||
for addr, slots := range al {
|
for addr, slots := range al {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package simulated
|
package simulated
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"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
|
// Backend is a simulated blockchain. You can use it to test your contracts or
|
||||||
// other code that interacts with the Ethereum chain.
|
// other code that interacts with the Ethereum chain.
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
eth *eth.Ethereum
|
node *node.Node
|
||||||
beacon *catalyst.SimulatedBeacon
|
beacon *catalyst.SimulatedBeacon
|
||||||
client simClient
|
client simClient
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +130,7 @@ func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backe
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Backend{
|
return &Backend{
|
||||||
eth: backend,
|
node: stack,
|
||||||
beacon: beacon,
|
beacon: beacon,
|
||||||
client: simClient{ethclient.NewClient(stack.Attach())},
|
client: simClient{ethclient.NewClient(stack.Attach())},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -142,12 +143,16 @@ func (n *Backend) Close() error {
|
||||||
n.client.Close()
|
n.client.Close()
|
||||||
n.client = simClient{}
|
n.client = simClient{}
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
if n.beacon != nil {
|
if n.beacon != nil {
|
||||||
err := n.beacon.Stop()
|
err = n.beacon.Stop()
|
||||||
n.beacon = nil
|
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.
|
// 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
|
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 {
|
func randBytes(len int) []byte {
|
||||||
buf := make([]byte, len)
|
buf := make([]byte, len)
|
||||||
if n, err := rand.Read(buf); n != len || err != nil {
|
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
|
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) {
|
func newSnappyReader(e *e2store.Reader, expectedType uint16, off int64) (io.Reader, int64, error) {
|
||||||
r, n, err := e.ReaderAt(expectedType, off)
|
r, n, err := e.ReaderAt(expectedType, off)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1186,9 +1186,6 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
call := args.ToMessage(header.BaseFee)
|
call := args.ToMessage(header.BaseFee)
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// Run the gas estimation andwrap any revertals into a custom return
|
// Run the gas estimation andwrap any revertals into a custom return
|
||||||
estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
|
estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1519,9 +1516,6 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
// Set the accesslist to the last al
|
// Set the accesslist to the last al
|
||||||
args.AccessList = &accessList
|
args.AccessList = &accessList
|
||||||
msg := args.ToMessage(header.BaseFee)
|
msg := args.ToMessage(header.BaseFee)
|
||||||
if err != nil {
|
|
||||||
return nil, 0, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the transaction with the access list tracer
|
// Apply the transaction with the access list tracer
|
||||||
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
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
|
// 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,
|
// 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()
|
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) {
|
func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
|
||||||
if !l.inner.Enabled(context.Background(), level) {
|
if !l.inner.Enabled(context.Background(), level) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ func (g *StandardGauge) Update(v int64) {
|
||||||
g.value.Store(v)
|
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) {
|
func (g *StandardGauge) UpdateIfGt(v int64) {
|
||||||
for {
|
for {
|
||||||
exist := g.value.Load()
|
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{})}
|
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() {
|
func (ma *meterArbiter) tick() {
|
||||||
for range ma.ticker.C {
|
for range ma.ticker.C {
|
||||||
ma.tickMeters()
|
ma.tickMeters()
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ var enablerFlags = []string{"metrics"}
|
||||||
// enablerEnvVars is the env var names to use to enable metrics collections.
|
// enablerEnvVars is the env var names to use to enable metrics collections.
|
||||||
var enablerEnvVars = []string{"GETH_METRICS"}
|
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
|
// 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.
|
// and peek into the command line args for the metrics flag.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -597,7 +597,7 @@ func newIPCServer(log log.Logger, endpoint string) *ipcServer {
|
||||||
return &ipcServer{log: log, endpoint: endpoint}
|
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 {
|
func (is *ipcServer) start(apis []rpc.API) error {
|
||||||
is.mu.Lock()
|
is.mu.Lock()
|
||||||
defer is.mu.Unlock()
|
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.
|
// inbound and outbound network traffic.
|
||||||
type meteredUdpConn struct {
|
type meteredUdpConn struct {
|
||||||
UDPConn
|
UDPConn
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestGetSetID(t *testing.T) {
|
||||||
assert.Equal(t, id, id2)
|
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) {
|
func TestGetSetIPv4(t *testing.T) {
|
||||||
ip := IPv4{192, 168, 0, 3}
|
ip := IPv4{192, 168, 0, 3}
|
||||||
var r Record
|
var r Record
|
||||||
|
|
@ -59,7 +59,7 @@ func TestGetSetIPv4(t *testing.T) {
|
||||||
assert.Equal(t, ip, ip2)
|
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) {
|
func TestGetSetIPv6(t *testing.T) {
|
||||||
ip := IPv6{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
ip := IPv6{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
||||||
var r Record
|
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
|
// 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
|
// specified remove mask then only the included flags are de-associated and the timer
|
||||||
// stays active.
|
// stays active.
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ func RegisterLifecycles(lifecycles LifecycleConstructors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds the host part to the configuration's ENR, signs it
|
// 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 {
|
func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error {
|
||||||
enrIp := enr.IP(ip)
|
enrIp := enr.IP(ip)
|
||||||
n.Record.Set(&enrIp)
|
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
|
// filter
|
||||||
func TestMsgFilterFailBadParams(t *testing.T) {
|
func TestMsgFilterFailBadParams(t *testing.T) {
|
||||||
// start the server
|
// start the server
|
||||||
|
|
|
||||||
|
|
@ -535,7 +535,7 @@ func (net *Network) GetRandomUpNode(excludeIDs ...enode.ID) *Node {
|
||||||
return net.getRandomUpNode(excludeIDs...)
|
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 {
|
func (net *Network) getRandomUpNode(excludeIDs ...enode.ID) *Node {
|
||||||
return net.getRandomNode(net.getUpNodeIDs(), excludeIDs)
|
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)) {
|
func (h *handler) handleResponses(batch []*jsonrpcMessage, handleCall func(*jsonrpcMessage)) {
|
||||||
var resolvedops []*requestOp
|
var resolvedops []*requestOp
|
||||||
handleResp := func(msg *jsonrpcMessage) {
|
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{} {
|
func (c *jsonCodec) closed() <-chan interface{} {
|
||||||
return c.closeCh
|
return c.closeCh
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -235,10 +235,10 @@ func (c *mockConn) writeJSON(ctx context.Context, msg interface{}, isError bool)
|
||||||
return c.enc.Encode(msg)
|
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 }
|
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 "" }
|
func (c *mockConn) remoteAddr() string { return "" }
|
||||||
|
|
||||||
// BenchmarkNotify benchmarks the performance of notifying a subscription.
|
// 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)
|
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 {
|
func (t Types) validate() error {
|
||||||
for typeKey, typeArr := range t {
|
for typeKey, typeArr := range t {
|
||||||
if len(typeKey) == 0 {
|
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
|
// specific data, and we fill the TypedData struct on our side
|
||||||
func TestGnosisCustomDataWithChainId(t *testing.T) {
|
func TestGnosisCustomDataWithChainId(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ type testFailure struct {
|
||||||
reason string
|
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) {
|
func (tm *testMatcher) slow(pattern string) {
|
||||||
tm.slowpat = append(tm.slowpat, regexp.MustCompile(pattern))
|
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)
|
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.
|
// Execute the message.
|
||||||
snapshot := st.StateDB.Snapshot()
|
snapshot := st.StateDB.Snapshot()
|
||||||
gaspool := new(core.GasPool)
|
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.
|
// The test cases are generated randomly.
|
||||||
func TestRangeProofWithNonExistentProof(t *testing.T) {
|
func TestRangeProofWithNonExistentProof(t *testing.T) {
|
||||||
trie, vals := randomTrie(4096)
|
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,
|
// 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
|
// storage tries are small (a couple of entries), whereas the full post-block account trie update is large (a couple
|
||||||
// of thousand entries)
|
// of thousand entries)
|
||||||
|
|
|
||||||
|
|
@ -108,12 +108,12 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
|
||||||
log.Crit("Both 'hash' and 'path' mode are configured")
|
log.Crit("Both 'hash' and 'path' mode are configured")
|
||||||
}
|
}
|
||||||
if config.PathDB != nil {
|
if config.PathDB != nil {
|
||||||
db.backend = pathdb.New(diskdb, config.PathDB)
|
db.backend = pathdb.New(diskdb, config.PathDB, config.IsVerkle)
|
||||||
} else {
|
} else {
|
||||||
var resolver hashdb.ChildResolver
|
var resolver hashdb.ChildResolver
|
||||||
if config.IsVerkle {
|
if config.IsVerkle {
|
||||||
// TODO define verkle resolver
|
// TODO define verkle resolver
|
||||||
log.Crit("Verkle node resolver is not defined")
|
log.Crit("verkle does not use a hash db")
|
||||||
} else {
|
} else {
|
||||||
resolver = trie.MerkleResolver{}
|
resolver = trie.MerkleResolver{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,12 @@ var (
|
||||||
// layer is the interface implemented by all state layers which includes some
|
// layer is the interface implemented by all state layers which includes some
|
||||||
// public methods and some additional methods for internal usage.
|
// public methods and some additional methods for internal usage.
|
||||||
type layer interface {
|
type layer interface {
|
||||||
// Node retrieves the trie node with the node info. An error will be returned
|
// 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
|
// if the read operation exits abnormally. Specifically, if the layer is
|
||||||
// stale, or the associated state is regarded as corrupted. Notably, no error
|
// already stale.
|
||||||
// will be returned if the requested node is not found in database.
|
//
|
||||||
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
|
// 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 returns the root hash for which this layer was made.
|
||||||
rootHash() common.Hash
|
rootHash() common.Hash
|
||||||
|
|
@ -132,6 +133,7 @@ type Database struct {
|
||||||
// the shutdown to reject all following unexpected mutations.
|
// the shutdown to reject all following unexpected mutations.
|
||||||
readOnly bool // Flag if database is opened in read only mode
|
readOnly bool // Flag if database is opened in read only mode
|
||||||
waitSync bool // Flag if database is deactivated due to initial state sync
|
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
|
bufferSize int // Memory allowance (in bytes) for caching dirty nodes
|
||||||
config *Config // Configuration for database
|
config *Config // Configuration for database
|
||||||
diskdb ethdb.Database // Persistent storage for matured trie nodes
|
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
|
// 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
|
// 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.
|
// 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 {
|
if config == nil {
|
||||||
config = Defaults
|
config = Defaults
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +153,7 @@ func New(diskdb ethdb.Database, config *Config) *Database {
|
||||||
|
|
||||||
db := &Database{
|
db := &Database{
|
||||||
readOnly: config.ReadOnly,
|
readOnly: config.ReadOnly,
|
||||||
|
isVerkle: isVerkle,
|
||||||
bufferSize: config.DirtyCacheSize,
|
bufferSize: config.DirtyCacheSize,
|
||||||
config: config,
|
config: config,
|
||||||
diskdb: diskdb,
|
diskdb: diskdb,
|
||||||
|
|
@ -208,15 +211,6 @@ func New(diskdb ethdb.Database, config *Config) *Database {
|
||||||
return db
|
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
|
// 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
|
// 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
|
// 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,
|
StateHistory: historyLimit,
|
||||||
CleanCacheSize: 16 * 1024,
|
CleanCacheSize: 16 * 1024,
|
||||||
DirtyCacheSize: 16 * 1024,
|
DirtyCacheSize: 16 * 1024,
|
||||||
})
|
}, false)
|
||||||
obj = &tester{
|
obj = &tester{
|
||||||
db: db,
|
db: db,
|
||||||
preimages: make(map[common.Hash]common.Address),
|
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)
|
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 {
|
func (t *tester) lastHash() common.Hash {
|
||||||
if len(t.roots) == 0 {
|
if len(t.roots) == 0 {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -550,7 +550,7 @@ func TestJournal(t *testing.T) {
|
||||||
t.Errorf("Failed to journal, err: %v", err)
|
t.Errorf("Failed to journal, err: %v", err)
|
||||||
}
|
}
|
||||||
tester.db.Close()
|
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.
|
// Verify states including disk layer and all diff on top.
|
||||||
for i := 0; i < len(tester.roots); i++ {
|
for i := 0; i < len(tester.roots); i++ {
|
||||||
|
|
@ -588,7 +588,7 @@ func TestCorruptedJournal(t *testing.T) {
|
||||||
rawdb.WriteTrieJournal(tester.db.diskdb, blob)
|
rawdb.WriteTrieJournal(tester.db.diskdb, blob)
|
||||||
|
|
||||||
// Verify states, all not-yet-written states should be discarded
|
// 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++ {
|
for i := 0; i < len(tester.roots); i++ {
|
||||||
if tester.roots[i] == root {
|
if tester.roots[i] == root {
|
||||||
if err := tester.verifyState(root); err != nil {
|
if err := tester.verifyState(root); err != nil {
|
||||||
|
|
@ -625,7 +625,7 @@ func TestTailTruncateHistory(t *testing.T) {
|
||||||
defer tester.release()
|
defer tester.release()
|
||||||
|
|
||||||
tester.db.Close()
|
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()
|
head, err := tester.db.freezer.Ancients()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,9 @@ func (dl *diffLayer) parentLayer() layer {
|
||||||
return dl.parent
|
return dl.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// node retrieves the node with provided node information. It's the internal
|
// node implements the layer interface, retrieving the trie node blob with the
|
||||||
// version of Node function with additional accessed layer tracked. No error
|
// provided node information. No error will be returned if the node is not found.
|
||||||
// will be returned if node is not found.
|
func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
|
||||||
func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, error) {
|
|
||||||
// Hold the lock, ensure the parent won't be changed during the
|
// Hold the lock, ensure the parent won't be changed during the
|
||||||
// state accessing.
|
// state accessing.
|
||||||
dl.lock.RLock()
|
dl.lock.RLock()
|
||||||
|
|
@ -109,31 +108,14 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, dept
|
||||||
if ok {
|
if ok {
|
||||||
n, ok := subset[string(path)]
|
n, ok := subset[string(path)]
|
||||||
if ok {
|
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)
|
dirtyHitMeter.Mark(1)
|
||||||
dirtyNodeHitDepthHist.Update(int64(depth))
|
dirtyNodeHitDepthHist.Update(int64(depth))
|
||||||
dirtyReadMeter.Mark(int64(len(n.Blob)))
|
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
|
// Trie node unknown to this layer, resolve from parent
|
||||||
if diff, ok := dl.parent.(*diffLayer); ok {
|
return dl.parent.node(owner, path, depth+1)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update implements the layer interface, creating a new layer on top of the
|
// update implements the layer interface, creating a new layer on top of the
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
|
|
||||||
func emptyLayer() *diskLayer {
|
func emptyLayer() *diskLayer {
|
||||||
return &diskLayer{
|
return &diskLayer{
|
||||||
db: New(rawdb.NewMemoryDatabase(), nil),
|
db: New(rawdb.NewMemoryDatabase(), nil, false),
|
||||||
buffer: newNodeBuffer(DefaultBufferSize, nil, 0),
|
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) {
|
func benchmarkSearch(b *testing.B, depth int, total int) {
|
||||||
var (
|
var (
|
||||||
npath []byte
|
npath []byte
|
||||||
nhash common.Hash
|
|
||||||
nblob []byte
|
nblob []byte
|
||||||
)
|
)
|
||||||
// First, we set up 128 diff layers, with 3K items each
|
// 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 {
|
if npath == nil && depth == index {
|
||||||
npath = common.CopyBytes(path)
|
npath = common.CopyBytes(path)
|
||||||
nblob = common.CopyBytes(node.Blob)
|
nblob = common.CopyBytes(node.Blob)
|
||||||
nhash = node.Hash
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil)
|
return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil)
|
||||||
|
|
@ -92,7 +90,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
for i := 0; i < b.N; i++ {
|
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 {
|
if err != nil {
|
||||||
b.Fatal(err)
|
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 {
|
func (dl *diskLayer) rootHash() common.Hash {
|
||||||
return dl.root
|
return dl.root
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +68,7 @@ func (dl *diskLayer) stateID() uint64 {
|
||||||
return dl.id
|
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.
|
// below the disk.
|
||||||
func (dl *diskLayer) parentLayer() layer {
|
func (dl *diskLayer) parentLayer() layer {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -94,27 +94,25 @@ func (dl *diskLayer) markStale() {
|
||||||
dl.stale = true
|
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.
|
// 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()
|
dl.lock.RLock()
|
||||||
defer dl.lock.RUnlock()
|
defer dl.lock.RUnlock()
|
||||||
|
|
||||||
if dl.stale {
|
if dl.stale {
|
||||||
return nil, errSnapshotStale
|
return nil, common.Hash{}, nil, errSnapshotStale
|
||||||
}
|
}
|
||||||
// Try to retrieve the trie node from the not-yet-written
|
// Try to retrieve the trie node from the not-yet-written
|
||||||
// node buffer first. Note the buffer is lock free since
|
// node buffer first. Note the buffer is lock free since
|
||||||
// it's impossible to mutate the buffer before tagging the
|
// it's impossible to mutate the buffer before tagging the
|
||||||
// layer as stale.
|
// layer as stale.
|
||||||
n, err := dl.buffer.node(owner, path, hash)
|
n, found := dl.buffer.node(owner, path)
|
||||||
if err != nil {
|
if found {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n != nil {
|
|
||||||
dirtyHitMeter.Mark(1)
|
dirtyHitMeter.Mark(1)
|
||||||
dirtyReadMeter.Mark(int64(len(n.Blob)))
|
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)
|
dirtyMissMeter.Mark(1)
|
||||||
|
|
||||||
|
|
@ -125,14 +123,9 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b
|
||||||
h := newHasher()
|
h := newHasher()
|
||||||
defer h.release()
|
defer h.release()
|
||||||
|
|
||||||
got := h.hash(blob)
|
cleanHitMeter.Mark(1)
|
||||||
if got == hash {
|
cleanReadMeter.Mark(int64(len(blob)))
|
||||||
cleanHitMeter.Mark(1)
|
return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
cleanMissMeter.Mark(1)
|
cleanMissMeter.Mark(1)
|
||||||
}
|
}
|
||||||
|
|
@ -146,16 +139,11 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b
|
||||||
} else {
|
} else {
|
||||||
nBlob, nHash = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path)
|
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 {
|
if dl.cleans != nil && len(nBlob) > 0 {
|
||||||
dl.cleans.Set(key, nBlob)
|
dl.cleans.Set(key, nBlob)
|
||||||
cleanWriteMeter.Mark(int64(len(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
|
// update implements the layer interface, returning a new diff layer on top
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,7 @@
|
||||||
|
|
||||||
package pathdb
|
package pathdb
|
||||||
|
|
||||||
import (
|
import "errors"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// errDatabaseReadOnly is returned if the database is opened in read only mode
|
// 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
|
// errStateUnrecoverable is returned if state is required to be reverted to
|
||||||
// a destination without associated state history available.
|
// a destination without associated state history available.
|
||||||
errStateUnrecoverable = errors.New("state is unrecoverable")
|
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)
|
cleanFalseMeter = metrics.NewRegisteredMeter("pathdb/clean/false", nil)
|
||||||
dirtyFalseMeter = metrics.NewRegisteredMeter("pathdb/dirty/false", nil)
|
dirtyFalseMeter = metrics.NewRegisteredMeter("pathdb/dirty/false", nil)
|
||||||
diskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil)
|
diskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil)
|
||||||
|
diffFalseMeter = metrics.NewRegisteredMeter("pathdb/diff/false", nil)
|
||||||
|
|
||||||
commitTimeTimer = metrics.NewRegisteredTimer("pathdb/commit/time", nil)
|
commitTimeTimer = metrics.NewRegisteredTimer("pathdb/commit/time", nil)
|
||||||
commitNodesMeter = metrics.NewRegisteredMeter("pathdb/commit/nodes", 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.
|
// 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]
|
subset, ok := b.nodes[owner]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, false
|
||||||
}
|
}
|
||||||
n, ok := subset[string(path)]
|
n, ok := subset[string(path)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, false
|
||||||
}
|
}
|
||||||
if n.Hash != hash {
|
return n, true
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit merges the dirty nodes into the nodebuffer. This operation won't take
|
// 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