mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-18 21:01:38 +00:00
Merge remote-tracking branch 'upstream/MariusVanDerWijden-patch-1'
This commit is contained in:
commit
0190137cb9
29 changed files with 637 additions and 160 deletions
|
|
@ -57,7 +57,7 @@ on how you can run your own `geth` instance.
|
||||||
By far the most common scenario is people wanting to simply interact with the Ethereum
|
By far the most common scenario is people wanting to simply interact with the Ethereum
|
||||||
network: create accounts; transfer funds; deploy and interact with contracts. For this
|
network: create accounts; transfer funds; deploy and interact with contracts. For this
|
||||||
particular use-case the user doesn't care about years-old historical data, so we can
|
particular use-case the user doesn't care about years-old historical data, so we can
|
||||||
fast-sync quickly to the current state of the network. To do so:
|
sync quickly to the current state of the network. To do so:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ geth console
|
$ geth console
|
||||||
|
|
@ -159,7 +159,7 @@ docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
|
||||||
ethereum/client-go
|
ethereum/client-go
|
||||||
```
|
```
|
||||||
|
|
||||||
This will start `geth` in fast-sync mode with a DB memory allowance of 1GB just as the
|
This will start `geth` in snap-sync mode with a DB memory allowance of 1GB just as the
|
||||||
above command does. It will also create a persistent volume in your home directory for
|
above command does. It will also create a persistent volume in your home directory for
|
||||||
saving your blockchain as well as map the default ports. There is also an `alpine` tag
|
saving your blockchain as well as map the default ports. There is also an `alpine` tag
|
||||||
available for a slim version of the image.
|
available for a slim version of the image.
|
||||||
|
|
|
||||||
|
|
@ -77,13 +77,13 @@ func localConsole(ctx *cli.Context) error {
|
||||||
// Create and start the node based on the CLI flags
|
// Create and start the node based on the CLI flags
|
||||||
prepare(ctx)
|
prepare(ctx)
|
||||||
stack, backend := makeFullNode(ctx)
|
stack, backend := makeFullNode(ctx)
|
||||||
startNode(ctx, stack, backend)
|
startNode(ctx, stack, backend, true)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
// Attach to the newly started node and start the JavaScript console
|
// Attach to the newly started node and create the JavaScript console.
|
||||||
client, err := stack.Attach()
|
client, err := stack.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
return fmt.Errorf("Failed to attach to the inproc geth: %v", err)
|
||||||
}
|
}
|
||||||
config := console.Config{
|
config := console.Config{
|
||||||
DataDir: utils.MakeDataDir(ctx),
|
DataDir: utils.MakeDataDir(ctx),
|
||||||
|
|
@ -91,29 +91,34 @@ func localConsole(ctx *cli.Context) error {
|
||||||
Client: client,
|
Client: client,
|
||||||
Preload: utils.MakeConsolePreloads(ctx),
|
Preload: utils.MakeConsolePreloads(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
console, err := console.New(config)
|
console, err := console.New(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
return fmt.Errorf("Failed to start the JavaScript console: %v", err)
|
||||||
}
|
}
|
||||||
defer console.Stop(false)
|
defer console.Stop(false)
|
||||||
|
|
||||||
// If only a short execution was requested, evaluate and return
|
// If only a short execution was requested, evaluate and return.
|
||||||
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
|
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
|
||||||
console.Evaluate(script)
|
console.Evaluate(script)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Otherwise print the welcome screen and enter interactive mode
|
|
||||||
|
// Track node shutdown and stop the console when it goes down.
|
||||||
|
// This happens when SIGTERM is sent to the process.
|
||||||
|
go func() {
|
||||||
|
stack.Wait()
|
||||||
|
console.StopInteractive()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Print the welcome screen and enter interactive mode.
|
||||||
console.Welcome()
|
console.Welcome()
|
||||||
console.Interactive()
|
console.Interactive()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// remoteConsole will connect to a remote geth instance, attaching a JavaScript
|
// remoteConsole will connect to a remote geth instance, attaching a JavaScript
|
||||||
// console to it.
|
// console to it.
|
||||||
func remoteConsole(ctx *cli.Context) error {
|
func remoteConsole(ctx *cli.Context) error {
|
||||||
// Attach to a remotely running geth instance and start the JavaScript console
|
|
||||||
endpoint := ctx.Args().First()
|
endpoint := ctx.Args().First()
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
path := node.DefaultDataDir()
|
path := node.DefaultDataDir()
|
||||||
|
|
@ -150,7 +155,6 @@ func remoteConsole(ctx *cli.Context) error {
|
||||||
Client: client,
|
Client: client,
|
||||||
Preload: utils.MakeConsolePreloads(ctx),
|
Preload: utils.MakeConsolePreloads(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
console, err := console.New(config)
|
console, err := console.New(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
||||||
|
|
@ -165,7 +169,6 @@ func remoteConsole(ctx *cli.Context) error {
|
||||||
// Otherwise print the welcome screen and enter interactive mode
|
// Otherwise print the welcome screen and enter interactive mode
|
||||||
console.Welcome()
|
console.Welcome()
|
||||||
console.Interactive()
|
console.Interactive()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,13 +192,13 @@ func dialRPC(endpoint string) (*rpc.Client, error) {
|
||||||
func ephemeralConsole(ctx *cli.Context) error {
|
func ephemeralConsole(ctx *cli.Context) error {
|
||||||
// Create and start the node based on the CLI flags
|
// Create and start the node based on the CLI flags
|
||||||
stack, backend := makeFullNode(ctx)
|
stack, backend := makeFullNode(ctx)
|
||||||
startNode(ctx, stack, backend)
|
startNode(ctx, stack, backend, false)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
// Attach to the newly started node and start the JavaScript console
|
// Attach to the newly started node and start the JavaScript console
|
||||||
client, err := stack.Attach()
|
client, err := stack.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
return fmt.Errorf("Failed to attach to the inproc geth: %v", err)
|
||||||
}
|
}
|
||||||
config := console.Config{
|
config := console.Config{
|
||||||
DataDir: utils.MakeDataDir(ctx),
|
DataDir: utils.MakeDataDir(ctx),
|
||||||
|
|
@ -206,22 +209,24 @@ func ephemeralConsole(ctx *cli.Context) error {
|
||||||
|
|
||||||
console, err := console.New(config)
|
console, err := console.New(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to start the JavaScript console: %v", err)
|
return fmt.Errorf("Failed to start the JavaScript console: %v", err)
|
||||||
}
|
}
|
||||||
defer console.Stop(false)
|
defer console.Stop(false)
|
||||||
|
|
||||||
// Evaluate each of the specified JavaScript files
|
// Interrupt the JS interpreter when node is stopped.
|
||||||
for _, file := range ctx.Args() {
|
|
||||||
if err = console.Execute(file); err != nil {
|
|
||||||
utils.Fatalf("Failed to execute %s: %v", file, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
stack.Wait()
|
stack.Wait()
|
||||||
console.Stop(false)
|
console.Stop(false)
|
||||||
}()
|
}()
|
||||||
console.Stop(true)
|
|
||||||
|
|
||||||
|
// Evaluate each of the specified JavaScript files.
|
||||||
|
for _, file := range ctx.Args() {
|
||||||
|
if err = console.Execute(file); err != nil {
|
||||||
|
return fmt.Errorf("Failed to execute %s: %v", file, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main script is now done, but keep running timers/callbacks.
|
||||||
|
console.Stop(true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ func geth(ctx *cli.Context) error {
|
||||||
stack, backend := makeFullNode(ctx)
|
stack, backend := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
startNode(ctx, stack, backend)
|
startNode(ctx, stack, backend, false)
|
||||||
stack.Wait()
|
stack.Wait()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -328,11 +328,11 @@ func geth(ctx *cli.Context) error {
|
||||||
// startNode boots up the system node and all registered protocols, after which
|
// startNode boots up the system node and all registered protocols, after which
|
||||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||||
// miner.
|
// miner.
|
||||||
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
|
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) {
|
||||||
debug.Memsize.Add("node", stack)
|
debug.Memsize.Add("node", stack)
|
||||||
|
|
||||||
// Start up the node itself
|
// Start up the node itself
|
||||||
utils.StartNode(ctx, stack)
|
utils.StartNode(ctx, stack, isConsole)
|
||||||
|
|
||||||
// Unlock any account specifically requested
|
// Unlock any account specifically requested
|
||||||
unlockAccounts(ctx, stack)
|
unlockAccounts(ctx, stack)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func Fatalf(format string, args ...interface{}) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartNode(ctx *cli.Context, stack *node.Node) {
|
func StartNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
||||||
if err := stack.Start(); err != nil {
|
if err := stack.Start(); err != nil {
|
||||||
Fatalf("Error starting protocol stack: %v", err)
|
Fatalf("Error starting protocol stack: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -87,17 +87,33 @@ func StartNode(ctx *cli.Context, stack *node.Node) {
|
||||||
go monitorFreeDiskSpace(sigc, stack.InstanceDir(), uint64(minFreeDiskSpace)*1024*1024)
|
go monitorFreeDiskSpace(sigc, stack.InstanceDir(), uint64(minFreeDiskSpace)*1024*1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
<-sigc
|
shutdown := func() {
|
||||||
log.Info("Got interrupt, shutting down...")
|
log.Info("Got interrupt, shutting down...")
|
||||||
go stack.Close()
|
go stack.Close()
|
||||||
for i := 10; i > 0; i-- {
|
for i := 10; i > 0; i-- {
|
||||||
<-sigc
|
<-sigc
|
||||||
if i > 1 {
|
if i > 1 {
|
||||||
log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
|
log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
debug.Exit() // ensure trace and CPU profile data is flushed.
|
||||||
|
debug.LoudPanic("boom")
|
||||||
|
}
|
||||||
|
|
||||||
|
if isConsole {
|
||||||
|
// In JS console mode, SIGINT is ignored because it's handled by the console.
|
||||||
|
// However, SIGTERM still shuts down the node.
|
||||||
|
for {
|
||||||
|
sig := <-sigc
|
||||||
|
if sig == syscall.SIGTERM {
|
||||||
|
shutdown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
<-sigc
|
||||||
|
shutdown()
|
||||||
}
|
}
|
||||||
debug.Exit() // ensure trace and CPU profile data is flushed.
|
|
||||||
debug.LoudPanic("boom")
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"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/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
@ -196,9 +197,8 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
||||||
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, beaconDifficulty)
|
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, beaconDifficulty)
|
||||||
}
|
}
|
||||||
// Verify that the gas limit is <= 2^63-1
|
// Verify that the gas limit is <= 2^63-1
|
||||||
cap := uint64(0x7fffffffffffffff)
|
if header.GasLimit > params.MaxGasLimit {
|
||||||
if header.GasLimit > cap {
|
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
||||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
|
|
||||||
}
|
}
|
||||||
// Verify that the gasUsed is <= gasLimit
|
// Verify that the gasUsed is <= gasLimit
|
||||||
if header.GasUsed > header.GasLimit {
|
if header.GasUsed > header.GasLimit {
|
||||||
|
|
|
||||||
|
|
@ -295,9 +295,8 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Verify that the gas limit is <= 2^63-1
|
// Verify that the gas limit is <= 2^63-1
|
||||||
cap := uint64(0x7fffffffffffffff)
|
if header.GasLimit > params.MaxGasLimit {
|
||||||
if header.GasLimit > cap {
|
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
||||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
|
|
||||||
}
|
}
|
||||||
// If all checks passed, validate any special fields for hard forks
|
// If all checks passed, validate any special fields for hard forks
|
||||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -281,9 +281,8 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
||||||
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
|
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
|
||||||
}
|
}
|
||||||
// Verify that the gas limit is <= 2^63-1
|
// Verify that the gas limit is <= 2^63-1
|
||||||
cap := uint64(0x7fffffffffffffff)
|
if header.GasLimit > params.MaxGasLimit {
|
||||||
if header.GasLimit > cap {
|
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
||||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
|
|
||||||
}
|
}
|
||||||
// Verify that the gasUsed is <= gasLimit
|
// Verify that the gasUsed is <= gasLimit
|
||||||
if header.GasUsed > header.GasLimit {
|
if header.GasUsed > header.GasLimit {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package console
|
package console
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -26,6 +27,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
|
|
@ -74,6 +76,13 @@ type Console struct {
|
||||||
histPath string // Absolute path to the console scrollback history
|
histPath string // Absolute path to the console scrollback history
|
||||||
history []string // Scroll history maintained by the console
|
history []string // Scroll history maintained by the console
|
||||||
printer io.Writer // Output writer to serialize any display strings to
|
printer io.Writer // Output writer to serialize any display strings to
|
||||||
|
|
||||||
|
interactiveStopped chan struct{}
|
||||||
|
stopInteractiveCh chan struct{}
|
||||||
|
signalReceived chan struct{}
|
||||||
|
stopped chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
stopOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a JavaScript interpreted runtime environment and sets defaults
|
// New initializes a JavaScript interpreted runtime environment and sets defaults
|
||||||
|
|
@ -92,12 +101,16 @@ func New(config Config) (*Console, error) {
|
||||||
|
|
||||||
// Initialize the console and return
|
// Initialize the console and return
|
||||||
console := &Console{
|
console := &Console{
|
||||||
client: config.Client,
|
client: config.Client,
|
||||||
jsre: jsre.New(config.DocRoot, config.Printer),
|
jsre: jsre.New(config.DocRoot, config.Printer),
|
||||||
prompt: config.Prompt,
|
prompt: config.Prompt,
|
||||||
prompter: config.Prompter,
|
prompter: config.Prompter,
|
||||||
printer: config.Printer,
|
printer: config.Printer,
|
||||||
histPath: filepath.Join(config.DataDir, HistoryFile),
|
histPath: filepath.Join(config.DataDir, HistoryFile),
|
||||||
|
interactiveStopped: make(chan struct{}),
|
||||||
|
stopInteractiveCh: make(chan struct{}),
|
||||||
|
signalReceived: make(chan struct{}, 1),
|
||||||
|
stopped: make(chan struct{}),
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(config.DataDir, 0700); err != nil {
|
if err := os.MkdirAll(config.DataDir, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -105,6 +118,10 @@ func New(config Config) (*Console, error) {
|
||||||
if err := console.init(config.Preload); err != nil {
|
if err := console.init(config.Preload); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.wg.Add(1)
|
||||||
|
go console.interruptHandler()
|
||||||
|
|
||||||
return console, nil
|
return console, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,9 +354,63 @@ func (c *Console) Evaluate(statement string) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
c.jsre.Evaluate(statement, c.printer)
|
c.jsre.Evaluate(statement, c.printer)
|
||||||
|
|
||||||
|
// Avoid exiting Interactive when jsre was interrupted by SIGINT.
|
||||||
|
c.clearSignalReceived()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interactive starts an interactive user session, where input is propted from
|
// interruptHandler runs in its own goroutine and waits for signals.
|
||||||
|
// When a signal is received, it interrupts the JS interpreter.
|
||||||
|
func (c *Console) interruptHandler() {
|
||||||
|
defer c.wg.Done()
|
||||||
|
|
||||||
|
// During Interactive, liner inhibits the signal while it is prompting for
|
||||||
|
// input. However, the signal will be received while evaluating JS.
|
||||||
|
//
|
||||||
|
// On unsupported terminals, SIGINT can also happen while prompting.
|
||||||
|
// Unfortunately, it is not possible to abort the prompt in this case and
|
||||||
|
// the c.readLines goroutine leaks.
|
||||||
|
sig := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sig, syscall.SIGINT)
|
||||||
|
defer signal.Stop(sig)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-sig:
|
||||||
|
c.setSignalReceived()
|
||||||
|
c.jsre.Interrupt(errors.New("interrupted"))
|
||||||
|
case <-c.stopInteractiveCh:
|
||||||
|
close(c.interactiveStopped)
|
||||||
|
c.jsre.Interrupt(errors.New("interrupted"))
|
||||||
|
case <-c.stopped:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Console) setSignalReceived() {
|
||||||
|
select {
|
||||||
|
case c.signalReceived <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Console) clearSignalReceived() {
|
||||||
|
select {
|
||||||
|
case <-c.signalReceived:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopInteractive causes Interactive to return as soon as possible.
|
||||||
|
func (c *Console) StopInteractive() {
|
||||||
|
select {
|
||||||
|
case c.stopInteractiveCh <- struct{}{}:
|
||||||
|
case <-c.stopped:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive starts an interactive user session, where in.put is propted from
|
||||||
// the configured user prompter.
|
// the configured user prompter.
|
||||||
func (c *Console) Interactive() {
|
func (c *Console) Interactive() {
|
||||||
var (
|
var (
|
||||||
|
|
@ -349,15 +420,11 @@ func (c *Console) Interactive() {
|
||||||
inputLine = make(chan string, 1) // receives user input
|
inputLine = make(chan string, 1) // receives user input
|
||||||
inputErr = make(chan error, 1) // receives liner errors
|
inputErr = make(chan error, 1) // receives liner errors
|
||||||
requestLine = make(chan string) // requests a line of input
|
requestLine = make(chan string) // requests a line of input
|
||||||
interrupt = make(chan os.Signal, 1)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Monitor Ctrl-C. While liner does turn on the relevant terminal mode bits to avoid
|
defer func() {
|
||||||
// the signal, a signal can still be received for unsupported terminals. Unfortunately
|
c.writeHistory()
|
||||||
// there is no way to cancel the line reader when this happens. The readLines
|
}()
|
||||||
// goroutine will be leaked in this case.
|
|
||||||
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
defer signal.Stop(interrupt)
|
|
||||||
|
|
||||||
// The line reader runs in a separate goroutine.
|
// The line reader runs in a separate goroutine.
|
||||||
go c.readLines(inputLine, inputErr, requestLine)
|
go c.readLines(inputLine, inputErr, requestLine)
|
||||||
|
|
@ -368,7 +435,14 @@ func (c *Console) Interactive() {
|
||||||
requestLine <- prompt
|
requestLine <- prompt
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-interrupt:
|
case <-c.interactiveStopped:
|
||||||
|
fmt.Fprintln(c.printer, "node is down, exiting console")
|
||||||
|
return
|
||||||
|
|
||||||
|
case <-c.signalReceived:
|
||||||
|
// SIGINT received while prompting for input -> unsupported terminal.
|
||||||
|
// I'm not sure if the best choice would be to leave the console running here.
|
||||||
|
// Bash keeps running in this case. node.js does not.
|
||||||
fmt.Fprintln(c.printer, "caught interrupt, exiting")
|
fmt.Fprintln(c.printer, "caught interrupt, exiting")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -476,12 +550,19 @@ func (c *Console) Execute(path string) error {
|
||||||
|
|
||||||
// Stop cleans up the console and terminates the runtime environment.
|
// Stop cleans up the console and terminates the runtime environment.
|
||||||
func (c *Console) Stop(graceful bool) error {
|
func (c *Console) Stop(graceful bool) error {
|
||||||
if err := ioutil.WriteFile(c.histPath, []byte(strings.Join(c.history, "\n")), 0600); err != nil {
|
c.stopOnce.Do(func() {
|
||||||
return err
|
// Stop the interrupt handler.
|
||||||
}
|
close(c.stopped)
|
||||||
if err := os.Chmod(c.histPath, 0600); err != nil { // Force 0600, even if it was different previously
|
c.wg.Wait()
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
c.jsre.Stop(graceful)
|
c.jsre.Stop(graceful)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Console) writeHistory() error {
|
||||||
|
if err := ioutil.WriteFile(c.histPath, []byte(strings.Join(c.history, "\n")), 0600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chmod(c.histPath, 0600) // Force 0600, even if it was different previously
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,12 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||||
return bc.hc.GetHeaderByNumber(number)
|
return bc.hc.GetHeaderByNumber(number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going
|
||||||
|
// backwards from the given number.
|
||||||
|
func (bc *BlockChain) GetHeadersFrom(number, count uint64) []rlp.RawValue {
|
||||||
|
return bc.hc.GetHeadersFrom(number, count)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBody retrieves a block body (transactions and uncles) from the database by
|
// GetBody retrieves a block body (transactions and uncles) from the database by
|
||||||
// hash, caching it if found.
|
// hash, caching it if found.
|
||||||
func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
|
func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"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/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -498,6 +499,46 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||||
return hc.GetHeader(hash, number)
|
return hc.GetHeader(hash, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going
|
||||||
|
// backwards from the given number.
|
||||||
|
// If the 'number' is higher than the highest local header, this method will
|
||||||
|
// return a best-effort response, containing the headers that we do have.
|
||||||
|
func (hc *HeaderChain) GetHeadersFrom(number, count uint64) []rlp.RawValue {
|
||||||
|
// If the request is for future headers, we still return the portion of
|
||||||
|
// headers that we are able to serve
|
||||||
|
if current := hc.CurrentHeader().Number.Uint64(); current < number {
|
||||||
|
if count > number-current {
|
||||||
|
count -= number - current
|
||||||
|
number = current
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var headers []rlp.RawValue
|
||||||
|
// If we have some of the headers in cache already, use that before going to db.
|
||||||
|
hash := rawdb.ReadCanonicalHash(hc.chainDb, number)
|
||||||
|
if hash == (common.Hash{}) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for count > 0 {
|
||||||
|
header, ok := hc.headerCache.Get(hash)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
h := header.(*types.Header)
|
||||||
|
rlpData, _ := rlp.EncodeToBytes(h)
|
||||||
|
headers = append(headers, rlpData)
|
||||||
|
hash = h.ParentHash
|
||||||
|
count--
|
||||||
|
number--
|
||||||
|
}
|
||||||
|
// Read remaining from db
|
||||||
|
if count > 0 {
|
||||||
|
headers = append(headers, rawdb.ReadHeaderRange(hc.chainDb, number, count)...)
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash {
|
func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash {
|
||||||
return rawdb.ReadCanonicalHash(hc.chainDb, number)
|
return rawdb.ReadCanonicalHash(hc.chainDb, number)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,56 @@ func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadHeaderRange returns the rlp-encoded headers, starting at 'number', and going
|
||||||
|
// backwards towards genesis. This method assumes that the caller already has
|
||||||
|
// placed a cap on count, to prevent DoS issues.
|
||||||
|
// Since this method operates in head-towards-genesis mode, it will return an empty
|
||||||
|
// slice in case the head ('number') is missing. Hence, the caller must ensure that
|
||||||
|
// the head ('number') argument is actually an existing header.
|
||||||
|
//
|
||||||
|
// N.B: Since the input is a number, as opposed to a hash, it's implicit that
|
||||||
|
// this method only operates on canon headers.
|
||||||
|
func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValue {
|
||||||
|
var rlpHeaders []rlp.RawValue
|
||||||
|
if count == 0 {
|
||||||
|
return rlpHeaders
|
||||||
|
}
|
||||||
|
i := number
|
||||||
|
if count-1 > number {
|
||||||
|
// It's ok to request block 0, 1 item
|
||||||
|
count = number + 1
|
||||||
|
}
|
||||||
|
limit, _ := db.Ancients()
|
||||||
|
// First read live blocks
|
||||||
|
if i >= limit {
|
||||||
|
// If we need to read live blocks, we need to figure out the hash first
|
||||||
|
hash := ReadCanonicalHash(db, number)
|
||||||
|
for ; i >= limit && count > 0; i-- {
|
||||||
|
if data, _ := db.Get(headerKey(i, hash)); len(data) > 0 {
|
||||||
|
rlpHeaders = append(rlpHeaders, data)
|
||||||
|
// Get the parent hash for next query
|
||||||
|
hash = types.HeaderParentHashFromRLP(data)
|
||||||
|
} else {
|
||||||
|
break // Maybe got moved to ancients
|
||||||
|
}
|
||||||
|
count--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return rlpHeaders
|
||||||
|
}
|
||||||
|
// read remaining from ancients
|
||||||
|
max := count * 700
|
||||||
|
data, err := db.AncientRange(freezerHeaderTable, i+1-count, count, max)
|
||||||
|
if err == nil && uint64(len(data)) == count {
|
||||||
|
// the data is on the order [h, h+1, .., n] -- reordering needed
|
||||||
|
for i := range data {
|
||||||
|
rlpHeaders = append(rlpHeaders, data[len(data)-1-i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rlpHeaders
|
||||||
|
}
|
||||||
|
|
||||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|
@ -664,7 +714,7 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.C
|
||||||
if logs := readLegacyLogs(db, hash, number, config); logs != nil {
|
if logs := readLegacyLogs(db, hash, number, config); logs != nil {
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
log.Error("Invalid receipt array RLP", "hash", "err", err)
|
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -883,3 +883,67 @@ func BenchmarkDecodeRLPLogs(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHeadersRLPStorage(t *testing.T) {
|
||||||
|
// Have N headers in the freezer
|
||||||
|
frdir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temp freezer dir: %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(frdir)
|
||||||
|
|
||||||
|
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
// Create blocks
|
||||||
|
var chain []*types.Block
|
||||||
|
var pHash common.Hash
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
block := types.NewBlockWithHeader(&types.Header{
|
||||||
|
Number: big.NewInt(int64(i)),
|
||||||
|
Extra: []byte("test block"),
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
TxHash: types.EmptyRootHash,
|
||||||
|
ReceiptHash: types.EmptyRootHash,
|
||||||
|
ParentHash: pHash,
|
||||||
|
})
|
||||||
|
chain = append(chain, block)
|
||||||
|
pHash = block.Hash()
|
||||||
|
}
|
||||||
|
var receipts []types.Receipts = make([]types.Receipts, 100)
|
||||||
|
// Write first half to ancients
|
||||||
|
WriteAncientBlocks(db, chain[:50], receipts[:50], big.NewInt(100))
|
||||||
|
// Write second half to db
|
||||||
|
for i := 50; i < 100; i++ {
|
||||||
|
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
||||||
|
WriteBlock(db, chain[i])
|
||||||
|
}
|
||||||
|
checkSequence := func(from, amount int) {
|
||||||
|
headersRlp := ReadHeaderRange(db, uint64(from), uint64(amount))
|
||||||
|
if have, want := len(headersRlp), amount; have != want {
|
||||||
|
t.Fatalf("have %d headers, want %d", have, want)
|
||||||
|
}
|
||||||
|
for i, headerRlp := range headersRlp {
|
||||||
|
var header types.Header
|
||||||
|
if err := rlp.DecodeBytes(headerRlp, &header); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if have, want := header.Number.Uint64(), uint64(from-i); have != want {
|
||||||
|
t.Fatalf("wrong number, have %d want %d", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkSequence(99, 20) // Latest block and 19 parents
|
||||||
|
checkSequence(99, 50) // Latest block -> all db blocks
|
||||||
|
checkSequence(99, 51) // Latest block -> one from ancients
|
||||||
|
checkSequence(99, 52) // Latest blocks -> two from ancients
|
||||||
|
checkSequence(50, 2) // One from db, one from ancients
|
||||||
|
checkSequence(49, 1) // One from ancients
|
||||||
|
checkSequence(49, 50) // All ancient ones
|
||||||
|
checkSequence(99, 100) // All blocks
|
||||||
|
checkSequence(0, 1) // Only genesis
|
||||||
|
checkSequence(1, 1) // Only block 1
|
||||||
|
checkSequence(1, 2) // Genesis + block 1
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -389,3 +389,21 @@ func (b *Block) Hash() common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Blocks []*Block
|
type Blocks []*Block
|
||||||
|
|
||||||
|
// HeaderParentHashFromRLP returns the parentHash of an RLP-encoded
|
||||||
|
// header. If 'header' is invalid, the zero hash is returned.
|
||||||
|
func HeaderParentHashFromRLP(header []byte) common.Hash {
|
||||||
|
// parentHash is the first list element.
|
||||||
|
listContent, _, err := rlp.SplitList(header)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
parentHash, _, err := rlp.SplitString(listContent)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
if len(parentHash) != 32 {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
return common.BytesToHash(parentHash)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -281,3 +281,64 @@ func makeBenchBlock() *Block {
|
||||||
}
|
}
|
||||||
return NewBlock(header, txs, uncles, receipts, newHasher())
|
return NewBlock(header, txs, uncles, receipts, newHasher())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRlpDecodeParentHash(t *testing.T) {
|
||||||
|
// A minimum one
|
||||||
|
want := common.HexToHash("0x112233445566778899001122334455667788990011223344556677889900aabb")
|
||||||
|
if rlpData, err := rlp.EncodeToBytes(Header{ParentHash: want}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
if have := HeaderParentHashFromRLP(rlpData); have != want {
|
||||||
|
t.Fatalf("have %x, want %x", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And a maximum one
|
||||||
|
// | Difficulty | dynamic| *big.Int | 0x5ad3c2c71bbff854908 (current mainnet TD: 76 bits) |
|
||||||
|
// | Number | dynamic| *big.Int | 64 bits |
|
||||||
|
// | Extra | dynamic| []byte | 65+32 byte (clique) |
|
||||||
|
// | BaseFee | dynamic| *big.Int | 64 bits |
|
||||||
|
mainnetTd := new(big.Int)
|
||||||
|
mainnetTd.SetString("5ad3c2c71bbff854908", 16)
|
||||||
|
if rlpData, err := rlp.EncodeToBytes(Header{
|
||||||
|
ParentHash: want,
|
||||||
|
Difficulty: mainnetTd,
|
||||||
|
Number: new(big.Int).SetUint64(math.MaxUint64),
|
||||||
|
Extra: make([]byte, 65+32),
|
||||||
|
BaseFee: new(big.Int).SetUint64(math.MaxUint64),
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
if have := HeaderParentHashFromRLP(rlpData); have != want {
|
||||||
|
t.Fatalf("have %x, want %x", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Also test a very very large header.
|
||||||
|
{
|
||||||
|
// The rlp-encoding of the heder belowCauses _total_ length of 65540,
|
||||||
|
// which is the first to blow the fast-path.
|
||||||
|
h := Header{
|
||||||
|
ParentHash: want,
|
||||||
|
Extra: make([]byte, 65041),
|
||||||
|
}
|
||||||
|
if rlpData, err := rlp.EncodeToBytes(h); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
if have := HeaderParentHashFromRLP(rlpData); have != want {
|
||||||
|
t.Fatalf("have %x, want %x", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Test some invalid erroneous stuff
|
||||||
|
for i, rlpData := range [][]byte{
|
||||||
|
nil,
|
||||||
|
common.FromHex("0x"),
|
||||||
|
common.FromHex("0x01"),
|
||||||
|
common.FromHex("0x3031323334"),
|
||||||
|
} {
|
||||||
|
if have, want := HeaderParentHashFromRLP(rlpData), (common.Hash{}); have != want {
|
||||||
|
t.Fatalf("invalid %d: have %x, want %x", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,62 +181,56 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
// Capture pre-execution values for tracing.
|
// Capture pre-execution values for tracing.
|
||||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operation from the jump table and validate the stack to ensure there are
|
// Get the operation from the jump table and validate the stack to ensure there are
|
||||||
// enough stack items available to perform the operation.
|
// enough stack items available to perform the operation.
|
||||||
op = contract.GetOp(pc)
|
op = contract.GetOp(pc)
|
||||||
operation := in.cfg.JumpTable[op]
|
operation := in.cfg.JumpTable[op]
|
||||||
|
cost = operation.constantGas // For tracing
|
||||||
// Validate stack
|
// Validate stack
|
||||||
if sLen := stack.len(); sLen < operation.minStack {
|
if sLen := stack.len(); sLen < operation.minStack {
|
||||||
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
|
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
|
||||||
} else if sLen > operation.maxStack {
|
} else if sLen > operation.maxStack {
|
||||||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||||
}
|
}
|
||||||
// Static portion of gas
|
if !contract.UseGas(cost) {
|
||||||
cost = operation.constantGas // For tracing
|
|
||||||
if !contract.UseGas(operation.constantGas) {
|
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
var memorySize uint64
|
|
||||||
// calculate the new memory size and expand the memory to fit
|
|
||||||
// the operation
|
|
||||||
// Memory check needs to be done prior to evaluating the dynamic gas portion,
|
|
||||||
// to detect calculation overflows
|
|
||||||
if operation.memorySize != nil {
|
|
||||||
memSize, overflow := operation.memorySize(stack)
|
|
||||||
if overflow {
|
|
||||||
return nil, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
// memory is expanded in words of 32 bytes. Gas
|
|
||||||
// is also calculated in words.
|
|
||||||
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
|
|
||||||
return nil, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Dynamic portion of gas
|
|
||||||
// consume the gas and return an error if not enough gas is available.
|
|
||||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
|
||||||
if operation.dynamicGas != nil {
|
if operation.dynamicGas != nil {
|
||||||
|
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
||||||
|
var memorySize uint64
|
||||||
|
// calculate the new memory size and expand the memory to fit
|
||||||
|
// the operation
|
||||||
|
// Memory check needs to be done prior to evaluating the dynamic gas portion,
|
||||||
|
// to detect calculation overflows
|
||||||
|
if operation.memorySize != nil {
|
||||||
|
memSize, overflow := operation.memorySize(stack)
|
||||||
|
if overflow {
|
||||||
|
return nil, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
// memory is expanded in words of 32 bytes. Gas
|
||||||
|
// is also calculated in words.
|
||||||
|
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
|
||||||
|
return nil, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Consume the gas and return an error if not enough gas is available.
|
||||||
|
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||||
var dynamicCost uint64
|
var dynamicCost uint64
|
||||||
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
||||||
cost += dynamicCost // total cost, for debug tracing
|
cost += dynamicCost // for tracing
|
||||||
if err != nil || !contract.UseGas(dynamicCost) {
|
if err != nil || !contract.UseGas(dynamicCost) {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
if memorySize > 0 {
|
||||||
|
mem.Resize(memorySize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if memorySize > 0 {
|
|
||||||
mem.Resize(memorySize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.cfg.Debug {
|
if in.cfg.Debug {
|
||||||
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||||
logged = true
|
logged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the operation
|
// execute the operation
|
||||||
res, err = operation.execute(&pc, in, callContext)
|
res, err = operation.execute(&pc, in, callContext)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -57,13 +59,31 @@ var (
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
type JumpTable [256]*operation
|
type JumpTable [256]*operation
|
||||||
|
|
||||||
|
func validate(jt JumpTable) JumpTable {
|
||||||
|
for i, op := range jt {
|
||||||
|
if op == nil {
|
||||||
|
panic(fmt.Sprintf("op 0x%x is not set", i))
|
||||||
|
}
|
||||||
|
// The interpreter has an assumption that if the memorySize function is
|
||||||
|
// set, then the dynamicGas function is also set. This is a somewhat
|
||||||
|
// arbitrary assumption, and can be removed if we need to -- but it
|
||||||
|
// allows us to avoid a condition check. As long as we have that assumption
|
||||||
|
// in there, this little sanity check prevents us from merging in a
|
||||||
|
// change which violates it.
|
||||||
|
if op.memorySize != nil && op.dynamicGas == nil {
|
||||||
|
panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jt
|
||||||
|
}
|
||||||
|
|
||||||
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// contantinople, istanbul, petersburg, berlin and london instructions.
|
// contantinople, istanbul, petersburg, berlin and london instructions.
|
||||||
func newLondonInstructionSet() JumpTable {
|
func newLondonInstructionSet() JumpTable {
|
||||||
instructionSet := newBerlinInstructionSet()
|
instructionSet := newBerlinInstructionSet()
|
||||||
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
||||||
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
|
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
|
||||||
|
|
@ -71,7 +91,7 @@ func newLondonInstructionSet() JumpTable {
|
||||||
func newBerlinInstructionSet() JumpTable {
|
func newBerlinInstructionSet() JumpTable {
|
||||||
instructionSet := newIstanbulInstructionSet()
|
instructionSet := newIstanbulInstructionSet()
|
||||||
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
|
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
|
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
|
||||||
|
|
@ -83,7 +103,7 @@ func newIstanbulInstructionSet() JumpTable {
|
||||||
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
|
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
|
||||||
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
|
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
|
||||||
|
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newConstantinopleInstructionSet returns the frontier, homestead,
|
// newConstantinopleInstructionSet returns the frontier, homestead,
|
||||||
|
|
@ -122,7 +142,7 @@ func newConstantinopleInstructionSet() JumpTable {
|
||||||
maxStack: maxStack(4, 1),
|
maxStack: maxStack(4, 1),
|
||||||
memorySize: memoryCreate2,
|
memorySize: memoryCreate2,
|
||||||
}
|
}
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newByzantiumInstructionSet returns the frontier, homestead and
|
// newByzantiumInstructionSet returns the frontier, homestead and
|
||||||
|
|
@ -158,14 +178,14 @@ func newByzantiumInstructionSet() JumpTable {
|
||||||
maxStack: maxStack(2, 0),
|
maxStack: maxStack(2, 0),
|
||||||
memorySize: memoryRevert,
|
memorySize: memoryRevert,
|
||||||
}
|
}
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EIP 158 a.k.a Spurious Dragon
|
// EIP 158 a.k.a Spurious Dragon
|
||||||
func newSpuriousDragonInstructionSet() JumpTable {
|
func newSpuriousDragonInstructionSet() JumpTable {
|
||||||
instructionSet := newTangerineWhistleInstructionSet()
|
instructionSet := newTangerineWhistleInstructionSet()
|
||||||
instructionSet[EXP].dynamicGas = gasExpEIP158
|
instructionSet[EXP].dynamicGas = gasExpEIP158
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,7 +199,7 @@ func newTangerineWhistleInstructionSet() JumpTable {
|
||||||
instructionSet[CALL].constantGas = params.CallGasEIP150
|
instructionSet[CALL].constantGas = params.CallGasEIP150
|
||||||
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
|
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
|
||||||
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
|
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHomesteadInstructionSet returns the frontier and homestead
|
// newHomesteadInstructionSet returns the frontier and homestead
|
||||||
|
|
@ -194,7 +214,7 @@ func newHomesteadInstructionSet() JumpTable {
|
||||||
maxStack: maxStack(6, 1),
|
maxStack: maxStack(6, 1),
|
||||||
memorySize: memoryDelegateCall,
|
memorySize: memoryDelegateCall,
|
||||||
}
|
}
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFrontierInstructionSet returns the frontier instructions
|
// newFrontierInstructionSet returns the frontier instructions
|
||||||
|
|
@ -1010,5 +1030,5 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tbl
|
return validate(tbl)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,6 @@ func (op OpCode) IsPush() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStaticJump specifies if an opcode is JUMP.
|
|
||||||
func (op OpCode) IsStaticJump() bool {
|
|
||||||
return op == JUMP
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0x0 range - arithmetic ops.
|
// 0x0 range - arithmetic ops.
|
||||||
const (
|
const (
|
||||||
STOP OpCode = 0x0
|
STOP OpCode = 0x0
|
||||||
|
|
|
||||||
|
|
@ -154,12 +154,24 @@ func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int) {
|
||||||
return head.Hash(), dlp.chain.GetTd(head.Hash(), head.NumberU64())
|
return head.Hash(), dlp.chain.GetTd(head.Hash(), head.NumberU64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unmarshalRlpHeaders(rlpdata []rlp.RawValue) []*types.Header {
|
||||||
|
var headers = make([]*types.Header, len(rlpdata))
|
||||||
|
for i, data := range rlpdata {
|
||||||
|
var h types.Header
|
||||||
|
if err := rlp.DecodeBytes(data, &h); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
headers[i] = &h
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
// RequestHeadersByHash constructs a GetBlockHeaders function based on a hashed
|
// RequestHeadersByHash constructs a GetBlockHeaders function based on a hashed
|
||||||
// origin; associated with a particular peer in the download tester. The returned
|
// origin; associated with a particular peer in the download tester. The returned
|
||||||
// function can be used to retrieve batches of headers from the particular peer.
|
// function can be used to retrieve batches of headers from the particular peer.
|
||||||
func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) {
|
func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) {
|
||||||
// Service the header query via the live handler code
|
// Service the header query via the live handler code
|
||||||
headers := eth.ServiceGetBlockHeadersQuery(dlp.chain, ð.GetBlockHeadersPacket{
|
rlpHeaders := eth.ServiceGetBlockHeadersQuery(dlp.chain, ð.GetBlockHeadersPacket{
|
||||||
Origin: eth.HashOrNumber{
|
Origin: eth.HashOrNumber{
|
||||||
Hash: origin,
|
Hash: origin,
|
||||||
},
|
},
|
||||||
|
|
@ -167,7 +179,7 @@ func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount i
|
||||||
Skip: uint64(skip),
|
Skip: uint64(skip),
|
||||||
Reverse: reverse,
|
Reverse: reverse,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
headers := unmarshalRlpHeaders(rlpHeaders)
|
||||||
// If a malicious peer is simulated withholding headers, delete them
|
// If a malicious peer is simulated withholding headers, delete them
|
||||||
for hash := range dlp.withholdHeaders {
|
for hash := range dlp.withholdHeaders {
|
||||||
for i, header := range headers {
|
for i, header := range headers {
|
||||||
|
|
@ -203,7 +215,7 @@ func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount i
|
||||||
// function can be used to retrieve batches of headers from the particular peer.
|
// function can be used to retrieve batches of headers from the particular peer.
|
||||||
func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) {
|
func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) {
|
||||||
// Service the header query via the live handler code
|
// Service the header query via the live handler code
|
||||||
headers := eth.ServiceGetBlockHeadersQuery(dlp.chain, ð.GetBlockHeadersPacket{
|
rlpHeaders := eth.ServiceGetBlockHeadersQuery(dlp.chain, ð.GetBlockHeadersPacket{
|
||||||
Origin: eth.HashOrNumber{
|
Origin: eth.HashOrNumber{
|
||||||
Number: origin,
|
Number: origin,
|
||||||
},
|
},
|
||||||
|
|
@ -211,7 +223,7 @@ func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int,
|
||||||
Skip: uint64(skip),
|
Skip: uint64(skip),
|
||||||
Reverse: reverse,
|
Reverse: reverse,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
headers := unmarshalRlpHeaders(rlpHeaders)
|
||||||
// If a malicious peer is simulated withholding headers, delete them
|
// If a malicious peer is simulated withholding headers, delete them
|
||||||
for hash := range dlp.withholdHeaders {
|
for hash := range dlp.withholdHeaders {
|
||||||
for i, header := range headers {
|
for i, header := range headers {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"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/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
@ -51,7 +50,6 @@ type PublicFilterAPI struct {
|
||||||
backend Backend
|
backend Backend
|
||||||
mux *event.TypeMux
|
mux *event.TypeMux
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
chainDb ethdb.Database
|
|
||||||
events *EventSystem
|
events *EventSystem
|
||||||
filtersMu sync.Mutex
|
filtersMu sync.Mutex
|
||||||
filters map[rpc.ID]*filter
|
filters map[rpc.ID]*filter
|
||||||
|
|
@ -62,7 +60,6 @@ type PublicFilterAPI struct {
|
||||||
func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *PublicFilterAPI {
|
func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *PublicFilterAPI {
|
||||||
api := &PublicFilterAPI{
|
api := &PublicFilterAPI{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
chainDb: backend.ChainDb(),
|
|
||||||
events: NewEventSystem(backend, lightMode),
|
events: NewEventSystem(backend, lightMode),
|
||||||
filters: make(map[rpc.ID]*filter),
|
filters: make(map[rpc.ID]*filter),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// testEthHandler is a mock event handler to listen for inbound network requests
|
// testEthHandler is a mock event handler to listen for inbound network requests
|
||||||
|
|
@ -560,15 +561,17 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
|
||||||
// Create a block to reply to the challenge if no timeout is simulated.
|
// Create a block to reply to the challenge if no timeout is simulated.
|
||||||
if !timeout {
|
if !timeout {
|
||||||
if empty {
|
if empty {
|
||||||
if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{}); err != nil {
|
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{}); err != nil {
|
||||||
t.Fatalf("failed to answer challenge: %v", err)
|
t.Fatalf("failed to answer challenge: %v", err)
|
||||||
}
|
}
|
||||||
} else if match {
|
} else if match {
|
||||||
if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{response}); err != nil {
|
responseRlp, _ := rlp.EncodeToBytes(response)
|
||||||
|
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{responseRlp}); err != nil {
|
||||||
t.Fatalf("failed to answer challenge: %v", err)
|
t.Fatalf("failed to answer challenge: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{{Number: response.Number}}); err != nil {
|
responseRlp, _ := rlp.EncodeToBytes(types.Header{Number: response.Number})
|
||||||
|
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{responseRlp}); err != nil {
|
||||||
t.Fatalf("failed to answer challenge: %v", err)
|
t.Fatalf("failed to answer challenge: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,6 @@ const (
|
||||||
// softResponseLimit is the target maximum size of replies to data retrievals.
|
// softResponseLimit is the target maximum size of replies to data retrievals.
|
||||||
softResponseLimit = 2 * 1024 * 1024
|
softResponseLimit = 2 * 1024 * 1024
|
||||||
|
|
||||||
// estHeaderSize is the approximate size of an RLP encoded block header.
|
|
||||||
estHeaderSize = 500
|
|
||||||
|
|
||||||
// maxHeadersServe is the maximum number of block headers to serve. This number
|
// maxHeadersServe is the maximum number of block headers to serve. This number
|
||||||
// is there to limit the number of disk lookups.
|
// is there to limit the number of disk lookups.
|
||||||
maxHeadersServe = 1024
|
maxHeadersServe = 1024
|
||||||
|
|
|
||||||
|
|
@ -136,11 +136,13 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
|
||||||
query *GetBlockHeadersPacket // The query to execute for header retrieval
|
query *GetBlockHeadersPacket // The query to execute for header retrieval
|
||||||
expect []common.Hash // The hashes of the block whose headers are expected
|
expect []common.Hash // The hashes of the block whose headers are expected
|
||||||
}{
|
}{
|
||||||
// A single random block should be retrievable by hash and number too
|
// A single random block should be retrievable by hash
|
||||||
{
|
{
|
||||||
&GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
|
&GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
|
||||||
[]common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()},
|
[]common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()},
|
||||||
}, {
|
},
|
||||||
|
// A single random block should be retrievable by number
|
||||||
|
{
|
||||||
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 1},
|
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 1},
|
||||||
[]common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()},
|
[]common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()},
|
||||||
},
|
},
|
||||||
|
|
@ -180,10 +182,15 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
|
||||||
{
|
{
|
||||||
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: 0}, Amount: 1},
|
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: 0}, Amount: 1},
|
||||||
[]common.Hash{backend.chain.GetBlockByNumber(0).Hash()},
|
[]common.Hash{backend.chain.GetBlockByNumber(0).Hash()},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64()}, Amount: 1},
|
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64()}, Amount: 1},
|
||||||
[]common.Hash{backend.chain.CurrentBlock().Hash()},
|
[]common.Hash{backend.chain.CurrentBlock().Hash()},
|
||||||
},
|
},
|
||||||
|
{ // If the peer requests a bit into the future, we deliver what we have
|
||||||
|
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64()}, Amount: 10},
|
||||||
|
[]common.Hash{backend.chain.CurrentBlock().Hash()},
|
||||||
|
},
|
||||||
// Ensure protocol limits are honored
|
// Ensure protocol limits are honored
|
||||||
{
|
{
|
||||||
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
|
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
|
||||||
|
|
@ -280,7 +287,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
|
||||||
RequestId: 456,
|
RequestId: 456,
|
||||||
BlockHeadersPacket: headers,
|
BlockHeadersPacket: headers,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
t.Errorf("test %d by hash: headers mismatch: %v", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,21 @@ func handleGetBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
|
||||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||||
}
|
}
|
||||||
response := ServiceGetBlockHeadersQuery(backend.Chain(), query.GetBlockHeadersPacket, peer)
|
response := ServiceGetBlockHeadersQuery(backend.Chain(), query.GetBlockHeadersPacket, peer)
|
||||||
return peer.ReplyBlockHeaders(query.RequestId, response)
|
return peer.ReplyBlockHeadersRLP(query.RequestId, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceGetBlockHeadersQuery assembles the response to a header query. It is
|
// ServiceGetBlockHeadersQuery assembles the response to a header query. It is
|
||||||
// exposed to allow external packages to test protocol behavior.
|
// exposed to allow external packages to test protocol behavior.
|
||||||
func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersPacket, peer *Peer) []*types.Header {
|
func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersPacket, peer *Peer) []rlp.RawValue {
|
||||||
|
if query.Skip == 0 {
|
||||||
|
// The fast path: when the request is for a contiguous segment of headers.
|
||||||
|
return serviceContiguousBlockHeaderQuery(chain, query)
|
||||||
|
} else {
|
||||||
|
return serviceNonContiguousBlockHeaderQuery(chain, query, peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serviceNonContiguousBlockHeaderQuery(chain *core.BlockChain, query *GetBlockHeadersPacket, peer *Peer) []rlp.RawValue {
|
||||||
hashMode := query.Origin.Hash != (common.Hash{})
|
hashMode := query.Origin.Hash != (common.Hash{})
|
||||||
first := true
|
first := true
|
||||||
maxNonCanonical := uint64(100)
|
maxNonCanonical := uint64(100)
|
||||||
|
|
@ -49,7 +58,7 @@ func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersP
|
||||||
// Gather headers until the fetch or network limits is reached
|
// Gather headers until the fetch or network limits is reached
|
||||||
var (
|
var (
|
||||||
bytes common.StorageSize
|
bytes common.StorageSize
|
||||||
headers []*types.Header
|
headers []rlp.RawValue
|
||||||
unknown bool
|
unknown bool
|
||||||
lookups int
|
lookups int
|
||||||
)
|
)
|
||||||
|
|
@ -74,9 +83,12 @@ func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersP
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
headers = append(headers, origin)
|
if rlpData, err := rlp.EncodeToBytes(origin); err != nil {
|
||||||
bytes += estHeaderSize
|
log.Crit("Unable to decode our own headers", "err", err)
|
||||||
|
} else {
|
||||||
|
headers = append(headers, rlp.RawValue(rlpData))
|
||||||
|
bytes += common.StorageSize(len(rlpData))
|
||||||
|
}
|
||||||
// Advance to the next header of the query
|
// Advance to the next header of the query
|
||||||
switch {
|
switch {
|
||||||
case hashMode && query.Reverse:
|
case hashMode && query.Reverse:
|
||||||
|
|
@ -127,6 +139,69 @@ func ServiceGetBlockHeadersQuery(chain *core.BlockChain, query *GetBlockHeadersP
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serviceContiguousBlockHeaderQuery(chain *core.BlockChain, query *GetBlockHeadersPacket) []rlp.RawValue {
|
||||||
|
count := query.Amount
|
||||||
|
if count > maxHeadersServe {
|
||||||
|
count = maxHeadersServe
|
||||||
|
}
|
||||||
|
if query.Origin.Hash == (common.Hash{}) {
|
||||||
|
// Number mode, just return the canon chain segment. The backend
|
||||||
|
// delivers in [N, N-1, N-2..] descending order, so we need to
|
||||||
|
// accommodate for that.
|
||||||
|
from := query.Origin.Number
|
||||||
|
if !query.Reverse {
|
||||||
|
from = from + count - 1
|
||||||
|
}
|
||||||
|
headers := chain.GetHeadersFrom(from, count)
|
||||||
|
if !query.Reverse {
|
||||||
|
for i, j := 0, len(headers)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
headers[i], headers[j] = headers[j], headers[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
// Hash mode.
|
||||||
|
var (
|
||||||
|
headers []rlp.RawValue
|
||||||
|
hash = query.Origin.Hash
|
||||||
|
header = chain.GetHeaderByHash(hash)
|
||||||
|
)
|
||||||
|
if header != nil {
|
||||||
|
rlpData, _ := rlp.EncodeToBytes(header)
|
||||||
|
headers = append(headers, rlpData)
|
||||||
|
} else {
|
||||||
|
// We don't even have the origin header
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
num := header.Number.Uint64()
|
||||||
|
if !query.Reverse {
|
||||||
|
// Theoretically, we are tasked to deliver header by hash H, and onwards.
|
||||||
|
// However, if H is not canon, we will be unable to deliver any descendants of
|
||||||
|
// H.
|
||||||
|
if canonHash := chain.GetCanonicalHash(num); canonHash != hash {
|
||||||
|
// Not canon, we can't deliver descendants
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
descendants := chain.GetHeadersFrom(num+count-1, count-1)
|
||||||
|
for i, j := 0, len(descendants)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
descendants[i], descendants[j] = descendants[j], descendants[i]
|
||||||
|
}
|
||||||
|
headers = append(headers, descendants...)
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
{ // Last mode: deliver ancestors of H
|
||||||
|
for i := uint64(1); header != nil && i < count; i++ {
|
||||||
|
header = chain.GetHeaderByHash(header.ParentHash)
|
||||||
|
if header == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rlpData, _ := rlp.EncodeToBytes(header)
|
||||||
|
headers = append(headers, rlpData)
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handleGetBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
func handleGetBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
||||||
// Decode the block body retrieval message
|
// Decode the block body retrieval message
|
||||||
var query GetBlockBodiesPacket66
|
var query GetBlockBodiesPacket66
|
||||||
|
|
|
||||||
|
|
@ -297,10 +297,10 @@ func (p *Peer) AsyncSendNewBlock(block *types.Block, td *big.Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyBlockHeaders is the eth/66 version of SendBlockHeaders.
|
// ReplyBlockHeaders is the eth/66 version of SendBlockHeaders.
|
||||||
func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error {
|
func (p *Peer) ReplyBlockHeadersRLP(id uint64, headers []rlp.RawValue) error {
|
||||||
return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket66{
|
return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersRLPPacket66{
|
||||||
RequestId: id,
|
RequestId: id,
|
||||||
BlockHeadersPacket: headers,
|
BlockHeadersRLPPacket: headers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,16 @@ type BlockHeadersPacket66 struct {
|
||||||
BlockHeadersPacket
|
BlockHeadersPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockHeadersRLPPacket represents a block header response, to use when we already
|
||||||
|
// have the headers rlp encoded.
|
||||||
|
type BlockHeadersRLPPacket []rlp.RawValue
|
||||||
|
|
||||||
|
// BlockHeadersPacket represents a block header response over eth/66.
|
||||||
|
type BlockHeadersRLPPacket66 struct {
|
||||||
|
RequestId uint64
|
||||||
|
BlockHeadersRLPPacket
|
||||||
|
}
|
||||||
|
|
||||||
// NewBlockPacket is the network packet for the block propagation message.
|
// NewBlockPacket is the network packet for the block propagation message.
|
||||||
type NewBlockPacket struct {
|
type NewBlockPacket struct {
|
||||||
Block *types.Block
|
Block *types.Block
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package jsre
|
||||||
import (
|
import (
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -220,19 +221,33 @@ loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do executes the given function on the JS event loop.
|
// Do executes the given function on the JS event loop.
|
||||||
|
// When the runtime is stopped, fn will not execute.
|
||||||
func (re *JSRE) Do(fn func(*goja.Runtime)) {
|
func (re *JSRE) Do(fn func(*goja.Runtime)) {
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
req := &evalReq{fn, done}
|
req := &evalReq{fn, done}
|
||||||
re.evalQueue <- req
|
select {
|
||||||
<-done
|
case re.evalQueue <- req:
|
||||||
|
<-done
|
||||||
|
case <-re.closed:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stops the event loop before exit, optionally waits for all timers to expire
|
// Stop terminates the event loop, optionally waiting for all timers to expire.
|
||||||
func (re *JSRE) Stop(waitForCallbacks bool) {
|
func (re *JSRE) Stop(waitForCallbacks bool) {
|
||||||
select {
|
timeout := time.NewTimer(10 * time.Millisecond)
|
||||||
case <-re.closed:
|
defer timeout.Stop()
|
||||||
case re.stopEventLoop <- waitForCallbacks:
|
|
||||||
<-re.closed
|
for {
|
||||||
|
select {
|
||||||
|
case <-re.closed:
|
||||||
|
return
|
||||||
|
case re.stopEventLoop <- waitForCallbacks:
|
||||||
|
<-re.closed
|
||||||
|
return
|
||||||
|
case <-timeout.C:
|
||||||
|
// JS is blocked, interrupt and try again.
|
||||||
|
re.vm.Interrupt(errors.New("JS runtime stopped"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,6 +297,19 @@ func (re *JSRE) Evaluate(code string, w io.Writer) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interrupt stops the current JS evaluation.
|
||||||
|
func (re *JSRE) Interrupt(v interface{}) {
|
||||||
|
done := make(chan bool)
|
||||||
|
noop := func(*goja.Runtime) {}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case re.evalQueue <- &evalReq{noop, done}:
|
||||||
|
// event loop is not blocked.
|
||||||
|
default:
|
||||||
|
re.vm.Interrupt(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compile compiles and then runs a piece of JS code.
|
// Compile compiles and then runs a piece of JS code.
|
||||||
func (re *JSRE) Compile(filename string, src string) (err error) {
|
func (re *JSRE) Compile(filename string, src string) (err error) {
|
||||||
re.Do(func(vm *goja.Runtime) { _, err = compileAndRun(vm, filename, src) })
|
re.Do(func(vm *goja.Runtime) { _, err = compileAndRun(vm, filename, src) })
|
||||||
|
|
|
||||||
|
|
@ -1039,17 +1039,15 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're post merge, just ignore
|
|
||||||
td, ttd := w.chain.GetTd(block.ParentHash(), block.NumberU64()-1), w.chain.Config().TerminalTotalDifficulty
|
|
||||||
if td != nil && ttd != nil && td.Cmp(ttd) >= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.isRunning() {
|
if w.isRunning() {
|
||||||
if interval != nil {
|
if interval != nil {
|
||||||
interval()
|
interval()
|
||||||
}
|
}
|
||||||
|
// If we're post merge, just ignore
|
||||||
|
td, ttd := w.chain.GetTd(block.ParentHash(), block.NumberU64()-1), w.chain.Config().TerminalTotalDifficulty
|
||||||
|
if td != nil && ttd != nil && td.Cmp(ttd) >= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}:
|
case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}:
|
||||||
w.unconfirmed.Shift(block.NumberU64() - 1)
|
w.unconfirmed.Shift(block.NumberU64() - 1)
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,10 @@ package params
|
||||||
import "math/big"
|
import "math/big"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations.
|
GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations.
|
||||||
MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be.
|
MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be.
|
||||||
GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
|
MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1).
|
||||||
|
GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
|
||||||
|
|
||||||
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
|
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
|
||||||
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
|
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ func (h *handler) handleImmediate(msg *jsonrpcMessage) bool {
|
||||||
return false
|
return false
|
||||||
case msg.isResponse():
|
case msg.isResponse():
|
||||||
h.handleResponse(msg)
|
h.handleResponse(msg)
|
||||||
h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "t", time.Since(start))
|
h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "duration", time.Since(start))
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
@ -292,12 +292,12 @@ func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMess
|
||||||
switch {
|
switch {
|
||||||
case msg.isNotification():
|
case msg.isNotification():
|
||||||
h.handleCall(ctx, msg)
|
h.handleCall(ctx, msg)
|
||||||
h.log.Debug("Served "+msg.Method, "t", time.Since(start))
|
h.log.Debug("Served "+msg.Method, "duration", time.Since(start))
|
||||||
return nil
|
return nil
|
||||||
case msg.isCall():
|
case msg.isCall():
|
||||||
resp := h.handleCall(ctx, msg)
|
resp := h.handleCall(ctx, msg)
|
||||||
var ctx []interface{}
|
var ctx []interface{}
|
||||||
ctx = append(ctx, "reqid", idForLog{msg.ID}, "t", time.Since(start))
|
ctx = append(ctx, "reqid", idForLog{msg.ID}, "duration", time.Since(start))
|
||||||
if resp.Error != nil {
|
if resp.Error != nil {
|
||||||
ctx = append(ctx, "err", resp.Error.Message)
|
ctx = append(ctx, "err", resp.Error.Message)
|
||||||
if resp.Error.Data != nil {
|
if resp.Error.Data != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue