Merge pull request #91 from openrelayxyz/merge/geth-v1.13.2
Merge/geth v1.13.2
This commit is contained in:
commit
b549624096
@ -199,7 +199,6 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateDB.GetCode(contract), nil
|
return stateDB.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +211,6 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateDB.GetBalance(contract), nil
|
return stateDB.GetBalance(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +223,6 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateDB.GetNonce(contract), nil
|
return stateDB.GetNonce(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +235,6 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
val := stateDB.GetState(contract, key)
|
val := stateDB.GetState(contract, key)
|
||||||
return val[:], nil
|
return val[:], nil
|
||||||
}
|
}
|
||||||
@ -700,8 +696,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||||||
}
|
}
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
block.AddTxWithChain(b.blockchain, tx)
|
||||||
})
|
})
|
||||||
stateDB, _ := b.blockchain.State()
|
stateDB, err := b.blockchain.State()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
||||||
b.pendingReceipts = receipts[0]
|
b.pendingReceipts = receipts[0]
|
||||||
@ -821,11 +819,12 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
|||||||
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
block.OffsetTime(int64(adjustment.Seconds()))
|
block.OffsetTime(int64(adjustment.Seconds()))
|
||||||
})
|
})
|
||||||
stateDB, _ := b.blockchain.State()
|
stateDB, err := b.blockchain.State()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
@ -77,7 +78,9 @@ func (w *watcher) loop() {
|
|||||||
}
|
}
|
||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
if err := watcher.Add(w.ac.keydir); err != nil {
|
if err := watcher.Add(w.ac.keydir); err != nil {
|
||||||
logger.Warn("Failed to watch keystore folder", "err", err)
|
if !os.IsNotExist(err) {
|
||||||
|
logger.Warn("Failed to watch keystore folder", "err", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -51,7 +52,14 @@ type resolver interface {
|
|||||||
RequestENR(*enode.Node) (*enode.Node, error)
|
RequestENR(*enode.Node) (*enode.Node, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCrawler(input nodeSet, disc resolver, iters ...enode.Iterator) *crawler {
|
func newCrawler(input nodeSet, bootnodes []*enode.Node, disc resolver, iters ...enode.Iterator) (*crawler, error) {
|
||||||
|
if len(input) == 0 {
|
||||||
|
input.add(bootnodes...)
|
||||||
|
}
|
||||||
|
if len(input) == 0 {
|
||||||
|
return nil, errors.New("no input nodes to start crawling")
|
||||||
|
}
|
||||||
|
|
||||||
c := &crawler{
|
c := &crawler{
|
||||||
input: input,
|
input: input,
|
||||||
output: make(nodeSet, len(input)),
|
output: make(nodeSet, len(input)),
|
||||||
@ -67,7 +75,7 @@ func newCrawler(input nodeSet, disc resolver, iters ...enode.Iterator) *crawler
|
|||||||
for id, n := range input {
|
for id, n := range input {
|
||||||
c.output[id] = n
|
c.output[id] = n
|
||||||
}
|
}
|
||||||
return c
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
|
func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
|
||||||
|
@ -143,7 +143,7 @@ var discoveryNodeFlags = []cli.Flag{
|
|||||||
|
|
||||||
func discv4Ping(ctx *cli.Context) error {
|
func discv4Ping(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
disc := startV4(ctx)
|
disc, _ := startV4(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
@ -156,7 +156,7 @@ func discv4Ping(ctx *cli.Context) error {
|
|||||||
|
|
||||||
func discv4RequestRecord(ctx *cli.Context) error {
|
func discv4RequestRecord(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
disc := startV4(ctx)
|
disc, _ := startV4(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
|
|
||||||
respN, err := disc.RequestENR(n)
|
respN, err := disc.RequestENR(n)
|
||||||
@ -169,7 +169,7 @@ func discv4RequestRecord(ctx *cli.Context) error {
|
|||||||
|
|
||||||
func discv4Resolve(ctx *cli.Context) error {
|
func discv4Resolve(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
disc := startV4(ctx)
|
disc, _ := startV4(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
|
|
||||||
fmt.Println(disc.Resolve(n).String())
|
fmt.Println(disc.Resolve(n).String())
|
||||||
@ -196,10 +196,13 @@ func discv4ResolveJSON(ctx *cli.Context) error {
|
|||||||
nodeargs = append(nodeargs, n)
|
nodeargs = append(nodeargs, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the crawler.
|
disc, config := startV4(ctx)
|
||||||
disc := startV4(ctx)
|
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
c := newCrawler(inputSet, disc, enode.IterNodes(nodeargs))
|
|
||||||
|
c, err := newCrawler(inputSet, config.Bootnodes, disc, enode.IterNodes(nodeargs))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.revalidateInterval = 0
|
c.revalidateInterval = 0
|
||||||
output := c.run(0, 1)
|
output := c.run(0, 1)
|
||||||
writeNodesJSON(nodesFile, output)
|
writeNodesJSON(nodesFile, output)
|
||||||
@ -211,14 +214,18 @@ func discv4Crawl(ctx *cli.Context) error {
|
|||||||
return errors.New("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
nodesFile := ctx.Args().First()
|
nodesFile := ctx.Args().First()
|
||||||
var inputSet nodeSet
|
inputSet := make(nodeSet)
|
||||||
if common.FileExist(nodesFile) {
|
if common.FileExist(nodesFile) {
|
||||||
inputSet = loadNodesJSON(nodesFile)
|
inputSet = loadNodesJSON(nodesFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
disc := startV4(ctx)
|
disc, config := startV4(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
c := newCrawler(inputSet, disc, disc.RandomNodes())
|
|
||||||
|
c, err := newCrawler(inputSet, config.Bootnodes, disc, disc.RandomNodes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.revalidateInterval = 10 * time.Minute
|
c.revalidateInterval = 10 * time.Minute
|
||||||
output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name))
|
output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name))
|
||||||
writeNodesJSON(nodesFile, output)
|
writeNodesJSON(nodesFile, output)
|
||||||
@ -238,14 +245,14 @@ func discv4Test(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startV4 starts an ephemeral discovery V4 node.
|
// startV4 starts an ephemeral discovery V4 node.
|
||||||
func startV4(ctx *cli.Context) *discover.UDPv4 {
|
func startV4(ctx *cli.Context) (*discover.UDPv4, discover.Config) {
|
||||||
ln, config := makeDiscoveryConfig(ctx)
|
ln, config := makeDiscoveryConfig(ctx)
|
||||||
socket := listen(ctx, ln)
|
socket := listen(ctx, ln)
|
||||||
disc, err := discover.ListenV4(socket, ln, config)
|
disc, err := discover.ListenV4(socket, ln, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
return disc
|
return disc, config
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
|
func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
|
||||||
|
@ -81,7 +81,7 @@ var (
|
|||||||
|
|
||||||
func discv5Ping(ctx *cli.Context) error {
|
func discv5Ping(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
disc := startV5(ctx)
|
disc, _ := startV5(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
|
|
||||||
fmt.Println(disc.Ping(n))
|
fmt.Println(disc.Ping(n))
|
||||||
@ -90,7 +90,7 @@ func discv5Ping(ctx *cli.Context) error {
|
|||||||
|
|
||||||
func discv5Resolve(ctx *cli.Context) error {
|
func discv5Resolve(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
disc := startV5(ctx)
|
disc, _ := startV5(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
|
|
||||||
fmt.Println(disc.Resolve(n))
|
fmt.Println(disc.Resolve(n))
|
||||||
@ -102,14 +102,18 @@ func discv5Crawl(ctx *cli.Context) error {
|
|||||||
return errors.New("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
nodesFile := ctx.Args().First()
|
nodesFile := ctx.Args().First()
|
||||||
var inputSet nodeSet
|
inputSet := make(nodeSet)
|
||||||
if common.FileExist(nodesFile) {
|
if common.FileExist(nodesFile) {
|
||||||
inputSet = loadNodesJSON(nodesFile)
|
inputSet = loadNodesJSON(nodesFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
disc := startV5(ctx)
|
disc, config := startV5(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
c := newCrawler(inputSet, disc, disc.RandomNodes())
|
|
||||||
|
c, err := newCrawler(inputSet, config.Bootnodes, disc, disc.RandomNodes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.revalidateInterval = 10 * time.Minute
|
c.revalidateInterval = 10 * time.Minute
|
||||||
output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name))
|
output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name))
|
||||||
writeNodesJSON(nodesFile, output)
|
writeNodesJSON(nodesFile, output)
|
||||||
@ -127,7 +131,7 @@ func discv5Test(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func discv5Listen(ctx *cli.Context) error {
|
func discv5Listen(ctx *cli.Context) error {
|
||||||
disc := startV5(ctx)
|
disc, _ := startV5(ctx)
|
||||||
defer disc.Close()
|
defer disc.Close()
|
||||||
|
|
||||||
fmt.Println(disc.Self())
|
fmt.Println(disc.Self())
|
||||||
@ -135,12 +139,12 @@ func discv5Listen(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startV5 starts an ephemeral discovery v5 node.
|
// startV5 starts an ephemeral discovery v5 node.
|
||||||
func startV5(ctx *cli.Context) *discover.UDPv5 {
|
func startV5(ctx *cli.Context) (*discover.UDPv5, discover.Config) {
|
||||||
ln, config := makeDiscoveryConfig(ctx)
|
ln, config := makeDiscoveryConfig(ctx)
|
||||||
socket := listen(ctx, ln)
|
socket := listen(ctx, ln)
|
||||||
disc, err := discover.ListenV5(socket, ln, config)
|
disc, err := discover.ListenV5(socket, ln, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
return disc
|
return disc, config
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,7 @@ func setupGeth(stack *node.Node) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
backend.SetSynced()
|
||||||
|
|
||||||
_, err = backend.BlockChain().InsertChain(chain.blocks[1:])
|
_, err = backend.BlockChain().InsertChain(chain.blocks[1:])
|
||||||
return err
|
return err
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -41,10 +40,7 @@ func blockTestCmd(ctx *cli.Context) error {
|
|||||||
if len(ctx.Args().First()) == 0 {
|
if len(ctx.Args().First()) == 0 {
|
||||||
return errors.New("path-to-test argument required")
|
return errors.New("path-to-test argument required")
|
||||||
}
|
}
|
||||||
// Configure the go-ethereum logger
|
|
||||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
|
||||||
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
|
||||||
log.Root().SetHandler(glogger)
|
|
||||||
var tracer vm.EVMLogger
|
var tracer vm.EVMLogger
|
||||||
// Configure the EVM logger
|
// Configure the EVM logger
|
||||||
if ctx.Bool(MachineFlag.Name) {
|
if ctx.Bool(MachineFlag.Name) {
|
||||||
|
185
cmd/evm/main.go
185
cmd/evm/main.go
@ -23,107 +23,116 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
|
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DebugFlag = &cli.BoolFlag{
|
DebugFlag = &cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "output full trace logs",
|
Usage: "output full trace logs",
|
||||||
}
|
Category: flags.VMCategory,
|
||||||
MemProfileFlag = &cli.StringFlag{
|
|
||||||
Name: "memprofile",
|
|
||||||
Usage: "creates a memory profile at the given path",
|
|
||||||
}
|
|
||||||
CPUProfileFlag = &cli.StringFlag{
|
|
||||||
Name: "cpuprofile",
|
|
||||||
Usage: "creates a CPU profile at the given path",
|
|
||||||
}
|
}
|
||||||
StatDumpFlag = &cli.BoolFlag{
|
StatDumpFlag = &cli.BoolFlag{
|
||||||
Name: "statdump",
|
Name: "statdump",
|
||||||
Usage: "displays stack and heap memory information",
|
Usage: "displays stack and heap memory information",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
CodeFlag = &cli.StringFlag{
|
CodeFlag = &cli.StringFlag{
|
||||||
Name: "code",
|
Name: "code",
|
||||||
Usage: "EVM code",
|
Usage: "EVM code",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
CodeFileFlag = &cli.StringFlag{
|
CodeFileFlag = &cli.StringFlag{
|
||||||
Name: "codefile",
|
Name: "codefile",
|
||||||
Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
|
Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
GasFlag = &cli.Uint64Flag{
|
GasFlag = &cli.Uint64Flag{
|
||||||
Name: "gas",
|
Name: "gas",
|
||||||
Usage: "gas limit for the evm",
|
Usage: "gas limit for the evm",
|
||||||
Value: 10000000000,
|
Value: 10000000000,
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
PriceFlag = &flags.BigFlag{
|
PriceFlag = &flags.BigFlag{
|
||||||
Name: "price",
|
Name: "price",
|
||||||
Usage: "price set for the evm",
|
Usage: "price set for the evm",
|
||||||
Value: new(big.Int),
|
Value: new(big.Int),
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
ValueFlag = &flags.BigFlag{
|
ValueFlag = &flags.BigFlag{
|
||||||
Name: "value",
|
Name: "value",
|
||||||
Usage: "value set for the evm",
|
Usage: "value set for the evm",
|
||||||
Value: new(big.Int),
|
Value: new(big.Int),
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
DumpFlag = &cli.BoolFlag{
|
DumpFlag = &cli.BoolFlag{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "dumps the state after the run",
|
Usage: "dumps the state after the run",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
InputFlag = &cli.StringFlag{
|
InputFlag = &cli.StringFlag{
|
||||||
Name: "input",
|
Name: "input",
|
||||||
Usage: "input for the EVM",
|
Usage: "input for the EVM",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
InputFileFlag = &cli.StringFlag{
|
InputFileFlag = &cli.StringFlag{
|
||||||
Name: "inputfile",
|
Name: "inputfile",
|
||||||
Usage: "file containing input for the EVM",
|
Usage: "file containing input for the EVM",
|
||||||
}
|
Category: flags.VMCategory,
|
||||||
VerbosityFlag = &cli.IntFlag{
|
|
||||||
Name: "verbosity",
|
|
||||||
Usage: "sets the verbosity level",
|
|
||||||
}
|
}
|
||||||
BenchFlag = &cli.BoolFlag{
|
BenchFlag = &cli.BoolFlag{
|
||||||
Name: "bench",
|
Name: "bench",
|
||||||
Usage: "benchmark the execution",
|
Usage: "benchmark the execution",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
CreateFlag = &cli.BoolFlag{
|
CreateFlag = &cli.BoolFlag{
|
||||||
Name: "create",
|
Name: "create",
|
||||||
Usage: "indicates the action should be create rather than call",
|
Usage: "indicates the action should be create rather than call",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
GenesisFlag = &cli.StringFlag{
|
GenesisFlag = &cli.StringFlag{
|
||||||
Name: "prestate",
|
Name: "prestate",
|
||||||
Usage: "JSON file with prestate (genesis) config",
|
Usage: "JSON file with prestate (genesis) config",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
MachineFlag = &cli.BoolFlag{
|
MachineFlag = &cli.BoolFlag{
|
||||||
Name: "json",
|
Name: "json",
|
||||||
Usage: "output trace logs in machine readable format (json)",
|
Usage: "output trace logs in machine readable format (json)",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
SenderFlag = &cli.StringFlag{
|
SenderFlag = &cli.StringFlag{
|
||||||
Name: "sender",
|
Name: "sender",
|
||||||
Usage: "The transaction origin",
|
Usage: "The transaction origin",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
ReceiverFlag = &cli.StringFlag{
|
ReceiverFlag = &cli.StringFlag{
|
||||||
Name: "receiver",
|
Name: "receiver",
|
||||||
Usage: "The transaction receiver (execution context)",
|
Usage: "The transaction receiver (execution context)",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
DisableMemoryFlag = &cli.BoolFlag{
|
DisableMemoryFlag = &cli.BoolFlag{
|
||||||
Name: "nomemory",
|
Name: "nomemory",
|
||||||
Value: true,
|
Value: true,
|
||||||
Usage: "disable memory output",
|
Usage: "disable memory output",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
DisableStackFlag = &cli.BoolFlag{
|
DisableStackFlag = &cli.BoolFlag{
|
||||||
Name: "nostack",
|
Name: "nostack",
|
||||||
Usage: "disable stack output",
|
Usage: "disable stack output",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
DisableStorageFlag = &cli.BoolFlag{
|
DisableStorageFlag = &cli.BoolFlag{
|
||||||
Name: "nostorage",
|
Name: "nostorage",
|
||||||
Usage: "disable storage output",
|
Usage: "disable storage output",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
DisableReturnDataFlag = &cli.BoolFlag{
|
DisableReturnDataFlag = &cli.BoolFlag{
|
||||||
Name: "noreturndata",
|
Name: "noreturndata",
|
||||||
Value: true,
|
Value: true,
|
||||||
Usage: "enable return data output",
|
Usage: "enable return data output",
|
||||||
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,34 +192,38 @@ var blockBuilderCommand = &cli.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// vmFlags contains flags related to running the EVM.
|
||||||
|
var vmFlags = []cli.Flag{
|
||||||
|
CodeFlag,
|
||||||
|
CodeFileFlag,
|
||||||
|
CreateFlag,
|
||||||
|
GasFlag,
|
||||||
|
PriceFlag,
|
||||||
|
ValueFlag,
|
||||||
|
InputFlag,
|
||||||
|
InputFileFlag,
|
||||||
|
GenesisFlag,
|
||||||
|
SenderFlag,
|
||||||
|
ReceiverFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
// traceFlags contains flags that configure tracing output.
|
||||||
|
var traceFlags = []cli.Flag{
|
||||||
|
BenchFlag,
|
||||||
|
DebugFlag,
|
||||||
|
DumpFlag,
|
||||||
|
MachineFlag,
|
||||||
|
StatDumpFlag,
|
||||||
|
DisableMemoryFlag,
|
||||||
|
DisableStackFlag,
|
||||||
|
DisableStorageFlag,
|
||||||
|
DisableReturnDataFlag,
|
||||||
|
}
|
||||||
|
|
||||||
var app = flags.NewApp("the evm command line interface")
|
var app = flags.NewApp("the evm command line interface")
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = flags.Merge(vmFlags, traceFlags, debug.Flags)
|
||||||
BenchFlag,
|
|
||||||
CreateFlag,
|
|
||||||
DebugFlag,
|
|
||||||
VerbosityFlag,
|
|
||||||
CodeFlag,
|
|
||||||
CodeFileFlag,
|
|
||||||
GasFlag,
|
|
||||||
PriceFlag,
|
|
||||||
ValueFlag,
|
|
||||||
DumpFlag,
|
|
||||||
InputFlag,
|
|
||||||
InputFileFlag,
|
|
||||||
MemProfileFlag,
|
|
||||||
CPUProfileFlag,
|
|
||||||
StatDumpFlag,
|
|
||||||
GenesisFlag,
|
|
||||||
MachineFlag,
|
|
||||||
SenderFlag,
|
|
||||||
ReceiverFlag,
|
|
||||||
DisableMemoryFlag,
|
|
||||||
DisableStackFlag,
|
|
||||||
DisableStorageFlag,
|
|
||||||
DisableReturnDataFlag,
|
|
||||||
}
|
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
compileCommand,
|
compileCommand,
|
||||||
disasmCommand,
|
disasmCommand,
|
||||||
@ -221,6 +234,14 @@ func init() {
|
|||||||
transactionCommand,
|
transactionCommand,
|
||||||
blockBuilderCommand,
|
blockBuilderCommand,
|
||||||
}
|
}
|
||||||
|
app.Before = func(ctx *cli.Context) error {
|
||||||
|
flags.MigrateGlobalFlags(ctx)
|
||||||
|
return debug.Setup(ctx)
|
||||||
|
}
|
||||||
|
app.After = func(ctx *cli.Context) error {
|
||||||
|
debug.Exit()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
goruntime "runtime"
|
goruntime "runtime"
|
||||||
"runtime/pprof"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -34,12 +33,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||||
@ -52,6 +49,7 @@ var runCommand = &cli.Command{
|
|||||||
Usage: "run arbitrary evm binary",
|
Usage: "run arbitrary evm binary",
|
||||||
ArgsUsage: "<code>",
|
ArgsUsage: "<code>",
|
||||||
Description: `The run command runs arbitrary EVM code.`,
|
Description: `The run command runs arbitrary EVM code.`,
|
||||||
|
Flags: flags.Merge(vmFlags, traceFlags),
|
||||||
}
|
}
|
||||||
|
|
||||||
// readGenesis will read the given JSON format genesis file and return
|
// readGenesis will read the given JSON format genesis file and return
|
||||||
@ -109,9 +107,6 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) (output []by
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runCmd(ctx *cli.Context) error {
|
func runCmd(ctx *cli.Context) error {
|
||||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
|
||||||
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
|
||||||
log.Root().SetHandler(glogger)
|
|
||||||
logconfig := &logger.Config{
|
logconfig := &logger.Config{
|
||||||
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
||||||
DisableStack: ctx.Bool(DisableStackFlag.Name),
|
DisableStack: ctx.Bool(DisableStackFlag.Name),
|
||||||
@ -121,15 +116,14 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tracer vm.EVMLogger
|
tracer vm.EVMLogger
|
||||||
debugLogger *logger.StructLogger
|
debugLogger *logger.StructLogger
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
sender = common.BytesToAddress([]byte("sender"))
|
sender = common.BytesToAddress([]byte("sender"))
|
||||||
receiver = common.BytesToAddress([]byte("receiver"))
|
receiver = common.BytesToAddress([]byte("receiver"))
|
||||||
genesisConfig *core.Genesis
|
preimages = ctx.Bool(DumpFlag.Name)
|
||||||
preimages = ctx.Bool(DumpFlag.Name)
|
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
|
||||||
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
|
|
||||||
)
|
)
|
||||||
if ctx.Bool(MachineFlag.Name) {
|
if ctx.Bool(MachineFlag.Name) {
|
||||||
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
|
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
|
||||||
@ -139,30 +133,30 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
} else {
|
} else {
|
||||||
debugLogger = logger.NewStructLogger(logconfig)
|
debugLogger = logger.NewStructLogger(logconfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialGas := ctx.Uint64(GasFlag.Name)
|
||||||
|
genesisConfig := new(core.Genesis)
|
||||||
|
genesisConfig.GasLimit = initialGas
|
||||||
if ctx.String(GenesisFlag.Name) != "" {
|
if ctx.String(GenesisFlag.Name) != "" {
|
||||||
gen := readGenesis(ctx.String(GenesisFlag.Name))
|
genesisConfig = readGenesis(ctx.String(GenesisFlag.Name))
|
||||||
genesisConfig = gen
|
if genesisConfig.GasLimit != 0 {
|
||||||
db := rawdb.NewMemoryDatabase()
|
initialGas = genesisConfig.GasLimit
|
||||||
triedb := trie.NewDatabase(db, &trie.Config{
|
}
|
||||||
Preimages: preimages,
|
|
||||||
HashDB: hashdb.Defaults,
|
|
||||||
})
|
|
||||||
defer triedb.Close()
|
|
||||||
genesis := gen.MustCommit(db, triedb)
|
|
||||||
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
|
||||||
statedb, _ = state.New(genesis.Root(), sdb, nil)
|
|
||||||
chainConfig = gen.Config
|
|
||||||
} else {
|
} else {
|
||||||
db := rawdb.NewMemoryDatabase()
|
genesisConfig.Config = params.AllEthashProtocolChanges
|
||||||
triedb := trie.NewDatabase(db, &trie.Config{
|
|
||||||
Preimages: preimages,
|
|
||||||
HashDB: hashdb.Defaults,
|
|
||||||
})
|
|
||||||
defer triedb.Close()
|
|
||||||
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
|
||||||
statedb, _ = state.New(types.EmptyRootHash, sdb, nil)
|
|
||||||
genesisConfig = new(core.Genesis)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
triedb := trie.NewDatabase(db, &trie.Config{
|
||||||
|
Preimages: preimages,
|
||||||
|
HashDB: hashdb.Defaults,
|
||||||
|
})
|
||||||
|
defer triedb.Close()
|
||||||
|
genesis := genesisConfig.MustCommit(db, triedb)
|
||||||
|
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
||||||
|
statedb, _ = state.New(genesis.Root(), sdb, nil)
|
||||||
|
chainConfig = genesisConfig.Config
|
||||||
|
|
||||||
if ctx.String(SenderFlag.Name) != "" {
|
if ctx.String(SenderFlag.Name) != "" {
|
||||||
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
||||||
}
|
}
|
||||||
@ -216,10 +210,6 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
code = common.Hex2Bytes(bin)
|
code = common.Hex2Bytes(bin)
|
||||||
}
|
}
|
||||||
initialGas := ctx.Uint64(GasFlag.Name)
|
|
||||||
if genesisConfig.GasLimit != 0 {
|
|
||||||
initialGas = genesisConfig.GasLimit
|
|
||||||
}
|
|
||||||
runtimeConfig := runtime.Config{
|
runtimeConfig := runtime.Config{
|
||||||
Origin: sender,
|
Origin: sender,
|
||||||
State: statedb,
|
State: statedb,
|
||||||
@ -236,19 +226,6 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if cpuProfilePath := ctx.String(CPUProfileFlag.Name); cpuProfilePath != "" {
|
|
||||||
f, err := os.Create(cpuProfilePath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("could not create CPU profile: ", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err := pprof.StartCPUProfile(f); err != nil {
|
|
||||||
fmt.Println("could not start CPU profile: ", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer pprof.StopCPUProfile()
|
|
||||||
}
|
|
||||||
|
|
||||||
if chainConfig != nil {
|
if chainConfig != nil {
|
||||||
runtimeConfig.ChainConfig = chainConfig
|
runtimeConfig.ChainConfig = chainConfig
|
||||||
} else {
|
} else {
|
||||||
@ -296,19 +273,6 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
fmt.Println(string(statedb.Dump(nil)))
|
fmt.Println(string(statedb.Dump(nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if memProfilePath := ctx.String(MemProfileFlag.Name); memProfilePath != "" {
|
|
||||||
f, err := os.Create(memProfilePath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("could not create memory profile: ", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
|
||||||
fmt.Println("could not write memory profile: ", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Bool(DebugFlag.Name) {
|
if ctx.Bool(DebugFlag.Name) {
|
||||||
if debugLogger != nil {
|
if debugLogger != nil {
|
||||||
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -52,11 +51,6 @@ type StatetestResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stateTestCmd(ctx *cli.Context) error {
|
func stateTestCmd(ctx *cli.Context) error {
|
||||||
// Configure the go-ethereum logger
|
|
||||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
|
||||||
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
|
||||||
log.Root().SetHandler(glogger)
|
|
||||||
|
|
||||||
// Configure the EVM logger
|
// Configure the EVM logger
|
||||||
config := &logger.Config{
|
config := &logger.Config{
|
||||||
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
||||||
|
@ -50,8 +50,7 @@ var (
|
|||||||
ArgsUsage: "<genesisPath>",
|
ArgsUsage: "<genesisPath>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.CachePreimagesFlag,
|
utils.CachePreimagesFlag,
|
||||||
utils.StateSchemeFlag,
|
}, utils.DatabaseFlags),
|
||||||
}, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
The init command initializes a new genesis block and definition for the network.
|
The init command initializes a new genesis block and definition for the network.
|
||||||
This is a destructive action and changes the network in which you will be
|
This is a destructive action and changes the network in which you will be
|
||||||
@ -97,9 +96,8 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
|||||||
utils.MetricsInfluxDBOrganizationFlag,
|
utils.MetricsInfluxDBOrganizationFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
utils.TransactionHistoryFlag,
|
utils.TransactionHistoryFlag,
|
||||||
utils.StateSchemeFlag,
|
|
||||||
utils.StateHistoryFlag,
|
utils.StateHistoryFlag,
|
||||||
}, utils.DatabasePathFlags),
|
}, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||||
with several RLP-encoded blocks, or several files can be used.
|
with several RLP-encoded blocks, or several files can be used.
|
||||||
@ -115,8 +113,7 @@ processing will proceed even if an individual RLP-file import failure occurs.`,
|
|||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.StateSchemeFlag,
|
}, utils.DatabaseFlags),
|
||||||
}, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
Requires a first argument of the file to write to.
|
Requires a first argument of the file to write to.
|
||||||
Optional second and third arguments control the first and
|
Optional second and third arguments control the first and
|
||||||
@ -132,7 +129,7 @@ be gzipped.`,
|
|||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.DatabasePathFlags),
|
}, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
The import-preimages command imports hash preimages from an RLP encoded stream.
|
The import-preimages command imports hash preimages from an RLP encoded stream.
|
||||||
It's deprecated, please use "geth db import" instead.
|
It's deprecated, please use "geth db import" instead.
|
||||||
@ -146,7 +143,7 @@ It's deprecated, please use "geth db import" instead.
|
|||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.DatabasePathFlags),
|
}, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
The export-preimages command exports hash preimages to an RLP encoded stream.
|
The export-preimages command exports hash preimages to an RLP encoded stream.
|
||||||
It's deprecated, please use "geth db export" instead.
|
It's deprecated, please use "geth db export" instead.
|
||||||
@ -165,8 +162,7 @@ It's deprecated, please use "geth db export" instead.
|
|||||||
utils.IncludeIncompletesFlag,
|
utils.IncludeIncompletesFlag,
|
||||||
utils.StartKeyFlag,
|
utils.StartKeyFlag,
|
||||||
utils.DumpLimitFlag,
|
utils.DumpLimitFlag,
|
||||||
utils.StateSchemeFlag,
|
}, utils.DatabaseFlags),
|
||||||
}, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
This command dumps out the state for a given block (or latest, if none provided).
|
This command dumps out the state for a given block (or latest, if none provided).
|
||||||
`,
|
`,
|
||||||
@ -340,7 +336,8 @@ func exportChain(ctx *cli.Context) error {
|
|||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chain, _ := utils.MakeChain(ctx, stack, true)
|
chain, db := utils.MakeChain(ctx, stack, true)
|
||||||
|
defer db.Close()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -380,6 +377,7 @@ func importPreimages(ctx *cli.Context) error {
|
|||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
db := utils.MakeChainDatabase(ctx, stack, false)
|
db := utils.MakeChainDatabase(ctx, stack, false)
|
||||||
|
defer db.Close()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil {
|
if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil {
|
||||||
@ -398,6 +396,7 @@ func exportPreimages(ctx *cli.Context) error {
|
|||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
db := utils.MakeChainDatabase(ctx, stack, true)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer db.Close()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil {
|
if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil {
|
||||||
@ -409,6 +408,8 @@ func exportPreimages(ctx *cli.Context) error {
|
|||||||
|
|
||||||
func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
|
func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
|
||||||
db := utils.MakeChainDatabase(ctx, stack, true)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
var header *types.Header
|
var header *types.Header
|
||||||
if ctx.NArg() > 1 {
|
if ctx.NArg() > 1 {
|
||||||
return nil, nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg())
|
return nil, nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg())
|
||||||
|
@ -48,7 +48,7 @@ var (
|
|||||||
Name: "removedb",
|
Name: "removedb",
|
||||||
Usage: "Remove blockchain and state databases",
|
Usage: "Remove blockchain and state databases",
|
||||||
ArgsUsage: "",
|
ArgsUsage: "",
|
||||||
Flags: utils.DatabasePathFlags,
|
Flags: utils.DatabaseFlags,
|
||||||
Description: `
|
Description: `
|
||||||
Remove blockchain and state databases`,
|
Remove blockchain and state databases`,
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ Remove blockchain and state databases`,
|
|||||||
ArgsUsage: "<prefix> <start>",
|
ArgsUsage: "<prefix> <start>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Usage: "Inspect the storage size for each type of data in the database",
|
Usage: "Inspect the storage size for each type of data in the database",
|
||||||
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ Remove blockchain and state databases`,
|
|||||||
Action: checkStateContent,
|
Action: checkStateContent,
|
||||||
Name: "check-state-content",
|
Name: "check-state-content",
|
||||||
ArgsUsage: "<start (optional)>",
|
ArgsUsage: "<start (optional)>",
|
||||||
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Usage: "Verify that state data is cryptographically correct",
|
Usage: "Verify that state data is cryptographically correct",
|
||||||
Description: `This command iterates the entire database for 32-byte keys, looking for rlp-encoded trie nodes.
|
Description: `This command iterates the entire database for 32-byte keys, looking for rlp-encoded trie nodes.
|
||||||
For each trie node encountered, it checks that the key corresponds to the keccak256(value). If this is not true, this indicates
|
For each trie node encountered, it checks that the key corresponds to the keccak256(value). If this is not true, this indicates
|
||||||
@ -97,7 +97,7 @@ a data corruption.`,
|
|||||||
Usage: "Print leveldb statistics",
|
Usage: "Print leveldb statistics",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
}
|
}
|
||||||
dbCompactCmd = &cli.Command{
|
dbCompactCmd = &cli.Command{
|
||||||
Action: dbCompact,
|
Action: dbCompact,
|
||||||
@ -107,7 +107,7 @@ a data corruption.`,
|
|||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `This command performs a database compaction.
|
Description: `This command performs a database compaction.
|
||||||
WARNING: This operation may take a very long time to finish, and may cause database
|
WARNING: This operation may take a very long time to finish, and may cause database
|
||||||
corruption if it is aborted during execution'!`,
|
corruption if it is aborted during execution'!`,
|
||||||
@ -119,7 +119,7 @@ corruption if it is aborted during execution'!`,
|
|||||||
ArgsUsage: "<hex-encoded key>",
|
ArgsUsage: "<hex-encoded key>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: "This command looks up the specified database key from the database.",
|
Description: "This command looks up the specified database key from the database.",
|
||||||
}
|
}
|
||||||
dbDeleteCmd = &cli.Command{
|
dbDeleteCmd = &cli.Command{
|
||||||
@ -129,7 +129,7 @@ corruption if it is aborted during execution'!`,
|
|||||||
ArgsUsage: "<hex-encoded key>",
|
ArgsUsage: "<hex-encoded key>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `This command deletes the specified database key from the database.
|
Description: `This command deletes the specified database key from the database.
|
||||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
ArgsUsage: "<hex-encoded key> <hex-encoded value>",
|
ArgsUsage: "<hex-encoded key> <hex-encoded value>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `This command sets a given database key to the given value.
|
Description: `This command sets a given database key to the given value.
|
||||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||||
}
|
}
|
||||||
@ -151,8 +151,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
ArgsUsage: "<hex-encoded state root> <hex-encoded account hash> <hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>",
|
ArgsUsage: "<hex-encoded state root> <hex-encoded account hash> <hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.StateSchemeFlag,
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
|
||||||
Description: "This command looks up the specified database key from the database.",
|
Description: "This command looks up the specified database key from the database.",
|
||||||
}
|
}
|
||||||
dbDumpFreezerIndex = &cli.Command{
|
dbDumpFreezerIndex = &cli.Command{
|
||||||
@ -162,7 +161,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
ArgsUsage: "<freezer-type> <table-type> <start (int)> <end (int)>",
|
ArgsUsage: "<freezer-type> <table-type> <start (int)> <end (int)>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: "This command displays information about the freezer index.",
|
Description: "This command displays information about the freezer index.",
|
||||||
}
|
}
|
||||||
dbImportCmd = &cli.Command{
|
dbImportCmd = &cli.Command{
|
||||||
@ -172,7 +171,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
ArgsUsage: "<dumpfile> <start (optional)",
|
ArgsUsage: "<dumpfile> <start (optional)",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: "The import command imports the specific chain data from an RLP encoded stream.",
|
Description: "The import command imports the specific chain data from an RLP encoded stream.",
|
||||||
}
|
}
|
||||||
dbExportCmd = &cli.Command{
|
dbExportCmd = &cli.Command{
|
||||||
@ -182,7 +181,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
ArgsUsage: "<type> <dumpfile>",
|
ArgsUsage: "<type> <dumpfile>",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.",
|
Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.",
|
||||||
}
|
}
|
||||||
dbMetadataCmd = &cli.Command{
|
dbMetadataCmd = &cli.Command{
|
||||||
@ -191,7 +190,7 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
Usage: "Shows metadata about the chain status.",
|
Usage: "Shows metadata about the chain status.",
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: "Shows metadata about the chain status.",
|
Description: "Shows metadata about the chain status.",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -595,6 +594,7 @@ func importLDBdata(ctx *cli.Context) error {
|
|||||||
close(stop)
|
close(stop)
|
||||||
}()
|
}()
|
||||||
db := utils.MakeChainDatabase(ctx, stack, false)
|
db := utils.MakeChainDatabase(ctx, stack, false)
|
||||||
|
defer db.Close()
|
||||||
return utils.ImportLDBData(db, fName, int64(start), stop)
|
return utils.ImportLDBData(db, fName, int64(start), stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,6 +691,7 @@ func exportChaindata(ctx *cli.Context) error {
|
|||||||
close(stop)
|
close(stop)
|
||||||
}()
|
}()
|
||||||
db := utils.MakeChainDatabase(ctx, stack, true)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer db.Close()
|
||||||
return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop)
|
return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,6 +699,8 @@ func showMetaData(ctx *cli.Context) error {
|
|||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
db := utils.MakeChainDatabase(ctx, stack, true)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
ancients, err := db.Ancients()
|
ancients, err := db.Ancients()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
|
fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
|
||||||
|
@ -96,7 +96,6 @@ var (
|
|||||||
utils.SnapshotFlag,
|
utils.SnapshotFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
utils.TransactionHistoryFlag,
|
utils.TransactionHistoryFlag,
|
||||||
utils.StateSchemeFlag,
|
|
||||||
utils.StateHistoryFlag,
|
utils.StateHistoryFlag,
|
||||||
utils.LightServeFlag,
|
utils.LightServeFlag,
|
||||||
utils.LightIngressFlag,
|
utils.LightIngressFlag,
|
||||||
@ -152,7 +151,7 @@ var (
|
|||||||
utils.GpoMaxGasPriceFlag,
|
utils.GpoMaxGasPriceFlag,
|
||||||
utils.GpoIgnoreGasPriceFlag,
|
utils.GpoIgnoreGasPriceFlag,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
utils.HTTPEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
|
@ -51,7 +51,7 @@ var (
|
|||||||
Action: pruneState,
|
Action: pruneState,
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge([]cli.Flag{
|
||||||
utils.BloomFilterSizeFlag,
|
utils.BloomFilterSizeFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot prune-state <state-root>
|
geth snapshot prune-state <state-root>
|
||||||
will prune historical state data with the help of the state snapshot.
|
will prune historical state data with the help of the state snapshot.
|
||||||
@ -69,9 +69,7 @@ WARNING: it's only supported in hash mode(--state.scheme=hash)".
|
|||||||
Usage: "Recalculate state hash based on the snapshot for verification",
|
Usage: "Recalculate state hash based on the snapshot for verification",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: verifyState,
|
Action: verifyState,
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
utils.StateSchemeFlag,
|
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot verify-state <state-root>
|
geth snapshot verify-state <state-root>
|
||||||
will traverse the whole accounts and storages set based on the specified
|
will traverse the whole accounts and storages set based on the specified
|
||||||
@ -84,7 +82,7 @@ In other words, this command does the snapshot to trie conversion.
|
|||||||
Usage: "Check that there is no 'dangling' snap storage",
|
Usage: "Check that there is no 'dangling' snap storage",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: checkDanglingStorage,
|
Action: checkDanglingStorage,
|
||||||
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot check-dangling-storage <state-root> traverses the snap storage
|
geth snapshot check-dangling-storage <state-root> traverses the snap storage
|
||||||
data, and verifies that all snapshot storage data has a corresponding account.
|
data, and verifies that all snapshot storage data has a corresponding account.
|
||||||
@ -95,7 +93,7 @@ data, and verifies that all snapshot storage data has a corresponding account.
|
|||||||
Usage: "Check all snapshot layers for the a specific account",
|
Usage: "Check all snapshot layers for the a specific account",
|
||||||
ArgsUsage: "<address | hash>",
|
ArgsUsage: "<address | hash>",
|
||||||
Action: checkAccount,
|
Action: checkAccount,
|
||||||
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot inspect-account <address | hash> checks all snapshot layers and prints out
|
geth snapshot inspect-account <address | hash> checks all snapshot layers and prints out
|
||||||
information about the specified address.
|
information about the specified address.
|
||||||
@ -106,9 +104,7 @@ information about the specified address.
|
|||||||
Usage: "Traverse the state with given root hash and perform quick verification",
|
Usage: "Traverse the state with given root hash and perform quick verification",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: traverseState,
|
Action: traverseState,
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
utils.StateSchemeFlag,
|
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot traverse-state <state-root>
|
geth snapshot traverse-state <state-root>
|
||||||
will traverse the whole state from the given state root and will abort if any
|
will traverse the whole state from the given state root and will abort if any
|
||||||
@ -123,9 +119,7 @@ It's also usable without snapshot enabled.
|
|||||||
Usage: "Traverse the state with given root hash and perform detailed verification",
|
Usage: "Traverse the state with given root hash and perform detailed verification",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: traverseRawState,
|
Action: traverseRawState,
|
||||||
Flags: flags.Merge([]cli.Flag{
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
utils.StateSchemeFlag,
|
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
geth snapshot traverse-rawstate <state-root>
|
geth snapshot traverse-rawstate <state-root>
|
||||||
will traverse the whole state from the given root and will abort if any referenced
|
will traverse the whole state from the given root and will abort if any referenced
|
||||||
@ -146,8 +140,7 @@ It's also usable without snapshot enabled.
|
|||||||
utils.ExcludeStorageFlag,
|
utils.ExcludeStorageFlag,
|
||||||
utils.StartKeyFlag,
|
utils.StartKeyFlag,
|
||||||
utils.DumpLimitFlag,
|
utils.DumpLimitFlag,
|
||||||
utils.StateSchemeFlag,
|
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
|
||||||
Description: `
|
Description: `
|
||||||
This command is semantically equivalent to 'geth dump', but uses the snapshots
|
This command is semantically equivalent to 'geth dump', but uses the snapshots
|
||||||
as the backend data source, making this command a lot faster.
|
as the backend data source, making this command a lot faster.
|
||||||
@ -252,7 +245,9 @@ func checkDanglingStorage(ctx *cli.Context) error {
|
|||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
return snapshot.CheckDanglingStorage(utils.MakeChainDatabase(ctx, stack, true))
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer db.Close()
|
||||||
|
return snapshot.CheckDanglingStorage(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// traverseState is a helper function used for pruning verification.
|
// traverseState is a helper function used for pruning verification.
|
||||||
@ -332,6 +327,11 @@ func traverseState(ctx *cli.Context) error {
|
|||||||
storageIter := trie.NewIterator(storageIt)
|
storageIter := trie.NewIterator(storageIt)
|
||||||
for storageIter.Next() {
|
for storageIter.Next() {
|
||||||
slots += 1
|
slots += 1
|
||||||
|
|
||||||
|
if time.Since(lastReport) > time.Second*8 {
|
||||||
|
log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
lastReport = time.Now()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if storageIter.Err != nil {
|
if storageIter.Err != nil {
|
||||||
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
|
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
|
||||||
@ -486,6 +486,10 @@ func traverseRawState(ctx *cli.Context) error {
|
|||||||
if storageIter.Leaf() {
|
if storageIter.Leaf() {
|
||||||
slots += 1
|
slots += 1
|
||||||
}
|
}
|
||||||
|
if time.Since(lastReport) > time.Second*8 {
|
||||||
|
log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
lastReport = time.Now()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if storageIter.Error() != nil {
|
if storageIter.Error() != nil {
|
||||||
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
|
log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
|
||||||
|
@ -45,7 +45,7 @@ var (
|
|||||||
Usage: "verify the conversion of a MPT into a verkle tree",
|
Usage: "verify the conversion of a MPT into a verkle tree",
|
||||||
ArgsUsage: "<root>",
|
ArgsUsage: "<root>",
|
||||||
Action: verifyVerkle,
|
Action: verifyVerkle,
|
||||||
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth verkle verify <state-root>
|
geth verkle verify <state-root>
|
||||||
This command takes a root commitment and attempts to rebuild the tree.
|
This command takes a root commitment and attempts to rebuild the tree.
|
||||||
@ -56,7 +56,7 @@ This command takes a root commitment and attempts to rebuild the tree.
|
|||||||
Usage: "Dump a verkle tree to a DOT file",
|
Usage: "Dump a verkle tree to a DOT file",
|
||||||
ArgsUsage: "<root> <key1> [<key 2> ...]",
|
ArgsUsage: "<root> <key1> [<key 2> ...]",
|
||||||
Action: expandVerkle,
|
Action: expandVerkle,
|
||||||
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
geth verkle dump <state-root> <key 1> [<key 2> ...]
|
geth verkle dump <state-root> <key 1> [<key 2> ...]
|
||||||
This command will produce a dot file representing the tree, rooted at <root>.
|
This command will produce a dot file representing the tree, rooted at <root>.
|
||||||
@ -115,6 +115,7 @@ func verifyVerkle(ctx *cli.Context) error {
|
|||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer chaindb.Close()
|
||||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||||
if headBlock == nil {
|
if headBlock == nil {
|
||||||
log.Error("Failed to load head block")
|
log.Error("Failed to load head block")
|
||||||
@ -163,6 +164,7 @@ func expandVerkle(ctx *cli.Context) error {
|
|||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
defer chaindb.Close()
|
||||||
var (
|
var (
|
||||||
rootC common.Hash
|
rootC common.Hash
|
||||||
keylist [][]byte
|
keylist [][]byte
|
||||||
|
@ -969,18 +969,19 @@ var (
|
|||||||
// NetworkFlags is the flag group of all built-in supported networks.
|
// NetworkFlags is the flag group of all built-in supported networks.
|
||||||
NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...)
|
NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...)
|
||||||
|
|
||||||
// DatabasePathFlags is the flag group of all database path flags.
|
// DatabaseFlags is the flag group of all database flags.
|
||||||
DatabasePathFlags = []cli.Flag{
|
DatabaseFlags = []cli.Flag{
|
||||||
DataDirFlag,
|
DataDirFlag,
|
||||||
AncientFlag,
|
AncientFlag,
|
||||||
RemoteDBFlag,
|
RemoteDBFlag,
|
||||||
|
StateSchemeFlag,
|
||||||
HttpHeaderFlag,
|
HttpHeaderFlag,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if rawdb.PebbleEnabled {
|
if rawdb.PebbleEnabled {
|
||||||
DatabasePathFlags = append(DatabasePathFlags, DBEngineFlag)
|
DatabaseFlags = append(DatabaseFlags, DBEngineFlag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1006,7 +1007,7 @@ func MakeDataDir(ctx *cli.Context) string {
|
|||||||
|
|
||||||
// setNodeKey creates a node key from set command line flags, either loading it
|
// setNodeKey creates a node key from set command line flags, either loading it
|
||||||
// from a file or as a specified hex value. If neither flags were provided, this
|
// from a file or as a specified hex value. If neither flags were provided, this
|
||||||
// method returns nil and an emphemeral key is to be generated.
|
// method returns nil and an ephemeral key is to be generated.
|
||||||
func setNodeKey(ctx *cli.Context, cfg *p2p.Config) {
|
func setNodeKey(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
var (
|
var (
|
||||||
hex = ctx.String(NodeKeyHexFlag.Name)
|
hex = ctx.String(NodeKeyHexFlag.Name)
|
||||||
@ -1039,35 +1040,45 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
|||||||
|
|
||||||
// setBootstrapNodes creates a list of bootstrap nodes from the command line
|
// setBootstrapNodes creates a list of bootstrap nodes from the command line
|
||||||
// flags, reverting to pre-configured ones if none have been specified.
|
// flags, reverting to pre-configured ones if none have been specified.
|
||||||
|
// Priority order for bootnodes configuration:
|
||||||
|
//
|
||||||
|
// 1. --bootnodes flag
|
||||||
|
// 2. Config file
|
||||||
|
// 3. Network preset flags (e.g. --goerli)
|
||||||
|
// 4. default to mainnet nodes
|
||||||
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
urls := params.MainnetBootnodes
|
urls := params.MainnetBootnodes
|
||||||
switch {
|
if ctx.IsSet(BootnodesFlag.Name) {
|
||||||
case ctx.IsSet(BootnodesFlag.Name):
|
|
||||||
urls = SplitAndTrim(ctx.String(BootnodesFlag.Name))
|
urls = SplitAndTrim(ctx.String(BootnodesFlag.Name))
|
||||||
case ctx.Bool(HoleskyFlag.Name):
|
} else {
|
||||||
urls = params.HoleskyBootnodes
|
if cfg.BootstrapNodes != nil {
|
||||||
case ctx.Bool(SepoliaFlag.Name):
|
return // Already set by config file, don't apply defaults.
|
||||||
urls = params.SepoliaBootnodes
|
}
|
||||||
case ctx.Bool(GoerliFlag.Name):
|
switch {
|
||||||
urls = params.GoerliBootnodes
|
case ctx.Bool(HoleskyFlag.Name):
|
||||||
|
urls = params.HoleskyBootnodes
|
||||||
|
case ctx.Bool(SepoliaFlag.Name):
|
||||||
|
urls = params.SepoliaBootnodes
|
||||||
|
case ctx.Bool(GoerliFlag.Name):
|
||||||
|
urls = params.GoerliBootnodes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cfg.BootstrapNodes = mustParseBootnodes(urls)
|
||||||
|
}
|
||||||
|
|
||||||
// don't apply defaults if BootstrapNodes is already set
|
func mustParseBootnodes(urls []string) []*enode.Node {
|
||||||
if cfg.BootstrapNodes != nil {
|
nodes := make([]*enode.Node, 0, len(urls))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
|
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
if url != "" {
|
if url != "" {
|
||||||
node, err := enode.Parse(enode.ValidSchemes, url)
|
node, err := enode.Parse(enode.ValidSchemes, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
|
nodes = append(nodes, node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// setBootstrapNodesV5 creates a list of bootstrap nodes from the command line
|
// setBootstrapNodesV5 creates a list of bootstrap nodes from the command line
|
||||||
@ -2129,7 +2140,7 @@ func DialRPCWithHeaders(endpoint string, headers []string) (*rpc.Client, error)
|
|||||||
}
|
}
|
||||||
var opts []rpc.ClientOption
|
var opts []rpc.ClientOption
|
||||||
if len(headers) > 0 {
|
if len(headers) > 0 {
|
||||||
var customHeaders = make(http.Header)
|
customHeaders := make(http.Header)
|
||||||
for _, h := range headers {
|
for _, h := range headers {
|
||||||
kv := strings.Split(h, ":")
|
kv := strings.Split(h, ":")
|
||||||
if len(kv) != 2 {
|
if len(kv) != 2 {
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
package asm
|
package asm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
@ -30,7 +32,7 @@ import (
|
|||||||
// and holds the tokens for the program.
|
// and holds the tokens for the program.
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
tokens []token
|
tokens []token
|
||||||
binary []interface{}
|
out []byte
|
||||||
|
|
||||||
labels map[string]int
|
labels map[string]int
|
||||||
|
|
||||||
@ -50,12 +52,10 @@ func NewCompiler(debug bool) *Compiler {
|
|||||||
// Feed feeds tokens in to ch and are interpreted by
|
// Feed feeds tokens in to ch and are interpreted by
|
||||||
// the compiler.
|
// the compiler.
|
||||||
//
|
//
|
||||||
// feed is the first pass in the compile stage as it
|
// feed is the first pass in the compile stage as it collects the used labels in the
|
||||||
// collects the used labels in the program and keeps a
|
// program and keeps a program counter which is used to determine the locations of the
|
||||||
// program counter which is used to determine the locations
|
// jump dests. The labels can than be used in the second stage to push labels and
|
||||||
// of the jump dests. The labels can than be used in the
|
// determine the right position.
|
||||||
// second stage to push labels and determine the right
|
|
||||||
// position.
|
|
||||||
func (c *Compiler) Feed(ch <-chan token) {
|
func (c *Compiler) Feed(ch <-chan token) {
|
||||||
var prev token
|
var prev token
|
||||||
for i := range ch {
|
for i := range ch {
|
||||||
@ -79,7 +79,6 @@ func (c *Compiler) Feed(ch <-chan token) {
|
|||||||
c.pc++
|
c.pc++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.tokens = append(c.tokens, i)
|
c.tokens = append(c.tokens, i)
|
||||||
prev = i
|
prev = i
|
||||||
}
|
}
|
||||||
@ -88,12 +87,11 @@ func (c *Compiler) Feed(ch <-chan token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile compiles the current tokens and returns a
|
// Compile compiles the current tokens and returns a binary string that can be interpreted
|
||||||
// binary string that can be interpreted by the EVM
|
// by the EVM and an error if it failed.
|
||||||
// and an error if it failed.
|
|
||||||
//
|
//
|
||||||
// compile is the second stage in the compile phase
|
// compile is the second stage in the compile phase which compiles the tokens to EVM
|
||||||
// which compiles the tokens to EVM instructions.
|
// instructions.
|
||||||
func (c *Compiler) Compile() (string, []error) {
|
func (c *Compiler) Compile() (string, []error) {
|
||||||
var errors []error
|
var errors []error
|
||||||
// continue looping over the tokens until
|
// continue looping over the tokens until
|
||||||
@ -105,16 +103,8 @@ func (c *Compiler) Compile() (string, []error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// turn the binary to hex
|
// turn the binary to hex
|
||||||
var bin strings.Builder
|
h := hex.EncodeToString(c.out)
|
||||||
for _, v := range c.binary {
|
return h, errors
|
||||||
switch v := v.(type) {
|
|
||||||
case vm.OpCode:
|
|
||||||
bin.WriteString(fmt.Sprintf("%x", []byte{byte(v)}))
|
|
||||||
case []byte:
|
|
||||||
bin.WriteString(fmt.Sprintf("%x", v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bin.String(), errors
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// next returns the next token and increments the
|
// next returns the next token and increments the
|
||||||
@ -156,87 +146,114 @@ func (c *Compiler) compileLine() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileNumber compiles the number to bytes
|
// parseNumber compiles the number to bytes
|
||||||
func (c *Compiler) compileNumber(element token) {
|
func parseNumber(tok token) ([]byte, error) {
|
||||||
num := math.MustParseBig256(element.text).Bytes()
|
if tok.typ != number {
|
||||||
if len(num) == 0 {
|
panic("parseNumber of non-number token")
|
||||||
num = []byte{0}
|
|
||||||
}
|
}
|
||||||
c.pushBin(num)
|
num, ok := math.ParseBig256(tok.text)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid number")
|
||||||
|
}
|
||||||
|
bytes := num.Bytes()
|
||||||
|
if len(bytes) == 0 {
|
||||||
|
bytes = []byte{0}
|
||||||
|
}
|
||||||
|
return bytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileElement compiles the element (push & label or both)
|
// compileElement compiles the element (push & label or both)
|
||||||
// to a binary representation and may error if incorrect statements
|
// to a binary representation and may error if incorrect statements
|
||||||
// where fed.
|
// where fed.
|
||||||
func (c *Compiler) compileElement(element token) error {
|
func (c *Compiler) compileElement(element token) error {
|
||||||
// check for a jump. jumps must be read and compiled
|
switch {
|
||||||
// from right to left.
|
case isJump(element.text):
|
||||||
if isJump(element.text) {
|
return c.compileJump(element.text)
|
||||||
rvalue := c.next()
|
case isPush(element.text):
|
||||||
switch rvalue.typ {
|
return c.compilePush()
|
||||||
case number:
|
default:
|
||||||
// TODO figure out how to return the error properly
|
c.outputOpcode(toBinary(element.text))
|
||||||
c.compileNumber(rvalue)
|
|
||||||
case stringValue:
|
|
||||||
// strings are quoted, remove them.
|
|
||||||
c.pushBin(rvalue.text[1 : len(rvalue.text)-2])
|
|
||||||
case label:
|
|
||||||
c.pushBin(vm.PUSH4)
|
|
||||||
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
|
||||||
pos = append(make([]byte, 4-len(pos)), pos...)
|
|
||||||
c.pushBin(pos)
|
|
||||||
case lineEnd:
|
|
||||||
c.pos--
|
|
||||||
default:
|
|
||||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
|
||||||
}
|
|
||||||
// push the operation
|
|
||||||
c.pushBin(toBinary(element.text))
|
|
||||||
return nil
|
return nil
|
||||||
} else if isPush(element.text) {
|
|
||||||
// handle pushes. pushes are read from left to right.
|
|
||||||
var value []byte
|
|
||||||
|
|
||||||
rvalue := c.next()
|
|
||||||
switch rvalue.typ {
|
|
||||||
case number:
|
|
||||||
value = math.MustParseBig256(rvalue.text).Bytes()
|
|
||||||
if len(value) == 0 {
|
|
||||||
value = []byte{0}
|
|
||||||
}
|
|
||||||
case stringValue:
|
|
||||||
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
|
||||||
case label:
|
|
||||||
value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
|
||||||
value = append(make([]byte, 4-len(value)), value...)
|
|
||||||
default:
|
|
||||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(value) > 32 {
|
|
||||||
return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
|
||||||
c.pushBin(value)
|
|
||||||
} else {
|
|
||||||
c.pushBin(toBinary(element.text))
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compileJump(jumpType string) error {
|
||||||
|
rvalue := c.next()
|
||||||
|
switch rvalue.typ {
|
||||||
|
case number:
|
||||||
|
numBytes, err := parseNumber(rvalue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.outputBytes(numBytes)
|
||||||
|
|
||||||
|
case stringValue:
|
||||||
|
// strings are quoted, remove them.
|
||||||
|
str := rvalue.text[1 : len(rvalue.text)-2]
|
||||||
|
c.outputBytes([]byte(str))
|
||||||
|
|
||||||
|
case label:
|
||||||
|
c.outputOpcode(vm.PUSH4)
|
||||||
|
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||||
|
pos = append(make([]byte, 4-len(pos)), pos...)
|
||||||
|
c.outputBytes(pos)
|
||||||
|
|
||||||
|
case lineEnd:
|
||||||
|
// push without argument is supported, it just takes the destination from the stack.
|
||||||
|
c.pos--
|
||||||
|
|
||||||
|
default:
|
||||||
|
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||||
|
}
|
||||||
|
// push the operation
|
||||||
|
c.outputOpcode(toBinary(jumpType))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compilePush() error {
|
||||||
|
// handle pushes. pushes are read from left to right.
|
||||||
|
var value []byte
|
||||||
|
rvalue := c.next()
|
||||||
|
switch rvalue.typ {
|
||||||
|
case number:
|
||||||
|
value = math.MustParseBig256(rvalue.text).Bytes()
|
||||||
|
if len(value) == 0 {
|
||||||
|
value = []byte{0}
|
||||||
|
}
|
||||||
|
case stringValue:
|
||||||
|
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
||||||
|
case label:
|
||||||
|
value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||||
|
value = append(make([]byte, 4-len(value)), value...)
|
||||||
|
default:
|
||||||
|
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||||
|
}
|
||||||
|
if len(value) > 32 {
|
||||||
|
return fmt.Errorf("%d: string or number size > 32 bytes", rvalue.lineno+1)
|
||||||
|
}
|
||||||
|
c.outputOpcode(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
||||||
|
c.outputBytes(value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileLabel pushes a jumpdest to the binary slice.
|
// compileLabel pushes a jumpdest to the binary slice.
|
||||||
func (c *Compiler) compileLabel() {
|
func (c *Compiler) compileLabel() {
|
||||||
c.pushBin(vm.JUMPDEST)
|
c.outputOpcode(vm.JUMPDEST)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushBin pushes the value v to the binary stack.
|
func (c *Compiler) outputOpcode(op vm.OpCode) {
|
||||||
func (c *Compiler) pushBin(v interface{}) {
|
|
||||||
if c.debug {
|
if c.debug {
|
||||||
fmt.Printf("%d: %v\n", len(c.binary), v)
|
fmt.Printf("%d: %v\n", len(c.out), op)
|
||||||
}
|
}
|
||||||
c.binary = append(c.binary, v)
|
c.out = append(c.out, byte(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
// output pushes the value v to the binary stack.
|
||||||
|
func (c *Compiler) outputBytes(b []byte) {
|
||||||
|
if c.debug {
|
||||||
|
fmt.Printf("%d: %x\n", len(c.out), b)
|
||||||
|
}
|
||||||
|
c.out = append(c.out, b...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPush returns whether the string op is either any of
|
// isPush returns whether the string op is either any of
|
||||||
@ -263,13 +280,13 @@ type compileError struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (err compileError) Error() string {
|
func (err compileError) Error() string {
|
||||||
return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
return fmt.Sprintf("%d: syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileErr(c token, got, want string) error {
|
func compileErr(c token, got, want string) error {
|
||||||
return compileError{
|
return compileError{
|
||||||
got: got,
|
got: got,
|
||||||
want: want,
|
want: want,
|
||||||
lineno: c.lineno,
|
lineno: c.lineno + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,14 @@ func TestCompiler(t *testing.T) {
|
|||||||
`,
|
`,
|
||||||
output: "6300000006565b",
|
output: "6300000006565b",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: `
|
||||||
|
JUMP @label
|
||||||
|
label: ;; comment
|
||||||
|
ADD ;; comment
|
||||||
|
`,
|
||||||
|
output: "6300000006565b01",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
ch := Lex([]byte(test.input), false)
|
ch := Lex([]byte(test.input), false)
|
||||||
|
@ -72,6 +72,16 @@ func TestLexer(t *testing.T) {
|
|||||||
input: "@label123",
|
input: "@label123",
|
||||||
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
||||||
},
|
},
|
||||||
|
// comment after label
|
||||||
|
{
|
||||||
|
input: "@label123 ;; comment",
|
||||||
|
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
||||||
|
},
|
||||||
|
// comment after instruction
|
||||||
|
{
|
||||||
|
input: "push 3 ;; comment\nadd",
|
||||||
|
tokens: []token{{typ: lineStart}, {typ: element, text: "push"}, {typ: number, text: "3"}, {typ: lineEnd, text: "\n"}, {typ: lineStart, lineno: 1}, {typ: element, lineno: 1, text: "add"}, {typ: eof, lineno: 1}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -42,6 +42,8 @@ type token struct {
|
|||||||
// is able to parse and return.
|
// is able to parse and return.
|
||||||
type tokenType int
|
type tokenType int
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer -type tokenType
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eof tokenType = iota // end of file
|
eof tokenType = iota // end of file
|
||||||
lineStart // emitted when a line starts
|
lineStart // emitted when a line starts
|
||||||
@ -52,31 +54,13 @@ const (
|
|||||||
labelDef // label definition is emitted when a new label is found
|
labelDef // label definition is emitted when a new label is found
|
||||||
number // number is emitted when a number is found
|
number // number is emitted when a number is found
|
||||||
stringValue // stringValue is emitted when a string has been found
|
stringValue // stringValue is emitted when a string has been found
|
||||||
|
|
||||||
Numbers = "1234567890" // characters representing any decimal number
|
|
||||||
HexadecimalNumbers = Numbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
|
||||||
Alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// String implements stringer
|
const (
|
||||||
func (it tokenType) String() string {
|
decimalNumbers = "1234567890" // characters representing any decimal number
|
||||||
if int(it) > len(stringtokenTypes) {
|
hexNumbers = decimalNumbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
||||||
return "invalid"
|
alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
||||||
}
|
)
|
||||||
return stringtokenTypes[it]
|
|
||||||
}
|
|
||||||
|
|
||||||
var stringtokenTypes = []string{
|
|
||||||
eof: "EOF",
|
|
||||||
lineStart: "new line",
|
|
||||||
lineEnd: "end of line",
|
|
||||||
invalidStatement: "invalid statement",
|
|
||||||
element: "element",
|
|
||||||
label: "label",
|
|
||||||
labelDef: "label definition",
|
|
||||||
number: "number",
|
|
||||||
stringValue: "string",
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexer is the basic construct for parsing
|
// lexer is the basic construct for parsing
|
||||||
// source code and turning them in to tokens.
|
// source code and turning them in to tokens.
|
||||||
@ -200,7 +184,6 @@ func lexLine(l *lexer) stateFn {
|
|||||||
l.emit(lineEnd)
|
l.emit(lineEnd)
|
||||||
l.ignore()
|
l.ignore()
|
||||||
l.lineno++
|
l.lineno++
|
||||||
|
|
||||||
l.emit(lineStart)
|
l.emit(lineStart)
|
||||||
case r == ';' && l.peek() == ';':
|
case r == ';' && l.peek() == ';':
|
||||||
return lexComment
|
return lexComment
|
||||||
@ -225,6 +208,7 @@ func lexLine(l *lexer) stateFn {
|
|||||||
// of the line and discards the text.
|
// of the line and discards the text.
|
||||||
func lexComment(l *lexer) stateFn {
|
func lexComment(l *lexer) stateFn {
|
||||||
l.acceptRunUntil('\n')
|
l.acceptRunUntil('\n')
|
||||||
|
l.backup()
|
||||||
l.ignore()
|
l.ignore()
|
||||||
|
|
||||||
return lexLine
|
return lexLine
|
||||||
@ -234,7 +218,7 @@ func lexComment(l *lexer) stateFn {
|
|||||||
// the lex text state function to advance the parsing
|
// the lex text state function to advance the parsing
|
||||||
// process.
|
// process.
|
||||||
func lexLabel(l *lexer) stateFn {
|
func lexLabel(l *lexer) stateFn {
|
||||||
l.acceptRun(Alpha + "_" + Numbers)
|
l.acceptRun(alpha + "_" + decimalNumbers)
|
||||||
|
|
||||||
l.emit(label)
|
l.emit(label)
|
||||||
|
|
||||||
@ -253,9 +237,9 @@ func lexInsideString(l *lexer) stateFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func lexNumber(l *lexer) stateFn {
|
func lexNumber(l *lexer) stateFn {
|
||||||
acceptance := Numbers
|
acceptance := decimalNumbers
|
||||||
if l.accept("xX") {
|
if l.accept("xX") {
|
||||||
acceptance = HexadecimalNumbers
|
acceptance = hexNumbers
|
||||||
}
|
}
|
||||||
l.acceptRun(acceptance)
|
l.acceptRun(acceptance)
|
||||||
|
|
||||||
@ -265,7 +249,7 @@ func lexNumber(l *lexer) stateFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func lexElement(l *lexer) stateFn {
|
func lexElement(l *lexer) stateFn {
|
||||||
l.acceptRun(Alpha + "_" + Numbers)
|
l.acceptRun(alpha + "_" + decimalNumbers)
|
||||||
|
|
||||||
if l.peek() == ':' {
|
if l.peek() == ':' {
|
||||||
l.emit(labelDef)
|
l.emit(labelDef)
|
||||||
|
31
core/asm/tokentype_string.go
Normal file
31
core/asm/tokentype_string.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by "stringer -type tokenType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[eof-0]
|
||||||
|
_ = x[lineStart-1]
|
||||||
|
_ = x[lineEnd-2]
|
||||||
|
_ = x[invalidStatement-3]
|
||||||
|
_ = x[element-4]
|
||||||
|
_ = x[label-5]
|
||||||
|
_ = x[labelDef-6]
|
||||||
|
_ = x[number-7]
|
||||||
|
_ = x[stringValue-8]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _tokenType_name = "eoflineStartlineEndinvalidStatementelementlabellabelDefnumberstringValue"
|
||||||
|
|
||||||
|
var _tokenType_index = [...]uint8{0, 3, 12, 19, 35, 42, 47, 55, 61, 72}
|
||||||
|
|
||||||
|
func (i tokenType) String() string {
|
||||||
|
if i < 0 || i >= tokenType(len(_tokenType_index)-1) {
|
||||||
|
return "tokenType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _tokenType_name[_tokenType_index[i]:_tokenType_index[i+1]]
|
||||||
|
}
|
@ -337,17 +337,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||||||
if err := bc.loadLastState(); err != nil {
|
if err := bc.loadLastState(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Make sure the state associated with the block is available
|
// Make sure the state associated with the block is available, or log out
|
||||||
|
// if there is no available state, waiting for state sync.
|
||||||
head := bc.CurrentBlock()
|
head := bc.CurrentBlock()
|
||||||
if !bc.HasState(head.Root) {
|
if !bc.HasState(head.Root) {
|
||||||
if head.Number.Uint64() == 0 {
|
if head.Number.Uint64() == 0 {
|
||||||
// The genesis state is missing, which is only possible in the path-based
|
// The genesis state is missing, which is only possible in the path-based
|
||||||
// scheme. This situation occurs when the state syncer overwrites it.
|
// scheme. This situation occurs when the initial state sync is not finished
|
||||||
//
|
// yet, or the chain head is rewound below the pivot point. In both scenario,
|
||||||
// The solution is to reset the state to the genesis state. Although it may not
|
// there is no possible recovery approach except for rerunning a snap sync.
|
||||||
// match the sync target, the state healer will later address and correct any
|
// Do nothing here until the state syncer picks it up.
|
||||||
// inconsistencies.
|
log.Info("Genesis state is missing, wait state sync")
|
||||||
bc.resetState()
|
|
||||||
} else {
|
} else {
|
||||||
// Head state is missing, before the state recovery, find out the
|
// Head state is missing, before the state recovery, find out the
|
||||||
// disk layer point of snapshot(if it's enabled). Make sure the
|
// disk layer point of snapshot(if it's enabled). Make sure the
|
||||||
@ -630,28 +630,6 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resetState resets the persistent state to genesis state if it's not present.
|
|
||||||
func (bc *BlockChain) resetState() {
|
|
||||||
// Short circuit if the genesis state is already present.
|
|
||||||
root := bc.genesisBlock.Root()
|
|
||||||
if bc.HasState(root) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Reset the state database to empty for committing genesis state.
|
|
||||||
// Note, it should only happen in path-based scheme and Reset function
|
|
||||||
// is also only call-able in this mode.
|
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
|
||||||
if err := bc.triedb.Reset(types.EmptyRootHash); err != nil {
|
|
||||||
log.Crit("Failed to clean state", "err", err) // Shouldn't happen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Write genesis state into database.
|
|
||||||
if err := CommitGenesisState(bc.db, bc.triedb, bc.genesisBlock.Hash()); err != nil {
|
|
||||||
log.Crit("Failed to commit genesis state", "err", err)
|
|
||||||
}
|
|
||||||
log.Info("Reset state to genesis", "root", root)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
||||||
// that the rewind must pass the specified state root. This method is meant to be
|
// that the rewind must pass the specified state root. This method is meant to be
|
||||||
// used when rewinding with snapshots enabled to ensure that we go back further than
|
// used when rewinding with snapshots enabled to ensure that we go back further than
|
||||||
@ -687,7 +665,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||||||
if newHeadBlock == nil {
|
if newHeadBlock == nil {
|
||||||
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
|
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
|
||||||
newHeadBlock = bc.genesisBlock
|
newHeadBlock = bc.genesisBlock
|
||||||
bc.resetState()
|
|
||||||
} else {
|
} else {
|
||||||
// Block exists, keep rewinding until we find one with state,
|
// Block exists, keep rewinding until we find one with state,
|
||||||
// keeping rewinding until we exceed the optional threshold
|
// keeping rewinding until we exceed the optional threshold
|
||||||
@ -715,16 +692,14 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if beyondRoot || newHeadBlock.NumberU64() == 0 {
|
if beyondRoot || newHeadBlock.NumberU64() == 0 {
|
||||||
if newHeadBlock.NumberU64() == 0 {
|
if !bc.HasState(newHeadBlock.Root()) && bc.stateRecoverable(newHeadBlock.Root()) {
|
||||||
bc.resetState()
|
|
||||||
} else if !bc.HasState(newHeadBlock.Root()) {
|
|
||||||
// Rewind to a block with recoverable state. If the state is
|
// Rewind to a block with recoverable state. If the state is
|
||||||
// missing, run the state recovery here.
|
// missing, run the state recovery here.
|
||||||
if err := bc.triedb.Recover(newHeadBlock.Root()); err != nil {
|
if err := bc.triedb.Recover(newHeadBlock.Root()); err != nil {
|
||||||
log.Crit("Failed to rollback state", "err", err) // Shouldn't happen
|
log.Crit("Failed to rollback state", "err", err) // Shouldn't happen
|
||||||
}
|
}
|
||||||
|
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||||
}
|
}
|
||||||
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Debug("Skipping block with threshold state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "root", newHeadBlock.Root())
|
log.Debug("Skipping block with threshold state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "root", newHeadBlock.Root())
|
||||||
@ -739,6 +714,15 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||||||
// to low, so it's safe to update in-memory markers directly.
|
// to low, so it's safe to update in-memory markers directly.
|
||||||
bc.currentBlock.Store(newHeadBlock.Header())
|
bc.currentBlock.Store(newHeadBlock.Header())
|
||||||
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
||||||
|
|
||||||
|
// The head state is missing, which is only possible in the path-based
|
||||||
|
// scheme. This situation occurs when the chain head is rewound below
|
||||||
|
// the pivot point. In this scenario, there is no possible recovery
|
||||||
|
// approach except for rerunning a snap sync. Do nothing here until the
|
||||||
|
// state syncer picks it up.
|
||||||
|
if !bc.HasState(newHeadBlock.Root()) {
|
||||||
|
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number(), "hash", newHeadBlock.Hash())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Rewind the snap block in a simpleton way to the target head
|
// Rewind the snap block in a simpleton way to the target head
|
||||||
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
|
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
|
||||||
@ -838,7 +822,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
|
|||||||
// Reset the trie database with the fresh snap synced state.
|
// Reset the trie database with the fresh snap synced state.
|
||||||
root := block.Root()
|
root := block.Root()
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||||
if err := bc.triedb.Reset(root); err != nil {
|
if err := bc.triedb.Enable(root); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1402,6 +1386,11 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
|
|||||||
// writeBlockWithState writes block, metadata and corresponding state data to the
|
// writeBlockWithState writes block, metadata and corresponding state data to the
|
||||||
// database.
|
// database.
|
||||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error {
|
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error {
|
||||||
|
//begin PluGeth injection
|
||||||
|
var interval time.Duration
|
||||||
|
_ = pluginSetTrieFlushIntervalClone(interval) // this is being called here to engage a testing scenario
|
||||||
|
//end PluGeth injection
|
||||||
|
|
||||||
// Calculate the total difficulty of the block
|
// Calculate the total difficulty of the block
|
||||||
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
if ptd == nil {
|
if ptd == nil {
|
||||||
|
@ -630,13 +630,16 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
|
|||||||
request <- &Retrieval{Bit: bit, Sections: sections, Context: s.ctx}
|
request <- &Retrieval{Bit: bit, Sections: sections, Context: s.ctx}
|
||||||
|
|
||||||
result := <-request
|
result := <-request
|
||||||
|
|
||||||
|
// Deliver a result before s.Close() to avoid a deadlock
|
||||||
|
s.deliverSections(result.Bit, result.Sections, result.Bitsets)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
s.errLock.Lock()
|
s.errLock.Lock()
|
||||||
s.err = result.Error
|
s.err = result.Error
|
||||||
s.errLock.Unlock()
|
s.errLock.Unlock()
|
||||||
s.Close()
|
s.Close()
|
||||||
}
|
}
|
||||||
s.deliverSections(result.Bit, result.Sections, result.Bitsets)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,16 @@ func TestCreation(t *testing.T) {
|
|||||||
{1735372, 1677557088, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 0}}, // First Shanghai block
|
{1735372, 1677557088, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 0}}, // First Shanghai block
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Holesky test cases
|
||||||
|
{
|
||||||
|
params.HoleskyChainConfig,
|
||||||
|
core.DefaultHoleskyGenesisBlock().ToBlock(),
|
||||||
|
[]testcase{
|
||||||
|
{0, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris block
|
||||||
|
{123, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // First MergeNetsplit block
|
||||||
|
{123, 1696000704, ID{Hash: checksumToBytes(0xfd4f016b), Next: 0}}, // Last MergeNetsplit block
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
for j, ttt := range tt.cases {
|
for j, ttt := range tt.cases {
|
||||||
|
@ -587,10 +587,9 @@ func DefaultHoleskyGenesisBlock() *Genesis {
|
|||||||
return &Genesis{
|
return &Genesis{
|
||||||
Config: params.HoleskyChainConfig,
|
Config: params.HoleskyChainConfig,
|
||||||
Nonce: 0x1234,
|
Nonce: 0x1234,
|
||||||
ExtraData: hexutil.MustDecode("0x686f77206d7563682069732074686520666973683f"),
|
|
||||||
GasLimit: 0x17d7840,
|
GasLimit: 0x17d7840,
|
||||||
Difficulty: big.NewInt(0x01),
|
Difficulty: big.NewInt(0x01),
|
||||||
Timestamp: 1694786100,
|
Timestamp: 1695902100,
|
||||||
Alloc: decodePrealloc(holeskyAllocData),
|
Alloc: decodePrealloc(holeskyAllocData),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,3 +76,25 @@ func DeleteSkeletonHeader(db ethdb.KeyValueWriter, number uint64) {
|
|||||||
log.Crit("Failed to delete skeleton header", "err", err)
|
log.Crit("Failed to delete skeleton header", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateSyncUnknown = uint8(0) // flags the state snap sync is unknown
|
||||||
|
StateSyncRunning = uint8(1) // flags the state snap sync is not completed yet
|
||||||
|
StateSyncFinished = uint8(2) // flags the state snap sync is completed
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadSnapSyncStatusFlag retrieves the state snap sync status flag.
|
||||||
|
func ReadSnapSyncStatusFlag(db ethdb.KeyValueReader) uint8 {
|
||||||
|
blob, err := db.Get(snapSyncStatusFlagKey)
|
||||||
|
if err != nil || len(blob) != 1 {
|
||||||
|
return StateSyncUnknown
|
||||||
|
}
|
||||||
|
return blob[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteSnapSyncStatusFlag stores the state snap sync status flag into database.
|
||||||
|
func WriteSnapSyncStatusFlag(db ethdb.KeyValueWriter, flag uint8) {
|
||||||
|
if err := db.Put(snapSyncStatusFlagKey, []byte{flag}); err != nil {
|
||||||
|
log.Crit("Failed to store sync status flag", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -89,6 +89,16 @@ func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash)
|
|||||||
return h.hash(data) == hash
|
return h.hash(data) == hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExistsAccountTrieNode checks the presence of the account trie node with the
|
||||||
|
// specified node path, regardless of the node hash.
|
||||||
|
func ExistsAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool {
|
||||||
|
has, err := db.Has(accountTrieNodeKey(path))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
// WriteAccountTrieNode writes the provided account trie node into database.
|
// WriteAccountTrieNode writes the provided account trie node into database.
|
||||||
func WriteAccountTrieNode(db ethdb.KeyValueWriter, path []byte, node []byte) {
|
func WriteAccountTrieNode(db ethdb.KeyValueWriter, path []byte, node []byte) {
|
||||||
if err := db.Put(accountTrieNodeKey(path), node); err != nil {
|
if err := db.Put(accountTrieNodeKey(path), node); err != nil {
|
||||||
@ -127,6 +137,16 @@ func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path [
|
|||||||
return h.hash(data) == hash
|
return h.hash(data) == hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExistsStorageTrieNode checks the presence of the storage trie node with the
|
||||||
|
// specified account hash and node path, regardless of the node hash.
|
||||||
|
func ExistsStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool {
|
||||||
|
has, err := db.Has(storageTrieNodeKey(accountHash, path))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
// WriteStorageTrieNode writes the provided storage trie node into database.
|
// WriteStorageTrieNode writes the provided storage trie node into database.
|
||||||
func WriteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte, node []byte) {
|
func WriteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte, node []byte) {
|
||||||
if err := db.Put(storageTrieNodeKey(accountHash, path), node); err != nil {
|
if err := db.Put(storageTrieNodeKey(accountHash, path), node); err != nil {
|
||||||
|
@ -253,7 +253,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We are about to exit on error. Print database metdata beore exiting
|
// We are about to exit on error. Print database metadata before exiting
|
||||||
printChainMetadata(db)
|
printChainMetadata(db)
|
||||||
return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
|
return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
|
||||||
frozen-1, number, head)
|
frozen-1, number, head)
|
||||||
@ -555,7 +555,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||||||
lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
|
lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
|
||||||
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
|
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
|
||||||
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
|
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
|
||||||
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey,
|
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey,
|
||||||
} {
|
} {
|
||||||
if bytes.Equal(key, meta) {
|
if bytes.Equal(key, meta) {
|
||||||
metadata.Add(size)
|
metadata.Add(size)
|
||||||
|
@ -108,7 +108,11 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
|
|||||||
// Leveldb uses LOCK as the filelock filename. To prevent the
|
// Leveldb uses LOCK as the filelock filename. To prevent the
|
||||||
// name collision, we use FLOCK as the lock name.
|
// name collision, we use FLOCK as the lock name.
|
||||||
lock := flock.New(flockFile)
|
lock := flock.New(flockFile)
|
||||||
if locked, err := lock.TryLock(); err != nil {
|
tryLock := lock.TryLock
|
||||||
|
if readonly {
|
||||||
|
tryLock = lock.TryRLock
|
||||||
|
}
|
||||||
|
if locked, err := tryLock(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !locked {
|
} else if !locked {
|
||||||
return nil, errors.New("locking failed")
|
return nil, errors.New("locking failed")
|
||||||
|
@ -212,6 +212,9 @@ func (t *freezerTable) repair() error {
|
|||||||
}
|
}
|
||||||
// Ensure the index is a multiple of indexEntrySize bytes
|
// Ensure the index is a multiple of indexEntrySize bytes
|
||||||
if overflow := stat.Size() % indexEntrySize; overflow != 0 {
|
if overflow := stat.Size() % indexEntrySize; overflow != 0 {
|
||||||
|
if t.readonly {
|
||||||
|
return fmt.Errorf("index file(path: %s, name: %s) size is not a multiple of %d", t.path, t.name, indexEntrySize)
|
||||||
|
}
|
||||||
truncateFreezerFile(t.index, stat.Size()-overflow) // New file can't trigger this path
|
truncateFreezerFile(t.index, stat.Size()-overflow) // New file can't trigger this path
|
||||||
}
|
}
|
||||||
// Retrieve the file sizes and prepare for truncation
|
// Retrieve the file sizes and prepare for truncation
|
||||||
@ -270,6 +273,9 @@ func (t *freezerTable) repair() error {
|
|||||||
// Keep truncating both files until they come in sync
|
// Keep truncating both files until they come in sync
|
||||||
contentExp = int64(lastIndex.offset)
|
contentExp = int64(lastIndex.offset)
|
||||||
for contentExp != contentSize {
|
for contentExp != contentSize {
|
||||||
|
if t.readonly {
|
||||||
|
return fmt.Errorf("freezer table(path: %s, name: %s, num: %d) is corrupted", t.path, t.name, lastIndex.filenum)
|
||||||
|
}
|
||||||
verbose = true
|
verbose = true
|
||||||
// Truncate the head file to the last offset pointer
|
// Truncate the head file to the last offset pointer
|
||||||
if contentExp < contentSize {
|
if contentExp < contentSize {
|
||||||
|
@ -289,6 +289,57 @@ func TestFreezerReadonlyValidate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFreezerConcurrentReadonly(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tables := map[string]bool{"a": true}
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
f, err := NewFreezer(dir, "", false, 2049, tables)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can't open freezer", err)
|
||||||
|
}
|
||||||
|
var item = make([]byte, 1024)
|
||||||
|
batch := f.tables["a"].newBatch()
|
||||||
|
items := uint64(10)
|
||||||
|
for i := uint64(0); i < items; i++ {
|
||||||
|
require.NoError(t, batch.AppendRaw(i, item))
|
||||||
|
}
|
||||||
|
require.NoError(t, batch.commit())
|
||||||
|
if loaded := f.tables["a"].items.Load(); loaded != items {
|
||||||
|
t.Fatalf("unexpected number of items in table, want: %d, have: %d", items, loaded)
|
||||||
|
}
|
||||||
|
require.NoError(t, f.Close())
|
||||||
|
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
fs = make([]*Freezer, 5)
|
||||||
|
errs = make([]error, 5)
|
||||||
|
)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
f, err := NewFreezer(dir, "", true, 2049, tables)
|
||||||
|
if err == nil {
|
||||||
|
fs[i] = f
|
||||||
|
} else {
|
||||||
|
errs[i] = err
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
for i := range fs {
|
||||||
|
if err := errs[i]; err != nil {
|
||||||
|
t.Fatal("failed to open freezer", err)
|
||||||
|
}
|
||||||
|
require.NoError(t, fs[i].Close())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newFreezerForTesting(t *testing.T, tables map[string]bool) (*Freezer, string) {
|
func newFreezerForTesting(t *testing.T, tables map[string]bool) (*Freezer, string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
@ -91,6 +91,9 @@ var (
|
|||||||
// transitionStatusKey tracks the eth2 transition status.
|
// transitionStatusKey tracks the eth2 transition status.
|
||||||
transitionStatusKey = []byte("eth2-transition")
|
transitionStatusKey = []byte("eth2-transition")
|
||||||
|
|
||||||
|
// snapSyncStatusFlagKey flags that status of snap sync.
|
||||||
|
snapSyncStatusFlagKey = []byte("SnapSyncStatus")
|
||||||
|
|
||||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||||
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
||||||
|
@ -446,6 +446,10 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
|
|||||||
internal += time.Since(istart)
|
internal += time.Since(istart)
|
||||||
}
|
}
|
||||||
if iter.Err != nil {
|
if iter.Err != nil {
|
||||||
|
// Trie errors should never happen. Still, in case of a bug, expose the
|
||||||
|
// error here, as the outer code will presume errors are interrupts, not
|
||||||
|
// some deeper issues.
|
||||||
|
log.Error("State snapshotter failed to iterate trie", "err", err)
|
||||||
return false, nil, iter.Err
|
return false, nil, iter.Err
|
||||||
}
|
}
|
||||||
// Delete all stale snapshot states remaining
|
// Delete all stale snapshot states remaining
|
||||||
|
@ -355,7 +355,13 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Initialize the state with head block, or fallback to empty one in
|
||||||
|
// case the head state is not available(might occur when node is not
|
||||||
|
// fully synced).
|
||||||
state, err := p.chain.StateAt(head.Root)
|
state, err := p.chain.StateAt(head.Root)
|
||||||
|
if err != nil {
|
||||||
|
state, err = p.chain.StateAt(types.EmptyRootHash)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ type evictHeap struct {
|
|||||||
index map[common.Address]int // Indices into the heap for replacements
|
index map[common.Address]int // Indices into the heap for replacements
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPriceHeap creates a new heap of cheapets accounts in the blob pool to evict
|
// newPriceHeap creates a new heap of cheapest accounts in the blob pool to evict
|
||||||
// from in case of over saturation.
|
// from in case of over saturation.
|
||||||
func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index *map[common.Address][]*blobTxMeta) *evictHeap {
|
func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index *map[common.Address][]*blobTxMeta) *evictHeap {
|
||||||
heap := &evictHeap{
|
heap := &evictHeap{
|
||||||
|
@ -143,7 +143,7 @@ func (l *limbo) push(tx *types.Transaction, block uint64) error {
|
|||||||
return errors.New("already tracked blob transaction")
|
return errors.New("already tracked blob transaction")
|
||||||
}
|
}
|
||||||
if err := l.setAndIndex(tx, block); err != nil {
|
if err := l.setAndIndex(tx, block); err != nil {
|
||||||
log.Error("Failed to set and index liboed blobs", "tx", tx, "err", err)
|
log.Error("Failed to set and index limboed blobs", "tx", tx, "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -191,7 +191,7 @@ func (l *limbo) update(txhash common.Hash, block uint64) {
|
|||||||
log.Trace("Blob transaction unchanged in limbo", "tx", txhash, "block", block)
|
log.Trace("Blob transaction unchanged in limbo", "tx", txhash, "block", block)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Retrieve the old blobs from the data store and write tehm back with a new
|
// Retrieve the old blobs from the data store and write them back with a new
|
||||||
// block number. IF anything fails, there's not much to do, go on.
|
// block number. IF anything fails, there's not much to do, go on.
|
||||||
item, err := l.getAndDrop(id)
|
item, err := l.getAndDrop(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
var log2_1_125 = math.Log2(1.125)
|
var log2_1_125 = math.Log2(1.125)
|
||||||
|
|
||||||
// evictionPriority calculates the eviction priority based on the algorithm
|
// evictionPriority calculates the eviction priority based on the algorithm
|
||||||
// described in the BlobPool docs for a both fee components.
|
// described in the BlobPool docs for both fee components.
|
||||||
//
|
//
|
||||||
// This method takes about 8ns on a very recent laptop CPU, recalculating about
|
// This method takes about 8ns on a very recent laptop CPU, recalculating about
|
||||||
// 125 million transaction priority values per second.
|
// 125 million transaction priority values per second.
|
||||||
|
@ -52,6 +52,6 @@ var (
|
|||||||
ErrOversizedData = errors.New("oversized data")
|
ErrOversizedData = errors.New("oversized data")
|
||||||
|
|
||||||
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
||||||
// transaction. Future transactions should only be able to replace other future transactions.
|
// one. Future transactions should only be able to replace other future transactions.
|
||||||
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
||||||
)
|
)
|
||||||
|
@ -298,7 +298,20 @@ func (pool *LegacyPool) Init(gasTip *big.Int, head *types.Header, reserve txpool
|
|||||||
|
|
||||||
// Set the basic pool parameters
|
// Set the basic pool parameters
|
||||||
pool.gasTip.Store(gasTip)
|
pool.gasTip.Store(gasTip)
|
||||||
pool.reset(nil, head)
|
|
||||||
|
// Initialize the state with head block, or fallback to empty one in
|
||||||
|
// case the head state is not available(might occur when node is not
|
||||||
|
// fully synced).
|
||||||
|
statedb, err := pool.chain.StateAt(head.Root)
|
||||||
|
if err != nil {
|
||||||
|
statedb, err = pool.chain.StateAt(types.EmptyRootHash)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pool.currentHead.Store(head)
|
||||||
|
pool.currentState = statedb
|
||||||
|
pool.pendingNonces = newNoncer(statedb)
|
||||||
|
|
||||||
// Start the reorg loop early, so it can handle requests generated during
|
// Start the reorg loop early, so it can handle requests generated during
|
||||||
// journal loading.
|
// journal loading.
|
||||||
@ -406,7 +419,7 @@ func (pool *LegacyPool) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset implements txpool.SubPool, allowing the legacy pool's internal state to be
|
// Reset implements txpool.SubPool, allowing the legacy pool's internal state to be
|
||||||
// kept in sync with the main transacion pool's internal state.
|
// kept in sync with the main transaction pool's internal state.
|
||||||
func (pool *LegacyPool) Reset(oldHead, newHead *types.Header) {
|
func (pool *LegacyPool) Reset(oldHead, newHead *types.Header) {
|
||||||
wait := pool.requestReset(oldHead, newHead)
|
wait := pool.requestReset(oldHead, newHead)
|
||||||
<-wait
|
<-wait
|
||||||
@ -637,7 +650,7 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
|||||||
// pending or queued one, it overwrites the previous transaction if its price is higher.
|
// pending or queued one, it overwrites the previous transaction if its price is higher.
|
||||||
//
|
//
|
||||||
// If a newly added transaction is marked as local, its sending account will be
|
// If a newly added transaction is marked as local, its sending account will be
|
||||||
// be added to the allowlist, preventing any associated transaction from being dropped
|
// added to the allowlist, preventing any associated transaction from being dropped
|
||||||
// out of the pool due to pricing constraints.
|
// out of the pool due to pricing constraints.
|
||||||
func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
||||||
// If the transaction is already known, discard it
|
// If the transaction is already known, discard it
|
||||||
@ -901,7 +914,7 @@ func (pool *LegacyPool) promoteTx(addr common.Address, hash common.Hash, tx *typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addLocals enqueues a batch of transactions into the pool if they are valid, marking the
|
// addLocals enqueues a batch of transactions into the pool if they are valid, marking the
|
||||||
// senders as a local ones, ensuring they go around the local pricing constraints.
|
// senders as local ones, ensuring they go around the local pricing constraints.
|
||||||
//
|
//
|
||||||
// This method is used to add transactions from the RPC API and performs synchronous pool
|
// This method is used to add transactions from the RPC API and performs synchronous pool
|
||||||
// reorganization and event propagation.
|
// reorganization and event propagation.
|
||||||
@ -943,7 +956,7 @@ func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add enqueues a batch of transactions into the pool if they are valid. Depending
|
// Add enqueues a batch of transactions into the pool if they are valid. Depending
|
||||||
// on the local flag, full pricing contraints will or will not be applied.
|
// on the local flag, full pricing constraints will or will not be applied.
|
||||||
//
|
//
|
||||||
// If sync is set, the method will block until all internal maintenance related
|
// If sync is set, the method will block until all internal maintenance related
|
||||||
// to the add is finished. Only use this during tests for determinism!
|
// to the add is finished. Only use this during tests for determinism!
|
||||||
|
@ -70,7 +70,7 @@ type TxPool struct {
|
|||||||
reservations map[common.Address]SubPool // Map with the account to pool reservations
|
reservations map[common.Address]SubPool // Map with the account to pool reservations
|
||||||
reserveLock sync.Mutex // Lock protecting the account reservations
|
reserveLock sync.Mutex // Lock protecting the account reservations
|
||||||
|
|
||||||
subs event.SubscriptionScope // Subscription scope to unscubscribe all on shutdown
|
subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown
|
||||||
quit chan chan error // Quit channel to tear down the head updater
|
quit chan chan error // Quit channel to tear down the head updater
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,7 +404,7 @@ func (p *TxPool) Locals() []common.Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Status returns the known status (unknown/pending/queued) of a transaction
|
// Status returns the known status (unknown/pending/queued) of a transaction
|
||||||
// identified by their hashes.
|
// identified by its hash.
|
||||||
func (p *TxPool) Status(hash common.Hash) TxStatus {
|
func (p *TxPool) Status(hash common.Hash) TxStatus {
|
||||||
for _, subpool := range p.subpools {
|
for _, subpool := range p.subpools {
|
||||||
if status := subpool.Status(hash); status != TxStatusUnknown {
|
if status := subpool.Status(hash); status != TxStatusUnknown {
|
||||||
|
@ -114,7 +114,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||||||
if sidecar == nil {
|
if sidecar == nil {
|
||||||
return fmt.Errorf("missing sidecar in blob transaction")
|
return fmt.Errorf("missing sidecar in blob transaction")
|
||||||
}
|
}
|
||||||
// Ensure the number of items in the blob transaction and vairous side
|
// Ensure the number of items in the blob transaction and various side
|
||||||
// data match up before doing any expensive validations
|
// data match up before doing any expensive validations
|
||||||
hashes := tx.BlobHashes()
|
hashes := tx.BlobHashes()
|
||||||
if len(hashes) == 0 {
|
if len(hashes) == 0 {
|
||||||
@ -182,7 +182,7 @@ type ValidationOptionsWithState struct {
|
|||||||
// be rejected once the number of remaining slots reaches zero.
|
// be rejected once the number of remaining slots reaches zero.
|
||||||
UsedAndLeftSlots func(addr common.Address) (int, int)
|
UsedAndLeftSlots func(addr common.Address) (int, int)
|
||||||
|
|
||||||
// ExistingExpenditure is a mandatory callback to retrieve the cummulative
|
// ExistingExpenditure is a mandatory callback to retrieve the cumulative
|
||||||
// cost of the already pooled transactions to check for overdrafts.
|
// cost of the already pooled transactions to check for overdrafts.
|
||||||
ExistingExpenditure func(addr common.Address) *big.Int
|
ExistingExpenditure func(addr common.Address) *big.Int
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
|
|||||||
return fmt.Errorf("%w: balance %v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, spent, cost, new(big.Int).Sub(need, balance))
|
return fmt.Errorf("%w: balance %v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, spent, cost, new(big.Int).Sub(need, balance))
|
||||||
}
|
}
|
||||||
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
|
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
|
||||||
// overflow the number of permitted transactions from a single accoun
|
// overflow the number of permitted transactions from a single account
|
||||||
// (i.e. max cancellable via out-of-bound transaction).
|
// (i.e. max cancellable via out-of-bound transaction).
|
||||||
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
|
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
|
||||||
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
||||||
|
@ -251,6 +251,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||||||
size.SetBytes(interpreter.hasherBuf[:])
|
size.SetBytes(interpreter.hasherBuf[:])
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -267,6 +268,7 @@ func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||||||
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -204,7 +204,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
|
|||||||
return nil, nil, errors.New("header not found")
|
return nil, nil, errors.New("header not found")
|
||||||
}
|
}
|
||||||
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
|
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
|
||||||
return stateDb, header, err
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return stateDb, header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||||
@ -223,7 +226,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
|
|||||||
return nil, nil, errors.New("hash is not currently canonical")
|
return nil, nil, errors.New("hash is not currently canonical")
|
||||||
}
|
}
|
||||||
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
|
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
|
||||||
return stateDb, header, err
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return stateDb, header, nil
|
||||||
}
|
}
|
||||||
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
}
|
}
|
||||||
|
@ -387,7 +387,7 @@ func (s *Ethereum) shouldPreserve(header *types.Header) bool {
|
|||||||
// r5 A [X] F G
|
// r5 A [X] F G
|
||||||
// r6 [X]
|
// r6 [X]
|
||||||
//
|
//
|
||||||
// In the round5, the inturn signer E is offline, so the worst case
|
// In the round5, the in-turn signer E is offline, so the worst case
|
||||||
// is A, F and G sign the block of round5 and reject the block of opponents
|
// is A, F and G sign the block of round5 and reject the block of opponents
|
||||||
// and in the round6, the last available signer B is offline, the whole
|
// and in the round6, the last available signer B is offline, the whole
|
||||||
// network is stuck.
|
// network is stuck.
|
||||||
@ -474,7 +474,7 @@ func (s *Ethereum) Engine() consensus.Engine { return s.engine }
|
|||||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||||
func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader }
|
func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader }
|
||||||
func (s *Ethereum) Synced() bool { return s.handler.acceptTxs.Load() }
|
func (s *Ethereum) Synced() bool { return s.handler.synced.Load() }
|
||||||
func (s *Ethereum) SetSynced() { s.handler.enableSyncedFeatures() }
|
func (s *Ethereum) SetSynced() { s.handler.enableSyncedFeatures() }
|
||||||
func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning }
|
func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning }
|
||||||
func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
|
func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
|
||||||
|
@ -403,7 +403,9 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td, ttd *big.Int,
|
|||||||
// subsequent state reads, explicitly disable the trie database and state
|
// subsequent state reads, explicitly disable the trie database and state
|
||||||
// syncer is responsible to address and correct any state missing.
|
// syncer is responsible to address and correct any state missing.
|
||||||
if d.blockchain.TrieDB().Scheme() == rawdb.PathScheme {
|
if d.blockchain.TrieDB().Scheme() == rawdb.PathScheme {
|
||||||
d.blockchain.TrieDB().Reset(types.EmptyRootHash)
|
if err := d.blockchain.TrieDB().Disable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Snap sync uses the snapshot namespace to store potentially flaky data until
|
// Snap sync uses the snapshot namespace to store potentially flaky data until
|
||||||
// sync completely heals and finishes. Pause snapshot maintenance in the mean-
|
// sync completely heals and finishes. Pause snapshot maintenance in the mean-
|
||||||
@ -1280,41 +1282,13 @@ func (d *Downloader) fetchReceipts(from uint64, beaconMode bool) error {
|
|||||||
// keeps processing and scheduling them into the header chain and downloader's
|
// keeps processing and scheduling them into the header chain and downloader's
|
||||||
// queue until the stream ends or a failure occurs.
|
// queue until the stream ends or a failure occurs.
|
||||||
func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode bool) error {
|
func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode bool) error {
|
||||||
// Keep a count of uncertain headers to roll back
|
|
||||||
var (
|
var (
|
||||||
rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis)
|
mode = d.getMode()
|
||||||
rollbackErr error
|
gotHeaders = false // Wait for batches of headers to process
|
||||||
mode = d.getMode()
|
|
||||||
)
|
)
|
||||||
defer func() {
|
|
||||||
if rollback > 0 {
|
|
||||||
lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0
|
|
||||||
if mode != LightSync {
|
|
||||||
lastFastBlock = d.blockchain.CurrentSnapBlock().Number
|
|
||||||
lastBlock = d.blockchain.CurrentBlock().Number
|
|
||||||
}
|
|
||||||
if err := d.lightchain.SetHead(rollback - 1); err != nil { // -1 to target the parent of the first uncertain block
|
|
||||||
// We're already unwinding the stack, only print the error to make it more visible
|
|
||||||
log.Error("Failed to roll back chain segment", "head", rollback-1, "err", err)
|
|
||||||
}
|
|
||||||
curFastBlock, curBlock := common.Big0, common.Big0
|
|
||||||
if mode != LightSync {
|
|
||||||
curFastBlock = d.blockchain.CurrentSnapBlock().Number
|
|
||||||
curBlock = d.blockchain.CurrentBlock().Number
|
|
||||||
}
|
|
||||||
log.Warn("Rolled back chain segment",
|
|
||||||
"header", fmt.Sprintf("%d->%d", lastHeader, d.lightchain.CurrentHeader().Number),
|
|
||||||
"snap", fmt.Sprintf("%d->%d", lastFastBlock, curFastBlock),
|
|
||||||
"block", fmt.Sprintf("%d->%d", lastBlock, curBlock), "reason", rollbackErr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Wait for batches of headers to process
|
|
||||||
gotHeaders := false
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
rollbackErr = errCanceled
|
|
||||||
return errCanceled
|
return errCanceled
|
||||||
|
|
||||||
case task := <-d.headerProcCh:
|
case task := <-d.headerProcCh:
|
||||||
@ -1363,8 +1337,6 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disable any rollback and return
|
|
||||||
rollback = 0
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Otherwise split the chunk of headers into batches and process them
|
// Otherwise split the chunk of headers into batches and process them
|
||||||
@ -1375,7 +1347,6 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
|||||||
// Terminate if something failed in between processing chunks
|
// Terminate if something failed in between processing chunks
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
rollbackErr = errCanceled
|
|
||||||
return errCanceled
|
return errCanceled
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -1422,29 +1393,11 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
|||||||
}
|
}
|
||||||
if len(chunkHeaders) > 0 {
|
if len(chunkHeaders) > 0 {
|
||||||
if n, err := d.lightchain.InsertHeaderChain(chunkHeaders); err != nil {
|
if n, err := d.lightchain.InsertHeaderChain(chunkHeaders); err != nil {
|
||||||
rollbackErr = err
|
|
||||||
|
|
||||||
// If some headers were inserted, track them as uncertain
|
|
||||||
if mode == SnapSync && n > 0 && rollback == 0 {
|
|
||||||
rollback = chunkHeaders[0].Number.Uint64()
|
|
||||||
}
|
|
||||||
log.Warn("Invalid header encountered", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err)
|
log.Warn("Invalid header encountered", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err)
|
||||||
return fmt.Errorf("%w: %v", errInvalidChain, err)
|
return fmt.Errorf("%w: %v", errInvalidChain, err)
|
||||||
}
|
}
|
||||||
// All verifications passed, track all headers within the allowed limits
|
|
||||||
if mode == SnapSync {
|
|
||||||
head := chunkHeaders[len(chunkHeaders)-1].Number.Uint64()
|
|
||||||
if head-rollback > uint64(fsHeaderSafetyNet) {
|
|
||||||
rollback = head - uint64(fsHeaderSafetyNet)
|
|
||||||
} else {
|
|
||||||
rollback = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if len(rejected) != 0 {
|
if len(rejected) != 0 {
|
||||||
// Merge threshold reached, stop importing, but don't roll back
|
|
||||||
rollback = 0
|
|
||||||
|
|
||||||
log.Info("Legacy sync reached merge threshold", "number", rejected[0].Number, "hash", rejected[0].Hash(), "td", td, "ttd", ttd)
|
log.Info("Legacy sync reached merge threshold", "number", rejected[0].Number, "hash", rejected[0].Hash(), "td", td, "ttd", ttd)
|
||||||
return ErrMergeTransition
|
return ErrMergeTransition
|
||||||
}
|
}
|
||||||
@ -1455,7 +1408,6 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
|||||||
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
|
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
rollbackErr = errCanceled
|
|
||||||
return errCanceled
|
return errCanceled
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
}
|
}
|
||||||
@ -1463,7 +1415,6 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
|||||||
// Otherwise insert the headers for content retrieval
|
// Otherwise insert the headers for content retrieval
|
||||||
inserts := d.queue.Schedule(chunkHeaders, chunkHashes, origin)
|
inserts := d.queue.Schedule(chunkHeaders, chunkHashes, origin)
|
||||||
if len(inserts) != len(chunkHeaders) {
|
if len(inserts) != len(chunkHeaders) {
|
||||||
rollbackErr = fmt.Errorf("stale headers: len inserts %v len(chunk) %v", len(inserts), len(chunkHeaders))
|
|
||||||
return fmt.Errorf("%w: stale headers", errBadPeer)
|
return fmt.Errorf("%w: stale headers", errBadPeer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -878,86 +878,6 @@ func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
|
|||||||
assertOwnChain(t, tester, len(chain.blocks))
|
assertOwnChain(t, tester, len(chain.blocks))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that upon detecting an invalid header, the recent ones are rolled back
|
|
||||||
// for various failure scenarios. Afterwards a full sync is attempted to make
|
|
||||||
// sure no state was corrupted.
|
|
||||||
func TestInvalidHeaderRollback66Snap(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, SnapSync) }
|
|
||||||
func TestInvalidHeaderRollback67Snap(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH67, SnapSync) }
|
|
||||||
|
|
||||||
func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
|
|
||||||
tester := newTester(t)
|
|
||||||
defer tester.terminate()
|
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
|
||||||
targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks
|
|
||||||
chain := testChainBase.shorten(targetBlocks)
|
|
||||||
|
|
||||||
// Attempt to sync with an attacker that feeds junk during the fast sync phase.
|
|
||||||
// This should result in the last fsHeaderSafetyNet headers being rolled back.
|
|
||||||
missing := fsHeaderSafetyNet + MaxHeaderFetch + 1
|
|
||||||
|
|
||||||
fastAttacker := tester.newPeer("fast-attack", protocol, chain.blocks[1:])
|
|
||||||
fastAttacker.withholdHeaders[chain.blocks[missing].Hash()] = struct{}{}
|
|
||||||
|
|
||||||
if err := tester.sync("fast-attack", nil, mode); err == nil {
|
|
||||||
t.Fatalf("succeeded fast attacker synchronisation")
|
|
||||||
}
|
|
||||||
if head := tester.chain.CurrentHeader().Number.Int64(); int(head) > MaxHeaderFetch {
|
|
||||||
t.Errorf("rollback head mismatch: have %v, want at most %v", head, MaxHeaderFetch)
|
|
||||||
}
|
|
||||||
// Attempt to sync with an attacker that feeds junk during the block import phase.
|
|
||||||
// This should result in both the last fsHeaderSafetyNet number of headers being
|
|
||||||
// rolled back, and also the pivot point being reverted to a non-block status.
|
|
||||||
missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1
|
|
||||||
|
|
||||||
blockAttacker := tester.newPeer("block-attack", protocol, chain.blocks[1:])
|
|
||||||
fastAttacker.withholdHeaders[chain.blocks[missing].Hash()] = struct{}{} // Make sure the fast-attacker doesn't fill in
|
|
||||||
blockAttacker.withholdHeaders[chain.blocks[missing].Hash()] = struct{}{}
|
|
||||||
|
|
||||||
if err := tester.sync("block-attack", nil, mode); err == nil {
|
|
||||||
t.Fatalf("succeeded block attacker synchronisation")
|
|
||||||
}
|
|
||||||
if head := tester.chain.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch {
|
|
||||||
t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch)
|
|
||||||
}
|
|
||||||
if mode == SnapSync {
|
|
||||||
if head := tester.chain.CurrentBlock().Number.Uint64(); head != 0 {
|
|
||||||
t.Errorf("fast sync pivot block #%d not rolled back", head)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Attempt to sync with an attacker that withholds promised blocks after the
|
|
||||||
// fast sync pivot point. This could be a trial to leave the node with a bad
|
|
||||||
// but already imported pivot block.
|
|
||||||
withholdAttacker := tester.newPeer("withhold-attack", protocol, chain.blocks[1:])
|
|
||||||
|
|
||||||
tester.downloader.syncInitHook = func(uint64, uint64) {
|
|
||||||
for i := missing; i < len(chain.blocks); i++ {
|
|
||||||
withholdAttacker.withholdHeaders[chain.blocks[i].Hash()] = struct{}{}
|
|
||||||
}
|
|
||||||
tester.downloader.syncInitHook = nil
|
|
||||||
}
|
|
||||||
if err := tester.sync("withhold-attack", nil, mode); err == nil {
|
|
||||||
t.Fatalf("succeeded withholding attacker synchronisation")
|
|
||||||
}
|
|
||||||
if head := tester.chain.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch {
|
|
||||||
t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch)
|
|
||||||
}
|
|
||||||
if mode == SnapSync {
|
|
||||||
if head := tester.chain.CurrentBlock().Number.Uint64(); head != 0 {
|
|
||||||
t.Errorf("fast sync pivot block #%d not rolled back", head)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Synchronise with the valid peer and make sure sync succeeds. Since the last rollback
|
|
||||||
// should also disable fast syncing for this process, verify that we did a fresh full
|
|
||||||
// sync. Note, we can't assert anything about the receipts since we won't purge the
|
|
||||||
// database of them, hence we can't use assertOwnChain.
|
|
||||||
tester.newPeer("valid", protocol, chain.blocks[1:])
|
|
||||||
if err := tester.sync("valid", nil, mode); err != nil {
|
|
||||||
t.Fatalf("failed to synchronise blocks: %v", err)
|
|
||||||
}
|
|
||||||
assertOwnChain(t, tester, len(chain.blocks))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that a peer advertising a high TD doesn't get to stall the downloader
|
// Tests that a peer advertising a high TD doesn't get to stall the downloader
|
||||||
// afterwards by not sending any useful hashes.
|
// afterwards by not sending any useful hashes.
|
||||||
func TestHighTDStarvationAttack66Full(t *testing.T) {
|
func TestHighTDStarvationAttack66Full(t *testing.T) {
|
||||||
|
@ -423,7 +423,7 @@ func (s *skeleton) sync(head *types.Header) (*types.Header, error) {
|
|||||||
for _, peer := range s.peers.AllPeers() {
|
for _, peer := range s.peers.AllPeers() {
|
||||||
s.idles[peer.id] = peer
|
s.idles[peer.id] = peer
|
||||||
}
|
}
|
||||||
// Nofity any tester listening for startup events
|
// Notify any tester listening for startup events
|
||||||
if s.syncStarting != nil {
|
if s.syncStarting != nil {
|
||||||
s.syncStarting()
|
s.syncStarting()
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -53,6 +53,9 @@ const (
|
|||||||
// re-request them.
|
// re-request them.
|
||||||
maxTxUnderpricedSetSize = 32768
|
maxTxUnderpricedSetSize = 32768
|
||||||
|
|
||||||
|
// maxTxUnderpricedTimeout is the max time a transaction should be stuck in the underpriced set.
|
||||||
|
maxTxUnderpricedTimeout = int64(5 * time.Minute)
|
||||||
|
|
||||||
// txArriveTimeout is the time allowance before an announced transaction is
|
// txArriveTimeout is the time allowance before an announced transaction is
|
||||||
// explicitly requested.
|
// explicitly requested.
|
||||||
txArriveTimeout = 500 * time.Millisecond
|
txArriveTimeout = 500 * time.Millisecond
|
||||||
@ -148,7 +151,7 @@ type TxFetcher struct {
|
|||||||
drop chan *txDrop
|
drop chan *txDrop
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
|
|
||||||
underpriced mapset.Set[common.Hash] // Transactions discarded as too cheap (don't re-fetch)
|
underpriced *lru.Cache[common.Hash, int64] // Transactions discarded as too cheap (don't re-fetch)
|
||||||
|
|
||||||
// Stage 1: Waiting lists for newly discovered transactions that might be
|
// Stage 1: Waiting lists for newly discovered transactions that might be
|
||||||
// broadcast without needing explicit request/reply round trips.
|
// broadcast without needing explicit request/reply round trips.
|
||||||
@ -202,7 +205,7 @@ func NewTxFetcherForTests(
|
|||||||
fetching: make(map[common.Hash]string),
|
fetching: make(map[common.Hash]string),
|
||||||
requests: make(map[string]*txRequest),
|
requests: make(map[string]*txRequest),
|
||||||
alternates: make(map[common.Hash]map[string]struct{}),
|
alternates: make(map[common.Hash]map[string]struct{}),
|
||||||
underpriced: mapset.NewSet[common.Hash](),
|
underpriced: lru.NewCache[common.Hash, int64](maxTxUnderpricedSetSize),
|
||||||
hasTx: hasTx,
|
hasTx: hasTx,
|
||||||
addTxs: addTxs,
|
addTxs: addTxs,
|
||||||
fetchTxs: fetchTxs,
|
fetchTxs: fetchTxs,
|
||||||
@ -223,17 +226,16 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
|
|||||||
// still valuable to check here because it runs concurrent to the internal
|
// still valuable to check here because it runs concurrent to the internal
|
||||||
// loop, so anything caught here is time saved internally.
|
// loop, so anything caught here is time saved internally.
|
||||||
var (
|
var (
|
||||||
unknowns = make([]common.Hash, 0, len(hashes))
|
unknowns = make([]common.Hash, 0, len(hashes))
|
||||||
duplicate, underpriced int64
|
duplicate int64
|
||||||
|
underpriced int64
|
||||||
)
|
)
|
||||||
for _, hash := range hashes {
|
for _, hash := range hashes {
|
||||||
switch {
|
switch {
|
||||||
case f.hasTx(hash):
|
case f.hasTx(hash):
|
||||||
duplicate++
|
duplicate++
|
||||||
|
case f.isKnownUnderpriced(hash):
|
||||||
case f.underpriced.Contains(hash):
|
|
||||||
underpriced++
|
underpriced++
|
||||||
|
|
||||||
default:
|
default:
|
||||||
unknowns = append(unknowns, hash)
|
unknowns = append(unknowns, hash)
|
||||||
}
|
}
|
||||||
@ -245,10 +247,7 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
|
|||||||
if len(unknowns) == 0 {
|
if len(unknowns) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
announce := &txAnnounce{
|
announce := &txAnnounce{origin: peer, hashes: unknowns}
|
||||||
origin: peer,
|
|
||||||
hashes: unknowns,
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case f.notify <- announce:
|
case f.notify <- announce:
|
||||||
return nil
|
return nil
|
||||||
@ -257,6 +256,16 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isKnownUnderpriced reports whether a transaction hash was recently found to be underpriced.
|
||||||
|
func (f *TxFetcher) isKnownUnderpriced(hash common.Hash) bool {
|
||||||
|
prevTime, ok := f.underpriced.Peek(hash)
|
||||||
|
if ok && prevTime+maxTxUnderpricedTimeout < time.Now().Unix() {
|
||||||
|
f.underpriced.Remove(hash)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// Enqueue imports a batch of received transaction into the transaction pool
|
// Enqueue imports a batch of received transaction into the transaction pool
|
||||||
// and the fetcher. This method may be called by both transaction broadcasts and
|
// and the fetcher. This method may be called by both transaction broadcasts and
|
||||||
// direct request replies. The differentiation is important so the fetcher can
|
// direct request replies. The differentiation is important so the fetcher can
|
||||||
@ -300,10 +309,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
|||||||
// Avoid re-request this transaction when we receive another
|
// Avoid re-request this transaction when we receive another
|
||||||
// announcement.
|
// announcement.
|
||||||
if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) {
|
if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) {
|
||||||
for f.underpriced.Cardinality() >= maxTxUnderpricedSetSize {
|
f.underpriced.Add(batch[j].Hash(), batch[j].Time().Unix())
|
||||||
f.underpriced.Pop()
|
|
||||||
}
|
|
||||||
f.underpriced.Add(batch[j].Hash())
|
|
||||||
}
|
}
|
||||||
// Track a few interesting failure types
|
// Track a few interesting failure types
|
||||||
switch {
|
switch {
|
||||||
|
@ -1509,8 +1509,8 @@ func testTransactionFetcher(t *testing.T, tt txFetcherTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case isUnderpriced:
|
case isUnderpriced:
|
||||||
if fetcher.underpriced.Cardinality() != int(step) {
|
if fetcher.underpriced.Len() != int(step) {
|
||||||
t.Errorf("step %d: underpriced set size mismatch: have %d, want %d", i, fetcher.underpriced.Cardinality(), step)
|
t.Errorf("step %d: underpriced set size mismatch: have %d, want %d", i, fetcher.underpriced.Len(), step)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -100,8 +100,8 @@ type handler struct {
|
|||||||
networkID uint64
|
networkID uint64
|
||||||
forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node
|
forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node
|
||||||
|
|
||||||
snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks)
|
snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks)
|
||||||
acceptTxs atomic.Bool // Flag whether we're considered synchronised (enables transaction processing)
|
synced atomic.Bool // Flag whether we're considered synchronised (enables transaction processing)
|
||||||
|
|
||||||
database ethdb.Database
|
database ethdb.Database
|
||||||
txpool txPool
|
txpool txPool
|
||||||
@ -163,32 +163,24 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||||||
fullBlock, snapBlock := h.chain.CurrentBlock(), h.chain.CurrentSnapBlock()
|
fullBlock, snapBlock := h.chain.CurrentBlock(), h.chain.CurrentSnapBlock()
|
||||||
if fullBlock.Number.Uint64() == 0 && snapBlock.Number.Uint64() > 0 {
|
if fullBlock.Number.Uint64() == 0 && snapBlock.Number.Uint64() > 0 {
|
||||||
h.snapSync.Store(true)
|
h.snapSync.Store(true)
|
||||||
log.Warn("Switch sync mode from full sync to snap sync")
|
log.Warn("Switch sync mode from full sync to snap sync", "reason", "snap sync incomplete")
|
||||||
|
} else if !h.chain.HasState(fullBlock.Root) {
|
||||||
|
h.snapSync.Store(true)
|
||||||
|
log.Warn("Switch sync mode from full sync to snap sync", "reason", "head state missing")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if h.chain.CurrentBlock().Number.Uint64() > 0 {
|
head := h.chain.CurrentBlock()
|
||||||
|
if head.Number.Uint64() > 0 && h.chain.HasState(head.Root) {
|
||||||
// Print warning log if database is not empty to run snap sync.
|
// Print warning log if database is not empty to run snap sync.
|
||||||
log.Warn("Switch sync mode from snap sync to full sync")
|
log.Warn("Switch sync mode from snap sync to full sync", "reason", "snap sync complete")
|
||||||
} else {
|
} else {
|
||||||
// If snap sync was requested and our database is empty, grant it
|
// If snap sync was requested and our database is empty, grant it
|
||||||
h.snapSync.Store(true)
|
h.snapSync.Store(true)
|
||||||
|
log.Info("Enabled snap sync", "head", head.Number, "hash", head.Hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If sync succeeds, pass a callback to potentially disable snap sync mode
|
|
||||||
// and enable transaction propagation.
|
|
||||||
success := func() {
|
|
||||||
// If we were running snap sync and it finished, disable doing another
|
|
||||||
// round on next sync cycle
|
|
||||||
if h.snapSync.Load() {
|
|
||||||
log.Info("Snap sync complete, auto disabling")
|
|
||||||
h.snapSync.Store(false)
|
|
||||||
}
|
|
||||||
// If we've successfully finished a sync cycle, accept transactions from
|
|
||||||
// the network
|
|
||||||
h.enableSyncedFeatures()
|
|
||||||
}
|
|
||||||
// Construct the downloader (long sync)
|
// Construct the downloader (long sync)
|
||||||
h.downloader = downloader.New(config.Database, h.eventMux, h.chain, nil, h.removePeer, success)
|
h.downloader = downloader.New(config.Database, h.eventMux, h.chain, nil, h.removePeer, h.enableSyncedFeatures)
|
||||||
if ttd := h.chain.Config().TerminalTotalDifficulty; ttd != nil {
|
if ttd := h.chain.Config().TerminalTotalDifficulty; ttd != nil {
|
||||||
if h.chain.Config().TerminalTotalDifficultyPassed {
|
if h.chain.Config().TerminalTotalDifficultyPassed {
|
||||||
log.Info("Chain post-merge, sync via beacon client")
|
log.Info("Chain post-merge, sync via beacon client")
|
||||||
@ -245,8 +237,8 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||||||
// accept each others' blocks until a restart. Unfortunately we haven't figured
|
// accept each others' blocks until a restart. Unfortunately we haven't figured
|
||||||
// out a way yet where nodes can decide unilaterally whether the network is new
|
// out a way yet where nodes can decide unilaterally whether the network is new
|
||||||
// or not. This should be fixed if we figure out a solution.
|
// or not. This should be fixed if we figure out a solution.
|
||||||
if h.snapSync.Load() {
|
if !h.synced.Load() {
|
||||||
log.Warn("Snap syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
|
log.Warn("Syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if h.merger.TDDReached() {
|
if h.merger.TDDReached() {
|
||||||
@ -272,11 +264,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n, err := h.chain.InsertChain(blocks)
|
return h.chain.InsertChain(blocks)
|
||||||
if err == nil {
|
|
||||||
h.enableSyncedFeatures() // Mark initial sync done on any fetcher import
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer)
|
h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer)
|
||||||
|
|
||||||
@ -680,7 +668,15 @@ func (h *handler) txBroadcastLoop() {
|
|||||||
// enableSyncedFeatures enables the post-sync functionalities when the initial
|
// enableSyncedFeatures enables the post-sync functionalities when the initial
|
||||||
// sync is finished.
|
// sync is finished.
|
||||||
func (h *handler) enableSyncedFeatures() {
|
func (h *handler) enableSyncedFeatures() {
|
||||||
h.acceptTxs.Store(true)
|
// Mark the local node as synced.
|
||||||
|
h.synced.Store(true)
|
||||||
|
|
||||||
|
// If we were running snap sync and it finished, disable doing another
|
||||||
|
// round on next sync cycle
|
||||||
|
if h.snapSync.Load() {
|
||||||
|
log.Info("Snap sync complete, auto disabling")
|
||||||
|
h.snapSync.Store(false)
|
||||||
|
}
|
||||||
if h.chain.TrieDB().Scheme() == rawdb.PathScheme {
|
if h.chain.TrieDB().Scheme() == rawdb.PathScheme {
|
||||||
h.chain.TrieDB().SetBufferSize(pathdb.DefaultBufferSize)
|
h.chain.TrieDB().SetBufferSize(pathdb.DefaultBufferSize)
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func (h *ethHandler) PeerInfo(id enode.ID) interface{} {
|
|||||||
// AcceptTxs retrieves whether transaction processing is enabled on the node
|
// AcceptTxs retrieves whether transaction processing is enabled on the node
|
||||||
// or if inbound transactions should simply be dropped.
|
// or if inbound transactions should simply be dropped.
|
||||||
func (h *ethHandler) AcceptTxs() bool {
|
func (h *ethHandler) AcceptTxs() bool {
|
||||||
return h.acceptTxs.Load()
|
return h.synced.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is invoked from a peer's message handler when it receives a new remote
|
// Handle is invoked from a peer's message handler when it receives a new remote
|
||||||
|
@ -248,7 +248,7 @@ func testRecvTransactions(t *testing.T, protocol uint) {
|
|||||||
handler := newTestHandler()
|
handler := newTestHandler()
|
||||||
defer handler.close()
|
defer handler.close()
|
||||||
|
|
||||||
handler.handler.acceptTxs.Store(true) // mark synced to accept transactions
|
handler.handler.synced.Store(true) // mark synced to accept transactions
|
||||||
|
|
||||||
txs := make(chan core.NewTxsEvent)
|
txs := make(chan core.NewTxsEvent)
|
||||||
sub := handler.txpool.SubscribeNewTxsEvent(txs)
|
sub := handler.txpool.SubscribeNewTxsEvent(txs)
|
||||||
@ -401,7 +401,7 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
|
|||||||
sinks[i] = newTestHandler()
|
sinks[i] = newTestHandler()
|
||||||
defer sinks[i].close()
|
defer sinks[i].close()
|
||||||
|
|
||||||
sinks[i].handler.acceptTxs.Store(true) // mark synced to accept transactions
|
sinks[i].handler.synced.Store(true) // mark synced to accept transactions
|
||||||
}
|
}
|
||||||
// Interconnect all the sink handlers with the source handler
|
// Interconnect all the sink handlers with the source handler
|
||||||
for i, sink := range sinks {
|
for i, sink := range sinks {
|
||||||
|
23
eth/sync.go
23
eth/sync.go
@ -197,16 +197,25 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
|
|||||||
return downloader.SnapSync, td
|
return downloader.SnapSync, td
|
||||||
}
|
}
|
||||||
// We are probably in full sync, but we might have rewound to before the
|
// We are probably in full sync, but we might have rewound to before the
|
||||||
// snap sync pivot, check if we should reenable
|
// snap sync pivot, check if we should re-enable snap sync.
|
||||||
|
head := cs.handler.chain.CurrentBlock()
|
||||||
if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil {
|
if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil {
|
||||||
if head := cs.handler.chain.CurrentBlock(); head.Number.Uint64() < *pivot {
|
if head.Number.Uint64() < *pivot {
|
||||||
block := cs.handler.chain.CurrentSnapBlock()
|
block := cs.handler.chain.CurrentSnapBlock()
|
||||||
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
|
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
|
||||||
return downloader.SnapSync, td
|
return downloader.SnapSync, td
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// We are in a full sync, but the associated head state is missing. To complete
|
||||||
|
// the head state, forcefully rerun the snap sync. Note it doesn't mean the
|
||||||
|
// persistent state is corrupted, just mismatch with the head block.
|
||||||
|
if !cs.handler.chain.HasState(head.Root) {
|
||||||
|
block := cs.handler.chain.CurrentSnapBlock()
|
||||||
|
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
|
||||||
|
log.Info("Reenabled snap sync as chain is stateless")
|
||||||
|
return downloader.SnapSync, td
|
||||||
|
}
|
||||||
// Nope, we're really full syncing
|
// Nope, we're really full syncing
|
||||||
head := cs.handler.chain.CurrentBlock()
|
|
||||||
td := cs.handler.chain.GetTd(head.Hash(), head.Number.Uint64())
|
td := cs.handler.chain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
return downloader.FullSync, td
|
return downloader.FullSync, td
|
||||||
}
|
}
|
||||||
@ -242,13 +251,7 @@ func (h *handler) doSync(op *chainSyncOp) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if h.snapSync.Load() {
|
h.enableSyncedFeatures()
|
||||||
log.Info("Snap sync complete, auto disabling")
|
|
||||||
h.snapSync.Store(false)
|
|
||||||
}
|
|
||||||
// If we've successfully finished a sync cycle, enable accepting transactions
|
|
||||||
// from the network.
|
|
||||||
h.acceptTxs.Store(true)
|
|
||||||
|
|
||||||
head := h.chain.CurrentBlock()
|
head := h.chain.CurrentBlock()
|
||||||
if head.Number.Uint64() > 0 {
|
if head.Number.Uint64() > 0 {
|
||||||
|
@ -108,10 +108,10 @@ func (ec *Client) PeerCount(ctx context.Context) (uint64, error) {
|
|||||||
return uint64(result), err
|
return uint64(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockReceipts returns the receipts of a given block number or hash
|
// BlockReceipts returns the receipts of a given block number or hash.
|
||||||
func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) {
|
func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) {
|
||||||
var r []*types.Receipt
|
var r []*types.Receipt
|
||||||
err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash)
|
err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash.String())
|
||||||
if err == nil && r == nil {
|
if err == nil && r == nil {
|
||||||
return nil, ethereum.NotFound
|
return nil, ethereum.NotFound
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -46,7 +46,7 @@ require (
|
|||||||
github.com/jackpal/go-nat-pmp v1.0.2
|
github.com/jackpal/go-nat-pmp v1.0.2
|
||||||
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
|
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c
|
github.com/karalabe/usb v0.0.2
|
||||||
github.com/kylelemons/godebug v1.1.0
|
github.com/kylelemons/godebug v1.1.0
|
||||||
github.com/mattn/go-colorable v0.1.13
|
github.com/mattn/go-colorable v0.1.13
|
||||||
github.com/mattn/go-isatty v0.0.16
|
github.com/mattn/go-isatty v0.0.16
|
||||||
|
4
go.sum
4
go.sum
@ -362,8 +362,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
|||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||||
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c h1:AqsttAyEyIEsNz5WLRwuRwjiT5CMDUfLk6cFJDVPebs=
|
github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4=
|
||||||
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||||
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
|
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
|
||||||
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
|
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
|
||||||
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
|
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
|
||||||
|
@ -87,8 +87,9 @@ var (
|
|||||||
Category: flags.LoggingCategory,
|
Category: flags.LoggingCategory,
|
||||||
}
|
}
|
||||||
logRotateFlag = &cli.BoolFlag{
|
logRotateFlag = &cli.BoolFlag{
|
||||||
Name: "log.rotate",
|
Name: "log.rotate",
|
||||||
Usage: "Enables log file rotation",
|
Usage: "Enables log file rotation",
|
||||||
|
Category: flags.LoggingCategory,
|
||||||
}
|
}
|
||||||
logMaxSizeMBsFlag = &cli.IntFlag{
|
logMaxSizeMBsFlag = &cli.IntFlag{
|
||||||
Name: "log.maxsize",
|
Name: "log.maxsize",
|
||||||
|
@ -1158,8 +1158,12 @@ func (e *revertError) ErrorData() interface{} {
|
|||||||
//
|
//
|
||||||
// Note, this function doesn't make and changes in the state/blockchain and is
|
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||||
// useful to execute and retrieve values.
|
// useful to execute and retrieve values.
|
||||||
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) {
|
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) {
|
||||||
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, blockOverrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
|
if blockNrOrHash == nil {
|
||||||
|
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
|
blockNrOrHash = &latest
|
||||||
|
}
|
||||||
|
result, err := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, blockOverrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -846,7 +846,7 @@ func TestCall(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tc := range testSuite {
|
for i, tc := range testSuite {
|
||||||
result, err := api.Call(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
|
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
|
||||||
if tc.expectErr != nil {
|
if tc.expectErr != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
||||||
|
@ -64,6 +64,7 @@ func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *stat
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testBlockChain struct {
|
type testBlockChain struct {
|
||||||
|
root common.Hash
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
@ -89,6 +90,10 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
|
|||||||
return bc.statedb, nil
|
return bc.statedb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *testBlockChain) HasState(root common.Hash) bool {
|
||||||
|
return bc.root == root
|
||||||
|
}
|
||||||
|
|
||||||
func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||||
return bc.chainHeadFeed.Subscribe(ch)
|
return bc.chainHeadFeed.Subscribe(ch)
|
||||||
}
|
}
|
||||||
@ -302,7 +307,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
|
|||||||
t.Fatalf("can't create new chain %v", err)
|
t.Fatalf("can't create new chain %v", err)
|
||||||
}
|
}
|
||||||
statedb, _ := state.New(bc.Genesis().Root(), bc.StateCache(), nil)
|
statedb, _ := state.New(bc.Genesis().Root(), bc.StateCache(), nil)
|
||||||
blockchain := &testBlockChain{chainConfig, statedb, 10000000, new(event.Feed)}
|
blockchain := &testBlockChain{bc.Genesis().Root(), chainConfig, statedb, 10000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := legacypool.New(testTxPoolConfig, blockchain)
|
pool := legacypool.New(testTxPoolConfig, blockchain)
|
||||||
txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain, []txpool.SubPool{pool})
|
txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain, []txpool.SubPool{pool})
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
// Genesis hashes to enforce below configs on.
|
// Genesis hashes to enforce below configs on.
|
||||||
var (
|
var (
|
||||||
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||||
HoleskyGenesisHash = common.HexToHash("0xff9006519a8ce843ac9c28549d24211420b546e12ce2d170c77a8cca7964f23d")
|
HoleskyGenesisHash = common.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4")
|
||||||
SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")
|
SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")
|
||||||
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
|
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
|
||||||
)
|
)
|
||||||
@ -80,8 +80,7 @@ var (
|
|||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
TerminalTotalDifficultyPassed: true,
|
TerminalTotalDifficultyPassed: true,
|
||||||
MergeNetsplitBlock: nil,
|
MergeNetsplitBlock: nil,
|
||||||
ShanghaiTime: newUint64(1694790240),
|
ShanghaiTime: newUint64(1696000704),
|
||||||
CancunTime: newUint64(2000000000),
|
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
}
|
}
|
||||||
// SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network.
|
// SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network.
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
VersionMinor = 13 // Minor version component of the current release
|
VersionMinor = 13 // Minor version component of the current release
|
||||||
VersionPatch = 1 // Patch version component of the current release
|
VersionPatch = 2 // Patch version component of the current release
|
||||||
VersionMeta = "stable" // Version metadata to append to the version string
|
VersionMeta = "stable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ func txFactory() {
|
|||||||
"from": coinBase,
|
"from": coinBase,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 126; i ++ {
|
for i := 0; i < 10; i ++ {
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
err = client.Call(&t3, "eth_sendTransaction", genericArg)
|
err = client.Call(&t3, "eth_sendTransaction", genericArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -293,7 +293,5 @@ func testGetContractCode(hash core.Hash) {
|
|||||||
log.Error("Exit with error, return value from GetContractCode is divergent from control value")
|
log.Error("Exit with error, return value from GetContractCode is divergent from control value")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("made it through checkGetContractCode")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ if ps -p $pid1 > /dev/null; then
|
|||||||
kill $pid1
|
kill $pid1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 255
|
sleep 25
|
||||||
|
|
||||||
if ps -p $pid0 > /dev/null; then
|
if ps -p $pid0 > /dev/null; then
|
||||||
kill $pid0
|
kill $pid0
|
||||||
|
@ -189,6 +189,15 @@ func (db *Database) WritePreimages() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preimage retrieves a cached trie node pre-image from memory. If it cannot be
|
||||||
|
// found cached, the method queries the persistent database for the content.
|
||||||
|
func (db *Database) Preimage(hash common.Hash) []byte {
|
||||||
|
if db.preimages == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return db.preimages.preimage(hash)
|
||||||
|
}
|
||||||
|
|
||||||
// Cap iteratively flushes old but still referenced trie nodes until the total
|
// Cap iteratively flushes old but still referenced trie nodes until the total
|
||||||
// memory usage goes below the given threshold. The held pre-images accumulated
|
// memory usage goes below the given threshold. The held pre-images accumulated
|
||||||
// up to this point will be flushed in case the size exceeds the threshold.
|
// up to this point will be flushed in case the size exceeds the threshold.
|
||||||
@ -264,15 +273,27 @@ func (db *Database) Recoverable(root common.Hash) (bool, error) {
|
|||||||
return pdb.Recoverable(root), nil
|
return pdb.Recoverable(root), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset wipes all available journal from the persistent database and discard
|
// Disable deactivates the database and invalidates all available state layers
|
||||||
// all caches and diff layers. Using the given root to create a new disk layer.
|
// as stale to prevent access to the persistent state, which is in the syncing
|
||||||
|
// stage.
|
||||||
|
//
|
||||||
// It's only supported by path-based database and will return an error for others.
|
// It's only supported by path-based database and will return an error for others.
|
||||||
func (db *Database) Reset(root common.Hash) error {
|
func (db *Database) Disable() error {
|
||||||
pdb, ok := db.backend.(*pathdb.Database)
|
pdb, ok := db.backend.(*pathdb.Database)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("not supported")
|
return errors.New("not supported")
|
||||||
}
|
}
|
||||||
return pdb.Reset(root)
|
return pdb.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable activates database and resets the state tree with the provided persistent
|
||||||
|
// state root once the state sync is finished.
|
||||||
|
func (db *Database) Enable(root common.Hash) error {
|
||||||
|
pdb, ok := db.backend.(*pathdb.Database)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not supported")
|
||||||
|
}
|
||||||
|
return pdb.Enable(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Journal commits an entire diff hierarchy to disk into a single journal entry.
|
// Journal commits an entire diff hierarchy to disk into a single journal entry.
|
||||||
|
95
trie/sync.go
95
trie/sync.go
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNotRequested is returned by the trie sync when it's requested to process a
|
// ErrNotRequested is returned by the trie sync when it's requested to process a
|
||||||
@ -42,6 +43,16 @@ var ErrAlreadyProcessed = errors.New("already processed")
|
|||||||
// memory if the node was configured with a significant number of peers.
|
// memory if the node was configured with a significant number of peers.
|
||||||
const maxFetchesPerDepth = 16384
|
const maxFetchesPerDepth = 16384
|
||||||
|
|
||||||
|
var (
|
||||||
|
// deletionGauge is the metric to track how many trie node deletions
|
||||||
|
// are performed in total during the sync process.
|
||||||
|
deletionGauge = metrics.NewRegisteredGauge("trie/sync/delete", nil)
|
||||||
|
|
||||||
|
// lookupGauge is the metric to track how many trie node lookups are
|
||||||
|
// performed to determine if node needs to be deleted.
|
||||||
|
lookupGauge = metrics.NewRegisteredGauge("trie/sync/lookup", nil)
|
||||||
|
)
|
||||||
|
|
||||||
// SyncPath is a path tuple identifying a particular trie node either in a single
|
// SyncPath is a path tuple identifying a particular trie node either in a single
|
||||||
// trie (account) or a layered trie (account -> storage).
|
// trie (account) or a layered trie (account -> storage).
|
||||||
//
|
//
|
||||||
@ -93,9 +104,10 @@ type LeafCallback func(keys [][]byte, path []byte, leaf []byte, parent common.Ha
|
|||||||
|
|
||||||
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
|
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
|
||||||
type nodeRequest struct {
|
type nodeRequest struct {
|
||||||
hash common.Hash // Hash of the trie node to retrieve
|
hash common.Hash // Hash of the trie node to retrieve
|
||||||
path []byte // Merkle path leading to this node for prioritization
|
path []byte // Merkle path leading to this node for prioritization
|
||||||
data []byte // Data content of the node, cached until all subtrees complete
|
data []byte // Data content of the node, cached until all subtrees complete
|
||||||
|
deletes [][]byte // List of internal path segments for trie nodes to delete
|
||||||
|
|
||||||
parent *nodeRequest // Parent state node referencing this entry
|
parent *nodeRequest // Parent state node referencing this entry
|
||||||
deps int // Number of dependencies before allowed to commit this node
|
deps int // Number of dependencies before allowed to commit this node
|
||||||
@ -125,18 +137,20 @@ type CodeSyncResult struct {
|
|||||||
// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
|
// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
|
||||||
// persisted data items.
|
// persisted data items.
|
||||||
type syncMemBatch struct {
|
type syncMemBatch struct {
|
||||||
nodes map[string][]byte // In-memory membatch of recently completed nodes
|
nodes map[string][]byte // In-memory membatch of recently completed nodes
|
||||||
hashes map[string]common.Hash // Hashes of recently completed nodes
|
hashes map[string]common.Hash // Hashes of recently completed nodes
|
||||||
codes map[common.Hash][]byte // In-memory membatch of recently completed codes
|
deletes map[string]struct{} // List of paths for trie node to delete
|
||||||
size uint64 // Estimated batch-size of in-memory data.
|
codes map[common.Hash][]byte // In-memory membatch of recently completed codes
|
||||||
|
size uint64 // Estimated batch-size of in-memory data.
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
|
// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
|
||||||
func newSyncMemBatch() *syncMemBatch {
|
func newSyncMemBatch() *syncMemBatch {
|
||||||
return &syncMemBatch{
|
return &syncMemBatch{
|
||||||
nodes: make(map[string][]byte),
|
nodes: make(map[string][]byte),
|
||||||
hashes: make(map[string]common.Hash),
|
hashes: make(map[string]common.Hash),
|
||||||
codes: make(map[common.Hash][]byte),
|
deletes: make(map[string]struct{}),
|
||||||
|
codes: make(map[common.Hash][]byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,16 +361,23 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error {
|
|||||||
// Commit flushes the data stored in the internal membatch out to persistent
|
// Commit flushes the data stored in the internal membatch out to persistent
|
||||||
// storage, returning any occurred error.
|
// storage, returning any occurred error.
|
||||||
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
||||||
// Dump the membatch into a database dbw
|
// Flush the pending node writes into database batch.
|
||||||
for path, value := range s.membatch.nodes {
|
for path, value := range s.membatch.nodes {
|
||||||
owner, inner := ResolvePath([]byte(path))
|
owner, inner := ResolvePath([]byte(path))
|
||||||
rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme)
|
rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme)
|
||||||
}
|
}
|
||||||
|
// Flush the pending node deletes into the database batch.
|
||||||
|
// Please note that each written and deleted node has a
|
||||||
|
// unique path, ensuring no duplication occurs.
|
||||||
|
for path := range s.membatch.deletes {
|
||||||
|
owner, inner := ResolvePath([]byte(path))
|
||||||
|
rawdb.DeleteTrieNode(dbw, owner, inner, common.Hash{} /* unused */, s.scheme)
|
||||||
|
}
|
||||||
|
// Flush the pending code writes into database batch.
|
||||||
for hash, value := range s.membatch.codes {
|
for hash, value := range s.membatch.codes {
|
||||||
rawdb.WriteCode(dbw, hash, value)
|
rawdb.WriteCode(dbw, hash, value)
|
||||||
}
|
}
|
||||||
// Drop the membatch data and return
|
s.membatch = newSyncMemBatch() // reset the batch
|
||||||
s.membatch = newSyncMemBatch()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,6 +446,39 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
|
|||||||
node: node.Val,
|
node: node.Val,
|
||||||
path: append(append([]byte(nil), req.path...), key...),
|
path: append(append([]byte(nil), req.path...), key...),
|
||||||
}}
|
}}
|
||||||
|
// Mark all internal nodes between shortNode and its **in disk**
|
||||||
|
// child as invalid. This is essential in the case of path mode
|
||||||
|
// scheme; otherwise, state healing might overwrite existing child
|
||||||
|
// nodes silently while leaving a dangling parent node within the
|
||||||
|
// range of this internal path on disk. This would break the
|
||||||
|
// guarantee for state healing.
|
||||||
|
//
|
||||||
|
// While it's possible for this shortNode to overwrite a previously
|
||||||
|
// existing full node, the other branches of the fullNode can be
|
||||||
|
// retained as they remain untouched and complete.
|
||||||
|
//
|
||||||
|
// This step is only necessary for path mode, as there is no deletion
|
||||||
|
// in hash mode at all.
|
||||||
|
if _, ok := node.Val.(hashNode); ok && s.scheme == rawdb.PathScheme {
|
||||||
|
owner, inner := ResolvePath(req.path)
|
||||||
|
for i := 1; i < len(key); i++ {
|
||||||
|
// While checking for a non-existent item in Pebble can be less efficient
|
||||||
|
// without a bloom filter, the relatively low frequency of lookups makes
|
||||||
|
// the performance impact negligible.
|
||||||
|
var exists bool
|
||||||
|
if owner == (common.Hash{}) {
|
||||||
|
exists = rawdb.ExistsAccountTrieNode(s.database, append(inner, key[:i]...))
|
||||||
|
} else {
|
||||||
|
exists = rawdb.ExistsStorageTrieNode(s.database, owner, append(inner, key[:i]...))
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
req.deletes = append(req.deletes, key[:i])
|
||||||
|
deletionGauge.Inc(1)
|
||||||
|
log.Debug("Detected dangling node", "owner", owner, "path", append(inner, key[:i]...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lookupGauge.Inc(int64(len(key) - 1))
|
||||||
|
}
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
for i := 0; i < 17; i++ {
|
for i := 0; i < 17; i++ {
|
||||||
if node.Children[i] != nil {
|
if node.Children[i] != nil {
|
||||||
@ -509,10 +563,19 @@ func (s *Sync) commitNodeRequest(req *nodeRequest) error {
|
|||||||
// Write the node content to the membatch
|
// Write the node content to the membatch
|
||||||
s.membatch.nodes[string(req.path)] = req.data
|
s.membatch.nodes[string(req.path)] = req.data
|
||||||
s.membatch.hashes[string(req.path)] = req.hash
|
s.membatch.hashes[string(req.path)] = req.hash
|
||||||
|
|
||||||
// The size tracking refers to the db-batch, not the in-memory data.
|
// The size tracking refers to the db-batch, not the in-memory data.
|
||||||
// Therefore, we ignore the req.path, and account only for the hash+data
|
if s.scheme == rawdb.PathScheme {
|
||||||
// which eventually is written to db.
|
s.membatch.size += uint64(len(req.path) + len(req.data))
|
||||||
s.membatch.size += common.HashLength + uint64(len(req.data))
|
} else {
|
||||||
|
s.membatch.size += common.HashLength + uint64(len(req.data))
|
||||||
|
}
|
||||||
|
// Delete the internal nodes which are marked as invalid
|
||||||
|
for _, segment := range req.deletes {
|
||||||
|
path := append(req.path, segment...)
|
||||||
|
s.membatch.deletes[string(path)] = struct{}{}
|
||||||
|
s.membatch.size += uint64(len(path))
|
||||||
|
}
|
||||||
delete(s.nodeReqs, string(req.path))
|
delete(s.nodeReqs, string(req.path))
|
||||||
s.fetches[len(req.path)]--
|
s.fetches[len(req.path)]--
|
||||||
|
|
||||||
|
@ -70,31 +70,53 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str
|
|||||||
|
|
||||||
// checkTrieContents cross references a reconstructed trie with an expected data
|
// checkTrieContents cross references a reconstructed trie with an expected data
|
||||||
// content map.
|
// content map.
|
||||||
func checkTrieContents(t *testing.T, db ethdb.Database, scheme string, root []byte, content map[string][]byte) {
|
func checkTrieContents(t *testing.T, db ethdb.Database, scheme string, root []byte, content map[string][]byte, rawTrie bool) {
|
||||||
// Check root availability and trie contents
|
// Check root availability and trie contents
|
||||||
ndb := newTestDatabase(db, scheme)
|
ndb := newTestDatabase(db, scheme)
|
||||||
trie, err := NewStateTrie(TrieID(common.BytesToHash(root)), ndb)
|
if err := checkTrieConsistency(db, scheme, common.BytesToHash(root), rawTrie); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create trie at %x: %v", root, err)
|
|
||||||
}
|
|
||||||
if err := checkTrieConsistency(db, scheme, common.BytesToHash(root)); err != nil {
|
|
||||||
t.Fatalf("inconsistent trie at %x: %v", root, err)
|
t.Fatalf("inconsistent trie at %x: %v", root, err)
|
||||||
}
|
}
|
||||||
|
type reader interface {
|
||||||
|
MustGet(key []byte) []byte
|
||||||
|
}
|
||||||
|
var r reader
|
||||||
|
if rawTrie {
|
||||||
|
trie, err := New(TrieID(common.BytesToHash(root)), ndb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create trie at %x: %v", root, err)
|
||||||
|
}
|
||||||
|
r = trie
|
||||||
|
} else {
|
||||||
|
trie, err := NewStateTrie(TrieID(common.BytesToHash(root)), ndb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create trie at %x: %v", root, err)
|
||||||
|
}
|
||||||
|
r = trie
|
||||||
|
}
|
||||||
for key, val := range content {
|
for key, val := range content {
|
||||||
if have := trie.MustGet([]byte(key)); !bytes.Equal(have, val) {
|
if have := r.MustGet([]byte(key)); !bytes.Equal(have, val) {
|
||||||
t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val)
|
t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTrieConsistency checks that all nodes in a trie are indeed present.
|
// checkTrieConsistency checks that all nodes in a trie are indeed present.
|
||||||
func checkTrieConsistency(db ethdb.Database, scheme string, root common.Hash) error {
|
func checkTrieConsistency(db ethdb.Database, scheme string, root common.Hash, rawTrie bool) error {
|
||||||
ndb := newTestDatabase(db, scheme)
|
ndb := newTestDatabase(db, scheme)
|
||||||
trie, err := NewStateTrie(TrieID(root), ndb)
|
var it NodeIterator
|
||||||
if err != nil {
|
if rawTrie {
|
||||||
return nil // Consider a non existent state consistent
|
trie, err := New(TrieID(root), ndb)
|
||||||
|
if err != nil {
|
||||||
|
return nil // Consider a non existent state consistent
|
||||||
|
}
|
||||||
|
it = trie.MustNodeIterator(nil)
|
||||||
|
} else {
|
||||||
|
trie, err := NewStateTrie(TrieID(root), ndb)
|
||||||
|
if err != nil {
|
||||||
|
return nil // Consider a non existent state consistent
|
||||||
|
}
|
||||||
|
it = trie.MustNodeIterator(nil)
|
||||||
}
|
}
|
||||||
it := trie.MustNodeIterator(nil)
|
|
||||||
for it.Next(true) {
|
for it.Next(true) {
|
||||||
}
|
}
|
||||||
return it.Error()
|
return it.Error()
|
||||||
@ -205,7 +227,7 @@ func testIterativeSync(t *testing.T, count int, bypath bool, scheme string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cross check that the two tries are in sync
|
// Cross check that the two tries are in sync
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
||||||
@ -271,7 +293,7 @@ func testIterativeDelayedSync(t *testing.T, scheme string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cross check that the two tries are in sync
|
// Cross check that the two tries are in sync
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that given a root hash, a trie can sync iteratively on a single thread,
|
// Tests that given a root hash, a trie can sync iteratively on a single thread,
|
||||||
@ -341,7 +363,7 @@ func testIterativeRandomSync(t *testing.T, count int, scheme string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cross check that the two tries are in sync
|
// Cross check that the two tries are in sync
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
||||||
@ -413,7 +435,7 @@ func testIterativeRandomDelayedSync(t *testing.T, scheme string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cross check that the two tries are in sync
|
// Cross check that the two tries are in sync
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that a trie sync will not request nodes multiple times, even if they
|
// Tests that a trie sync will not request nodes multiple times, even if they
|
||||||
@ -484,7 +506,7 @@ func testDuplicateAvoidanceSync(t *testing.T, scheme string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cross check that the two tries are in sync
|
// Cross check that the two tries are in sync
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that at any point in time during a sync, only complete sub-tries are in
|
// Tests that at any point in time during a sync, only complete sub-tries are in
|
||||||
@ -569,7 +591,7 @@ func testIncompleteSync(t *testing.T, scheme string) {
|
|||||||
nodeHash := addedHashes[i]
|
nodeHash := addedHashes[i]
|
||||||
value := rawdb.ReadTrieNode(diskdb, owner, inner, nodeHash, scheme)
|
value := rawdb.ReadTrieNode(diskdb, owner, inner, nodeHash, scheme)
|
||||||
rawdb.DeleteTrieNode(diskdb, owner, inner, nodeHash, scheme)
|
rawdb.DeleteTrieNode(diskdb, owner, inner, nodeHash, scheme)
|
||||||
if err := checkTrieConsistency(diskdb, srcDb.Scheme(), root); err == nil {
|
if err := checkTrieConsistency(diskdb, srcDb.Scheme(), root, false); err == nil {
|
||||||
t.Fatalf("trie inconsistency not caught, missing: %x", path)
|
t.Fatalf("trie inconsistency not caught, missing: %x", path)
|
||||||
}
|
}
|
||||||
rawdb.WriteTrieNode(diskdb, owner, inner, nodeHash, value, scheme)
|
rawdb.WriteTrieNode(diskdb, owner, inner, nodeHash, value, scheme)
|
||||||
@ -643,7 +665,7 @@ func testSyncOrdering(t *testing.T, scheme string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cross check that the two tries are in sync
|
// Cross check that the two tries are in sync
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
|
|
||||||
// Check that the trie nodes have been requested path-ordered
|
// Check that the trie nodes have been requested path-ordered
|
||||||
for i := 0; i < len(reqs)-1; i++ {
|
for i := 0; i < len(reqs)-1; i++ {
|
||||||
@ -664,7 +686,7 @@ func syncWith(t *testing.T, root common.Hash, db ethdb.Database, srcDb *Database
|
|||||||
|
|
||||||
// The code requests are ignored here since there is no code
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
paths, nodes, _ := sched.Missing(1)
|
paths, nodes, _ := sched.Missing(0)
|
||||||
var elements []trieElement
|
var elements []trieElement
|
||||||
for i := 0; i < len(paths); i++ {
|
for i := 0; i < len(paths); i++ {
|
||||||
elements = append(elements, trieElement{
|
elements = append(elements, trieElement{
|
||||||
@ -698,7 +720,7 @@ func syncWith(t *testing.T, root common.Hash, db ethdb.Database, srcDb *Database
|
|||||||
}
|
}
|
||||||
batch.Write()
|
batch.Write()
|
||||||
|
|
||||||
paths, nodes, _ = sched.Missing(1)
|
paths, nodes, _ = sched.Missing(0)
|
||||||
elements = elements[:0]
|
elements = elements[:0]
|
||||||
for i := 0; i < len(paths); i++ {
|
for i := 0; i < len(paths); i++ {
|
||||||
elements = append(elements, trieElement{
|
elements = append(elements, trieElement{
|
||||||
@ -724,7 +746,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
|
|||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
syncWith(t, srcTrie.Hash(), diskdb, srcDb)
|
syncWith(t, srcTrie.Hash(), diskdb, srcDb)
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), srcData, false)
|
||||||
|
|
||||||
// Push more modifications into the src trie, to see if dest trie can still
|
// Push more modifications into the src trie, to see if dest trie can still
|
||||||
// sync with it(overwrite stale states)
|
// sync with it(overwrite stale states)
|
||||||
@ -748,7 +770,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
|
|||||||
srcTrie, _ = NewStateTrie(TrieID(root), srcDb)
|
srcTrie, _ = NewStateTrie(TrieID(root), srcDb)
|
||||||
|
|
||||||
syncWith(t, srcTrie.Hash(), diskdb, srcDb)
|
syncWith(t, srcTrie.Hash(), diskdb, srcDb)
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), diff)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), diff, false)
|
||||||
|
|
||||||
// Revert added modifications from the src trie, to see if dest trie can still
|
// Revert added modifications from the src trie, to see if dest trie can still
|
||||||
// sync with it(overwrite reverted states)
|
// sync with it(overwrite reverted states)
|
||||||
@ -772,5 +794,98 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
|
|||||||
srcTrie, _ = NewStateTrie(TrieID(root), srcDb)
|
srcTrie, _ = NewStateTrie(TrieID(root), srcDb)
|
||||||
|
|
||||||
syncWith(t, srcTrie.Hash(), diskdb, srcDb)
|
syncWith(t, srcTrie.Hash(), diskdb, srcDb)
|
||||||
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), reverted)
|
checkTrieContents(t, diskdb, srcDb.Scheme(), srcTrie.Hash().Bytes(), reverted, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests if state syncer can correctly catch up the pivot move.
|
||||||
|
func TestPivotMove(t *testing.T) {
|
||||||
|
testPivotMove(t, rawdb.HashScheme, true)
|
||||||
|
testPivotMove(t, rawdb.HashScheme, false)
|
||||||
|
testPivotMove(t, rawdb.PathScheme, true)
|
||||||
|
testPivotMove(t, rawdb.PathScheme, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPivotMove(t *testing.T, scheme string, tiny bool) {
|
||||||
|
var (
|
||||||
|
srcDisk = rawdb.NewMemoryDatabase()
|
||||||
|
srcTrieDB = newTestDatabase(srcDisk, scheme)
|
||||||
|
srcTrie, _ = New(TrieID(types.EmptyRootHash), srcTrieDB)
|
||||||
|
|
||||||
|
deleteFn = func(key []byte, tr *Trie, states map[string][]byte) {
|
||||||
|
tr.Delete(key)
|
||||||
|
delete(states, string(key))
|
||||||
|
}
|
||||||
|
writeFn = func(key []byte, val []byte, tr *Trie, states map[string][]byte) {
|
||||||
|
if val == nil {
|
||||||
|
if tiny {
|
||||||
|
val = randBytes(4)
|
||||||
|
} else {
|
||||||
|
val = randBytes(32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.Update(key, val)
|
||||||
|
states[string(key)] = common.CopyBytes(val)
|
||||||
|
}
|
||||||
|
copyStates = func(states map[string][]byte) map[string][]byte {
|
||||||
|
cpy := make(map[string][]byte)
|
||||||
|
for k, v := range states {
|
||||||
|
cpy[k] = v
|
||||||
|
}
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
)
|
||||||
|
stateA := make(map[string][]byte)
|
||||||
|
writeFn([]byte{0x01, 0x23}, nil, srcTrie, stateA)
|
||||||
|
writeFn([]byte{0x01, 0x24}, nil, srcTrie, stateA)
|
||||||
|
writeFn([]byte{0x12, 0x33}, nil, srcTrie, stateA)
|
||||||
|
writeFn([]byte{0x12, 0x34}, nil, srcTrie, stateA)
|
||||||
|
writeFn([]byte{0x02, 0x34}, nil, srcTrie, stateA)
|
||||||
|
writeFn([]byte{0x13, 0x44}, nil, srcTrie, stateA)
|
||||||
|
|
||||||
|
rootA, nodesA, _ := srcTrie.Commit(false)
|
||||||
|
if err := srcTrieDB.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := srcTrieDB.Commit(rootA, false); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Create a destination trie and sync with the scheduler
|
||||||
|
destDisk := rawdb.NewMemoryDatabase()
|
||||||
|
syncWith(t, rootA, destDisk, srcTrieDB)
|
||||||
|
checkTrieContents(t, destDisk, scheme, srcTrie.Hash().Bytes(), stateA, true)
|
||||||
|
|
||||||
|
// Delete element to collapse trie
|
||||||
|
stateB := copyStates(stateA)
|
||||||
|
srcTrie, _ = New(TrieID(rootA), srcTrieDB)
|
||||||
|
deleteFn([]byte{0x02, 0x34}, srcTrie, stateB)
|
||||||
|
deleteFn([]byte{0x13, 0x44}, srcTrie, stateB)
|
||||||
|
writeFn([]byte{0x01, 0x24}, nil, srcTrie, stateB)
|
||||||
|
|
||||||
|
rootB, nodesB, _ := srcTrie.Commit(false)
|
||||||
|
if err := srcTrieDB.Update(rootB, rootA, 0, trienode.NewWithNodeSet(nodesB), nil); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := srcTrieDB.Commit(rootB, false); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
syncWith(t, rootB, destDisk, srcTrieDB)
|
||||||
|
checkTrieContents(t, destDisk, scheme, srcTrie.Hash().Bytes(), stateB, true)
|
||||||
|
|
||||||
|
// Add elements to expand trie
|
||||||
|
stateC := copyStates(stateB)
|
||||||
|
srcTrie, _ = New(TrieID(rootB), srcTrieDB)
|
||||||
|
|
||||||
|
writeFn([]byte{0x01, 0x24}, stateA[string([]byte{0x01, 0x24})], srcTrie, stateC)
|
||||||
|
writeFn([]byte{0x02, 0x34}, nil, srcTrie, stateC)
|
||||||
|
writeFn([]byte{0x13, 0x44}, nil, srcTrie, stateC)
|
||||||
|
|
||||||
|
rootC, nodesC, _ := srcTrie.Commit(false)
|
||||||
|
if err := srcTrieDB.Update(rootC, rootB, 0, trienode.NewWithNodeSet(nodesC), nil); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := srcTrieDB.Commit(rootC, false); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
syncWith(t, rootC, destDisk, srcTrieDB)
|
||||||
|
checkTrieContents(t, destDisk, scheme, srcTrie.Hash().Bytes(), stateC, true)
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,8 @@ type Database struct {
|
|||||||
// readOnly is the flag whether the mutation is allowed to be applied.
|
// readOnly is the flag whether the mutation is allowed to be applied.
|
||||||
// It will be set automatically when the database is journaled during
|
// It will be set automatically when the database is journaled during
|
||||||
// the shutdown to reject all following unexpected mutations.
|
// the shutdown to reject all following unexpected mutations.
|
||||||
readOnly bool // Indicator 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
|
||||||
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
|
||||||
@ -179,6 +180,12 @@ func New(diskdb ethdb.Database, config *Config) *Database {
|
|||||||
log.Warn("Truncated extra state histories", "number", pruned)
|
log.Warn("Truncated extra state histories", "number", pruned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Disable database in case node is still in the initial state sync stage.
|
||||||
|
if rawdb.ReadSnapSyncStatusFlag(diskdb) == rawdb.StateSyncRunning && !db.readOnly {
|
||||||
|
if err := db.Disable(); err != nil {
|
||||||
|
log.Crit("Failed to disable database", "err", err) // impossible to happen
|
||||||
|
}
|
||||||
|
}
|
||||||
log.Warn("Path-based state scheme is an experimental feature")
|
log.Warn("Path-based state scheme is an experimental feature")
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
@ -204,9 +211,9 @@ func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint6
|
|||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
defer db.lock.Unlock()
|
defer db.lock.Unlock()
|
||||||
|
|
||||||
// Short circuit if the database is in read only mode.
|
// Short circuit if the mutation is not allowed.
|
||||||
if db.readOnly {
|
if err := db.modifyAllowed(); err != nil {
|
||||||
return errSnapshotReadOnly
|
return err
|
||||||
}
|
}
|
||||||
if err := db.tree.add(root, parentRoot, block, nodes, states); err != nil {
|
if err := db.tree.add(root, parentRoot, block, nodes, states); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -227,45 +234,59 @@ func (db *Database) Commit(root common.Hash, report bool) error {
|
|||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
defer db.lock.Unlock()
|
defer db.lock.Unlock()
|
||||||
|
|
||||||
// Short circuit if the database is in read only mode.
|
// Short circuit if the mutation is not allowed.
|
||||||
if db.readOnly {
|
if err := db.modifyAllowed(); err != nil {
|
||||||
return errSnapshotReadOnly
|
return err
|
||||||
}
|
}
|
||||||
return db.tree.cap(root, 0)
|
return db.tree.cap(root, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset rebuilds the database with the specified state as the base.
|
// Disable deactivates the database and invalidates all available state layers
|
||||||
//
|
// as stale to prevent access to the persistent state, which is in the syncing
|
||||||
// - if target state is empty, clear the stored state and all layers on top
|
// stage.
|
||||||
// - if target state is non-empty, ensure the stored state matches with it
|
func (db *Database) Disable() error {
|
||||||
// and clear all other layers on top.
|
|
||||||
func (db *Database) Reset(root common.Hash) error {
|
|
||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
defer db.lock.Unlock()
|
defer db.lock.Unlock()
|
||||||
|
|
||||||
// Short circuit if the database is in read only mode.
|
// Short circuit if the database is in read only mode.
|
||||||
if db.readOnly {
|
if db.readOnly {
|
||||||
return errSnapshotReadOnly
|
return errDatabaseReadOnly
|
||||||
}
|
}
|
||||||
batch := db.diskdb.NewBatch()
|
// Prevent duplicated disable operation.
|
||||||
root = types.TrieRootHash(root)
|
if db.waitSync {
|
||||||
if root == types.EmptyRootHash {
|
log.Error("Reject duplicated disable operation")
|
||||||
// Empty state is requested as the target, nuke out
|
return nil
|
||||||
// the root node and leave all others as dangling.
|
|
||||||
rawdb.DeleteAccountTrieNode(batch, nil)
|
|
||||||
} else {
|
|
||||||
// Ensure the requested state is existent before any
|
|
||||||
// action is applied.
|
|
||||||
_, hash := rawdb.ReadAccountTrieNode(db.diskdb, nil)
|
|
||||||
if hash != root {
|
|
||||||
return fmt.Errorf("state is mismatched, local: %x, target: %x", hash, root)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Mark the disk layer as stale before applying any mutation.
|
db.waitSync = true
|
||||||
|
|
||||||
|
// Mark the disk layer as stale to prevent access to persistent state.
|
||||||
db.tree.bottom().markStale()
|
db.tree.bottom().markStale()
|
||||||
|
|
||||||
|
// Write the initial sync flag to persist it across restarts.
|
||||||
|
rawdb.WriteSnapSyncStatusFlag(db.diskdb, rawdb.StateSyncRunning)
|
||||||
|
log.Info("Disabled trie database due to state sync")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable activates database and resets the state tree with the provided persistent
|
||||||
|
// state root once the state sync is finished.
|
||||||
|
func (db *Database) Enable(root common.Hash) error {
|
||||||
|
db.lock.Lock()
|
||||||
|
defer db.lock.Unlock()
|
||||||
|
|
||||||
|
// Short circuit if the database is in read only mode.
|
||||||
|
if db.readOnly {
|
||||||
|
return errDatabaseReadOnly
|
||||||
|
}
|
||||||
|
// Ensure the provided state root matches the stored one.
|
||||||
|
root = types.TrieRootHash(root)
|
||||||
|
_, stored := rawdb.ReadAccountTrieNode(db.diskdb, nil)
|
||||||
|
if stored != root {
|
||||||
|
return fmt.Errorf("state root mismatch: stored %x, synced %x", stored, root)
|
||||||
|
}
|
||||||
// Drop the stale state journal in persistent database and
|
// Drop the stale state journal in persistent database and
|
||||||
// reset the persistent state id back to zero.
|
// reset the persistent state id back to zero.
|
||||||
|
batch := db.diskdb.NewBatch()
|
||||||
rawdb.DeleteTrieJournal(batch)
|
rawdb.DeleteTrieJournal(batch)
|
||||||
rawdb.WritePersistentStateID(batch, 0)
|
rawdb.WritePersistentStateID(batch, 0)
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
@ -282,8 +303,11 @@ func (db *Database) Reset(root common.Hash) error {
|
|||||||
}
|
}
|
||||||
// Re-construct a new disk layer backed by persistent state
|
// Re-construct a new disk layer backed by persistent state
|
||||||
// with **empty clean cache and node buffer**.
|
// with **empty clean cache and node buffer**.
|
||||||
dl := newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, 0))
|
db.tree.reset(newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, 0)))
|
||||||
db.tree.reset(dl)
|
|
||||||
|
// Re-enable the database as the final step.
|
||||||
|
db.waitSync = false
|
||||||
|
rawdb.WriteSnapSyncStatusFlag(db.diskdb, rawdb.StateSyncFinished)
|
||||||
log.Info("Rebuilt trie database", "root", root)
|
log.Info("Rebuilt trie database", "root", root)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -296,7 +320,10 @@ func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error
|
|||||||
defer db.lock.Unlock()
|
defer db.lock.Unlock()
|
||||||
|
|
||||||
// Short circuit if rollback operation is not supported.
|
// Short circuit if rollback operation is not supported.
|
||||||
if db.readOnly || db.freezer == nil {
|
if err := db.modifyAllowed(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if db.freezer == nil {
|
||||||
return errors.New("state rollback is non-supported")
|
return errors.New("state rollback is non-supported")
|
||||||
}
|
}
|
||||||
// Short circuit if the target state is not recoverable.
|
// Short circuit if the target state is not recoverable.
|
||||||
@ -424,3 +451,15 @@ func (db *Database) SetBufferSize(size int) error {
|
|||||||
func (db *Database) Scheme() string {
|
func (db *Database) Scheme() string {
|
||||||
return rawdb.PathScheme
|
return rawdb.PathScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// modifyAllowed returns the indicator if mutation is allowed. This function
|
||||||
|
// assumes the db.lock is already held.
|
||||||
|
func (db *Database) modifyAllowed() error {
|
||||||
|
if db.readOnly {
|
||||||
|
return errDatabaseReadOnly
|
||||||
|
}
|
||||||
|
if db.waitSync {
|
||||||
|
return errDatabaseWaitSync
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -439,38 +439,39 @@ func TestDatabaseRecoverable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReset(t *testing.T) {
|
func TestDisable(t *testing.T) {
|
||||||
var (
|
tester := newTester(t)
|
||||||
tester = newTester(t)
|
|
||||||
index = tester.bottomIndex()
|
|
||||||
)
|
|
||||||
defer tester.release()
|
defer tester.release()
|
||||||
|
|
||||||
// Reset database to unknown target, should reject it
|
_, stored := rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)
|
||||||
if err := tester.db.Reset(testutil.RandomHash()); err == nil {
|
if err := tester.db.Disable(); err != nil {
|
||||||
t.Fatal("Failed to reject invalid reset")
|
t.Fatal("Failed to deactivate database")
|
||||||
}
|
}
|
||||||
// Reset database to state persisted in the disk
|
if err := tester.db.Enable(types.EmptyRootHash); err == nil {
|
||||||
if err := tester.db.Reset(types.EmptyRootHash); err != nil {
|
t.Fatalf("Invalid activation should be rejected")
|
||||||
t.Fatalf("Failed to reset database %v", err)
|
|
||||||
}
|
}
|
||||||
|
if err := tester.db.Enable(stored); err != nil {
|
||||||
|
t.Fatal("Failed to activate database")
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure journal is deleted from disk
|
// Ensure journal is deleted from disk
|
||||||
if blob := rawdb.ReadTrieJournal(tester.db.diskdb); len(blob) != 0 {
|
if blob := rawdb.ReadTrieJournal(tester.db.diskdb); len(blob) != 0 {
|
||||||
t.Fatal("Failed to clean journal")
|
t.Fatal("Failed to clean journal")
|
||||||
}
|
}
|
||||||
// Ensure all trie histories are removed
|
// Ensure all trie histories are removed
|
||||||
for i := 0; i <= index; i++ {
|
n, err := tester.db.freezer.Ancients()
|
||||||
_, err := readHistory(tester.db.freezer, uint64(i+1))
|
if err != nil {
|
||||||
if err == nil {
|
t.Fatal("Failed to clean state history")
|
||||||
t.Fatalf("Failed to clean state history, index %d", i+1)
|
}
|
||||||
}
|
if n != 0 {
|
||||||
|
t.Fatal("Failed to clean state history")
|
||||||
}
|
}
|
||||||
// Verify layer tree structure, single disk layer is expected
|
// Verify layer tree structure, single disk layer is expected
|
||||||
if tester.db.tree.len() != 1 {
|
if tester.db.tree.len() != 1 {
|
||||||
t.Fatalf("Extra layer kept %d", tester.db.tree.len())
|
t.Fatalf("Extra layer kept %d", tester.db.tree.len())
|
||||||
}
|
}
|
||||||
if tester.db.tree.bottom().rootHash() != types.EmptyRootHash {
|
if tester.db.tree.bottom().rootHash() != stored {
|
||||||
t.Fatalf("Root hash is not matched exp %x got %x", types.EmptyRootHash, tester.db.tree.bottom().rootHash())
|
t.Fatalf("Root hash is not matched exp %x got %x", stored, tester.db.tree.bottom().rootHash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, dept
|
|||||||
if n.Hash != hash {
|
if n.Hash != hash {
|
||||||
dirtyFalseMeter.Mark(1)
|
dirtyFalseMeter.Mark(1)
|
||||||
log.Error("Unexpected trie node in diff layer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
|
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)
|
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))
|
||||||
|
@ -150,7 +150,7 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b
|
|||||||
if nHash != hash {
|
if nHash != hash {
|
||||||
diskFalseMeter.Mark(1)
|
diskFalseMeter.Mark(1)
|
||||||
log.Error("Unexpected trie node in disk", "owner", owner, "path", path, "expect", hash, "got", nHash)
|
log.Error("Unexpected trie node in disk", "owner", owner, "path", path, "expect", hash, "got", nHash)
|
||||||
return nil, newUnexpectedNodeError("disk", hash, nHash, owner, path)
|
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)
|
||||||
|
@ -21,12 +21,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// errSnapshotReadOnly is returned if the database is opened in read only mode
|
// errDatabaseReadOnly is returned if the database is opened in read only mode
|
||||||
// and mutation is requested.
|
// to prevent any mutation.
|
||||||
errSnapshotReadOnly = errors.New("read only")
|
errDatabaseReadOnly = errors.New("read only")
|
||||||
|
|
||||||
|
// errDatabaseWaitSync is returned if the initial state sync is not completed
|
||||||
|
// yet and database is disabled to prevent accessing state.
|
||||||
|
errDatabaseWaitSync = errors.New("waiting for sync")
|
||||||
|
|
||||||
// errSnapshotStale is returned from data accessors if the underlying layer
|
// errSnapshotStale is returned from data accessors if the underlying layer
|
||||||
// layer had been invalidated due to the chain progressing forward far enough
|
// layer had been invalidated due to the chain progressing forward far enough
|
||||||
@ -46,6 +51,10 @@ var (
|
|||||||
errUnexpectedNode = errors.New("unexpected node")
|
errUnexpectedNode = errors.New("unexpected node")
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUnexpectedNodeError(loc string, expHash common.Hash, gotHash common.Hash, owner common.Hash, path []byte) error {
|
func newUnexpectedNodeError(loc string, expHash common.Hash, gotHash common.Hash, owner common.Hash, path []byte, blob []byte) error {
|
||||||
return fmt.Errorf("%w, loc: %s, node: (%x %v), %x!=%x", errUnexpectedNode, loc, owner, path, expHash, gotHash)
|
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)
|
||||||
}
|
}
|
||||||
|
@ -356,7 +356,7 @@ func (db *Database) Journal(root common.Hash) error {
|
|||||||
|
|
||||||
// Short circuit if the database is in read only mode.
|
// Short circuit if the database is in read only mode.
|
||||||
if db.readOnly {
|
if db.readOnly {
|
||||||
return errSnapshotReadOnly
|
return errDatabaseReadOnly
|
||||||
}
|
}
|
||||||
// Firstly write out the metadata of journal
|
// Firstly write out the metadata of journal
|
||||||
journal := new(bytes.Buffer)
|
journal := new(bytes.Buffer)
|
||||||
|
@ -71,7 +71,7 @@ func (b *nodebuffer) node(owner common.Hash, path []byte, hash common.Hash) (*tr
|
|||||||
if n.Hash != hash {
|
if n.Hash != hash {
|
||||||
dirtyFalseMeter.Mark(1)
|
dirtyFalseMeter.Mark(1)
|
||||||
log.Error("Unexpected trie node in node buffer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
|
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)
|
return nil, newUnexpectedNodeError("dirty", hash, n.Hash, owner, path, n.Blob)
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user