diff --git a/.travis.yml b/.travis.yml
index 1c56f5d5d..94d2dda73 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,10 +24,12 @@ jobs:
script:
- go run build/ci.go lint
- # This builder does the Docker Hub build and upload for amd64
+ # These builders create the Docker sub-images for multi-arch push and each
+ # will attempt to push the multi-arch image if they are the last builder
- stage: build
if: type = push
os: linux
+ arch: amd64
dist: bionic
go: 1.16.x
env:
@@ -36,8 +38,27 @@ jobs:
- docker
git:
submodules: false # avoid cloning ethereum/tests
+ before_install:
+ - export DOCKER_CLI_EXPERIMENTAL=enabled
script:
- - go run build/ci.go docker -upload karalabe/geth-docker-test
+ - go run build/ci.go docker -image -manifest amd64,arm64 -upload karalabe/geth-docker-test
+
+ - stage: build
+ if: type = push
+ os: linux
+ arch: arm64
+ dist: bionic
+ go: 1.16.x
+ env:
+ - docker
+ services:
+ - docker
+ git:
+ submodules: false # avoid cloning ethereum/tests
+ before_install:
+ - export DOCKER_CLI_EXPERIMENTAL=enabled
+ script:
+ - go run build/ci.go docker -image -manifest amd64,arm64 -upload karalabe/geth-docker-test
# This builder does the Ubuntu PPA upload
- stage: build
diff --git a/Dockerfile b/Dockerfile
index 6e0dea11b..e76c5765b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,15 @@
+# Support setting various labels on the final image
+ARG COMMIT=""
+ARG VERSION=""
+ARG BUILDNUM=""
+
# Build Geth in a stock Go builder container
FROM golang:1.16-alpine as builder
-RUN apk add --no-cache make gcc musl-dev linux-headers git
+RUN apk add --no-cache gcc musl-dev linux-headers git
ADD . /go-ethereum
-RUN cd /go-ethereum && make geth
+RUN cd /go-ethereum && go run build/ci.go install ./cmd/geth
# Pull Geth into a second stage deploy alpine container
FROM alpine:latest
@@ -14,3 +19,10 @@ COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp
ENTRYPOINT ["geth"]
+
+# Add some metadata labels to help programatic image consumption
+ARG COMMIT=""
+ARG VERSION=""
+ARG BUILDNUM=""
+
+LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
diff --git a/Dockerfile.alltools b/Dockerfile.alltools
index 483afad8c..71f63b7a4 100644
--- a/Dockerfile.alltools
+++ b/Dockerfile.alltools
@@ -1,10 +1,15 @@
+# Support setting various labels on the final image
+ARG COMMIT=""
+ARG VERSION=""
+ARG BUILDNUM=""
+
# Build Geth in a stock Go builder container
FROM golang:1.16-alpine as builder
-RUN apk add --no-cache make gcc musl-dev linux-headers git
+RUN apk add --no-cache gcc musl-dev linux-headers git
ADD . /go-ethereum
-RUN cd /go-ethereum && make all
+RUN cd /go-ethereum && go run build/ci.go install
# Pull all binaries into a second stage deploy alpine container
FROM alpine:latest
@@ -13,3 +18,10 @@ RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp
+
+# Add some metadata labels to help programatic image consumption
+ARG COMMIT=""
+ARG VERSION=""
+ARG BUILDNUM=""
+
+LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index 71648059c..274f6e4d9 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -229,13 +229,13 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
- head, err := c.transactor.HeaderByNumber(opts.Context, nil)
+ head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil)
if err != nil {
return nil, err
}
if head.BaseFee != nil && opts.GasPrice == nil {
if opts.GasTipCap == nil {
- tip, err := c.transactor.SuggestGasTipCap(opts.Context)
+ tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
if err != nil {
return nil, err
}
@@ -256,13 +256,10 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
}
if opts.GasPrice == nil {
- price, err := c.transactor.SuggestGasTipCap(opts.Context)
+ price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
return nil, err
}
- if head.BaseFee != nil {
- price.Add(price, head.BaseFee)
- }
opts.GasPrice = price
}
}
@@ -443,7 +440,7 @@ func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event strin
// user specified it as such.
func ensureContext(ctx context.Context) context.Context {
if ctx == nil {
- return context.TODO()
+ return context.Background()
}
return ctx
}
diff --git a/build/ci.go b/build/ci.go
index 12e2b8b57..63dd60632 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -54,6 +54,7 @@ import (
"path/filepath"
"regexp"
"runtime"
+ "strconv"
"strings"
"time"
@@ -183,7 +184,7 @@ func main() {
case "archive":
doArchive(os.Args[2:])
case "docker":
- doDockerImage(os.Args[2:])
+ doDocker(os.Args[2:])
case "debsrc":
doDebianSource(os.Args[2:])
case "nsis":
@@ -455,9 +456,11 @@ func maybeSkipArchive(env build.Environment) {
}
// Builds the docker images and optionally uploads them to Docker Hub.
-func doDockerImage(cmdline []string) {
+func doDocker(cmdline []string) {
var (
- upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
+ image = flag.Bool("image", false, `Whether to build and push an arch specific docker image`)
+ manifest = flag.String("manifest", "", `Push a multi-arch docker image for the specified architectures (usually "amd64,arm64")`)
+ upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
)
flag.CommandLine.Parse(cmdline)
@@ -465,6 +468,15 @@ func doDockerImage(cmdline []string) {
env := build.Env()
maybeSkipArchive(env)
+ // Retrieve the upload credentials and authenticate
+ user := getenvBase64("DOCKER_HUB_USERNAME")
+ pass := getenvBase64("DOCKER_HUB_PASSWORD")
+
+ if len(user) > 0 && len(pass) > 0 {
+ auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
+ auther.Stdin = bytes.NewReader(pass)
+ build.MustRun(auther)
+ }
// Retrieve the version infos to build and push to the following paths:
// - ethereum/client-go:latest - Pushes to the master branch, Geth only
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only
@@ -482,25 +494,130 @@ func doDockerImage(cmdline []string) {
case strings.HasPrefix(env.Tag, "v1."):
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), params.Version}
}
- // Build the docker images via CLI (don't pull in the `moby` dep to call 3 commands)
- build.MustRunCommand("docker", "build", "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
- build.MustRunCommand("docker", "build", "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
+ // If architecture specific image builds are requested, build and push them
+ if *image {
+ build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
+ build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
- // Retrieve the upload credentials and authenticate
- user := getenvBase64("DOCKER_HUB_USERNAME")
- pass := getenvBase64("DOCKER_HUB_PASSWORD")
+ // Tag and upload the images to Docker Hub
+ for _, tag := range tags {
+ gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, runtime.GOARCH)
+ toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, runtime.GOARCH)
- if len(user) > 0 && len(pass) > 0 {
- auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
- auther.Stdin = bytes.NewReader(pass)
- build.MustRun(auther)
+ // If the image already exists (non version tag), check the build
+ // number to prevent overwriting a newer commit if concurrent builds
+ // are running. This is still a tiny bit racey if two published are
+ // done at the same time, but that's extremely unlikely even on the
+ // master branch.
+ for _, img := range []string{gethImage, toolImage} {
+ if exec.Command("docker", "pull", img).Run() != nil {
+ continue // Generally the only failure is a missing image, which is good
+ }
+ buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
+ if err != nil {
+ log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
+ }
+ buildnum = bytes.TrimSpace(buildnum)
+
+ if len(buildnum) > 0 && len(env.Buildnum) > 0 {
+ oldnum, err := strconv.Atoi(string(buildnum))
+ if err != nil {
+ log.Fatalf("Failed to parse old image build number: %v", err)
+ }
+ newnum, err := strconv.Atoi(env.Buildnum)
+ if err != nil {
+ log.Fatalf("Failed to parse current build number: %v", err)
+ }
+ if oldnum > newnum {
+ log.Fatalf("Current build number %d not newer than existing %d", newnum, oldnum)
+ } else {
+ log.Printf("Updating %s from build %d to %d", img, oldnum, newnum)
+ }
+ }
+ }
+ build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), gethImage)
+ build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), toolImage)
+ build.MustRunCommand("docker", "push", gethImage)
+ build.MustRunCommand("docker", "push", toolImage)
+ }
}
- // Tag and upload the images to Docker Hub
- for _, tag := range tags {
- build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), fmt.Sprintf("%s:%s", *upload, tag))
- build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), fmt.Sprintf("%s:alltools-%s", *upload, tag))
- build.MustRunCommand("docker", "push", fmt.Sprintf("%s:%s", *upload, tag))
- build.MustRunCommand("docker", "push", fmt.Sprintf("%s:alltools-%s", *upload, tag))
+ // If multi-arch image manifest push is requested, assemble it
+ if len(*manifest) != 0 {
+ // Since different architectures are pushed by different builders, wait
+ // until all required images are updated.
+ var mismatch bool
+ for i := 0; i < 2; i++ { // 2 attempts, second is race check
+ mismatch = false // hope there's no mismatch now
+
+ for _, tag := range tags {
+ for _, arch := range strings.Split(*manifest, ",") {
+ gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, arch)
+ toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, arch)
+
+ for _, img := range []string{gethImage, toolImage} {
+ if out, err := exec.Command("docker", "pull", img).CombinedOutput(); err != nil {
+ log.Printf("Required image %s unavailable: %v\nOutput: %s", img, err, out)
+ mismatch = true
+ break
+ }
+ buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
+ if err != nil {
+ log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
+ }
+ buildnum = bytes.TrimSpace(buildnum)
+
+ if string(buildnum) != env.Buildnum {
+ log.Printf("Build number mismatch on %s: want %s, have %s", img, env.Buildnum, buildnum)
+ mismatch = true
+ break
+ }
+ }
+ if mismatch {
+ break
+ }
+ }
+ if mismatch {
+ break
+ }
+ }
+ if mismatch {
+ // Build numbers mismatching, retry in a short time to
+ // avoid concurrent failes in both publisher images. If
+ // however the retry failed too, it means the concurrent
+ // builder is still crunching, let that do the publish.
+ if i == 0 {
+ time.Sleep(30 * time.Second)
+ }
+ continue
+ }
+ break
+ }
+ if mismatch {
+ log.Println("Relinquishing publish to other builder")
+ return
+ }
+ // Assemble and push the Geth manifest image
+ for _, tag := range tags {
+ gethImage := fmt.Sprintf("%s:%s", *upload, tag)
+
+ var gethSubImages []string
+ for _, arch := range strings.Split(*manifest, ",") {
+ gethSubImages = append(gethSubImages, gethImage+"-"+arch)
+ }
+ build.MustRunCommand("docker", append([]string{"manifest", "create", gethImage}, gethSubImages...)...)
+ build.MustRunCommand("docker", "manifest", "push", gethImage)
+ }
+ // Assemble and push the alltools manifest image
+ for _, tag := range tags {
+ toolImage := fmt.Sprintf("%s:alltools-%s", *upload, tag)
+
+ var toolSubImages []string
+ for _, arch := range strings.Split(*manifest, ",") {
+ toolSubImages = append(toolSubImages, toolImage+"-"+arch)
+ }
+ build.MustRunCommand("docker", append([]string{"manifest", "create", toolImage}, toolSubImages...)...)
+ build.MustRunCommand("docker", "manifest", "push", toolImage)
+ }
}
}
diff --git a/cmd/devp2p/internal/ethtest/helpers.go b/cmd/devp2p/internal/ethtest/helpers.go
index a9a213f33..6f7365483 100644
--- a/cmd/devp2p/internal/ethtest/helpers.go
+++ b/cmd/devp2p/internal/ethtest/helpers.go
@@ -24,6 +24,7 @@ import (
"time"
"github.com/davecgh/go-spew/spew"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
@@ -649,58 +650,68 @@ func (s *Suite) hashAnnounce(isEth66 bool) error {
return fmt.Errorf("peering failed: %v", err)
}
// create NewBlockHashes announcement
- nextBlock := s.fullChain.blocks[s.chain.Len()]
- newBlockHash := &NewBlockHashes{
- {Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()},
+ type anno struct {
+ Hash common.Hash // Hash of one particular block being announced
+ Number uint64 // Number of one particular block being announced
}
-
+ nextBlock := s.fullChain.blocks[s.chain.Len()]
+ announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()}
+ newBlockHash := &NewBlockHashes{announcement}
if err := sendConn.Write(newBlockHash); err != nil {
return fmt.Errorf("failed to write to connection: %v", err)
}
+ // Announcement sent, now wait for a header request
+ var (
+ id uint64
+ msg Message
+ blockHeaderReq GetBlockHeaders
+ )
if isEth66 {
- // expect GetBlockHeaders request, and respond
- id, msg := sendConn.Read66()
+ id, msg = sendConn.Read66()
switch msg := msg.(type) {
case GetBlockHeaders:
- blockHeaderReq := msg
- if blockHeaderReq.Amount != 1 {
- return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
- }
- if blockHeaderReq.Origin.Hash != nextBlock.Hash() {
- return fmt.Errorf("unexpected block header requested: %v", pretty.Sdump(blockHeaderReq))
- }
- resp := ð.BlockHeadersPacket66{
- RequestId: id,
- BlockHeadersPacket: eth.BlockHeadersPacket{
- nextBlock.Header(),
- },
- }
- if err := sendConn.Write66(resp, BlockHeaders{}.Code()); err != nil {
- return fmt.Errorf("failed to write to connection: %v", err)
- }
+ blockHeaderReq = msg
default:
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
}
+ if blockHeaderReq.Amount != 1 {
+ return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
+ }
+ if blockHeaderReq.Origin.Hash != announcement.Hash {
+ return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
+ pretty.Sdump(announcement),
+ pretty.Sdump(blockHeaderReq))
+ }
+ if err := sendConn.Write66(ð.BlockHeadersPacket66{
+ RequestId: id,
+ BlockHeadersPacket: eth.BlockHeadersPacket{
+ nextBlock.Header(),
+ },
+ }, BlockHeaders{}.Code()); err != nil {
+ return fmt.Errorf("failed to write to connection: %v", err)
+ }
} else {
- // expect GetBlockHeaders request, and respond
- switch msg := sendConn.Read().(type) {
+ msg = sendConn.Read()
+ switch msg := msg.(type) {
case *GetBlockHeaders:
- blockHeaderReq := *msg
- if blockHeaderReq.Amount != 1 {
- return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
- }
- if blockHeaderReq.Origin.Hash != nextBlock.Hash() {
- return fmt.Errorf("unexpected block header requested: %v", pretty.Sdump(blockHeaderReq))
- }
- if err := sendConn.Write(&BlockHeaders{nextBlock.Header()}); err != nil {
- return fmt.Errorf("failed to write to connection: %v", err)
- }
+ blockHeaderReq = *msg
default:
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
}
+ if blockHeaderReq.Amount != 1 {
+ return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
+ }
+ if blockHeaderReq.Origin.Hash != announcement.Hash {
+ return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
+ pretty.Sdump(announcement),
+ pretty.Sdump(blockHeaderReq))
+ }
+ if err := sendConn.Write(&BlockHeaders{nextBlock.Header()}); err != nil {
+ return fmt.Errorf("failed to write to connection: %v", err)
+ }
}
// wait for block announcement
- msg := recvConn.readAndServe(s.chain, timeout)
+ msg = recvConn.readAndServe(s.chain, timeout)
switch msg := msg.(type) {
case *NewBlockHashes:
hashes := *msg
diff --git a/cmd/devp2p/internal/v4test/discv4tests.go b/cmd/devp2p/internal/v4test/discv4tests.go
index 140b96bfa..1b5e5304e 100644
--- a/cmd/devp2p/internal/v4test/discv4tests.go
+++ b/cmd/devp2p/internal/v4test/discv4tests.go
@@ -21,7 +21,6 @@ import (
"crypto/rand"
"fmt"
"net"
- "reflect"
"time"
"github.com/ethereum/go-ethereum/crypto"
@@ -89,16 +88,18 @@ func BasicPing(t *utesting.T) {
// checkPong verifies that reply is a valid PONG matching the given ping hash.
func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
- if reply == nil || reply.Kind() != v4wire.PongPacket {
- return fmt.Errorf("expected PONG reply, got %v", reply)
+ if reply == nil {
+ return fmt.Errorf("expected PONG reply, got nil")
+ }
+ if reply.Kind() != v4wire.PongPacket {
+ return fmt.Errorf("expected PONG reply, got %v %v", reply.Name(), reply)
}
pong := reply.(*v4wire.Pong)
if !bytes.Equal(pong.ReplyTok, pingHash) {
return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash)
}
- wantEndpoint := te.localEndpoint(te.l1)
- if !reflect.DeepEqual(pong.To, wantEndpoint) {
- return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, wantEndpoint)
+ if want := te.localEndpoint(te.l1); !want.IP.Equal(pong.To.IP) || want.UDP != pong.To.UDP {
+ return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, want)
}
if v4wire.Expired(pong.Expiration) {
return fmt.Errorf("PONG is expired (%v)", pong.Expiration)
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index ccc90618b..1ab2f001e 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -152,7 +152,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
vmConfig.Tracer = tracer
vmConfig.Debug = (tracer != nil)
- statedb.Prepare(tx.Hash(), blockHash, txIndex)
+ statedb.Prepare(tx.Hash(), txIndex)
txContext := core.NewEVMTxContext(msg)
snapshot := statedb.Snapshot()
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
@@ -197,7 +197,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
// Set the receipt logs and create the bloom filter.
- receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
// These three are non-consensus fields:
//receipt.BlockHash
diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go
index 695fdba1e..c7f079c02 100644
--- a/cmd/evm/internal/t8ntool/gen_stenv.go
+++ b/cmd/evm/internal/t8ntool/gen_stenv.go
@@ -16,11 +16,11 @@ var _ = (*stEnvMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (s stEnv) MarshalJSON() ([]byte, error) {
type stEnv struct {
- Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
- GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
- Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
- Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
@@ -40,11 +40,11 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON.
func (s *stEnv) UnmarshalJSON(input []byte) error {
type stEnv struct {
- Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
- GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
- Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
- Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 8a3e4e0ea..b9c0d17f3 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -129,11 +129,6 @@ var (
Name: "noreturndata",
Usage: "disable return data output",
}
- EVMInterpreterFlag = cli.StringFlag{
- Name: "vm.evm",
- Usage: "External EVM configuration (default = built-in interpreter)",
- Value: "",
- }
)
var stateTransitionCommand = cli.Command{
@@ -185,7 +180,6 @@ func init() {
DisableStackFlag,
DisableStorageFlag,
DisableReturnDataFlag,
- EVMInterpreterFlag,
}
app.Commands = []cli.Command{
compileCommand,
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index 2d890ef1a..e409d2692 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -211,9 +211,8 @@ func runCmd(ctx *cli.Context) error {
Coinbase: genesisConfig.Coinbase,
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
EVMConfig: vm.Config{
- Tracer: tracer,
- Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
- EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name),
+ Tracer: tracer,
+ Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
},
}
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index c4ebf6488..117be96ae 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
@@ -63,7 +64,12 @@ var tomlSettings = toml.Config{
return field
},
MissingField: func(rt reflect.Type, field string) error {
- link := ""
+ id := fmt.Sprintf("%s.%s", rt.String(), field)
+ if deprecated(id) {
+ log.Warn("Config field is deprecated and won't have an effect", "name", id)
+ return nil
+ }
+ var link string
if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
}
@@ -228,3 +234,14 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name)
}
}
+
+func deprecated(field string) bool {
+ switch field {
+ case "ethconfig.Config.EVMInterpreter":
+ return true
+ case "ethconfig.Config.EWASMInterpreter":
+ return true
+ default:
+ return false
+ }
+}
diff --git a/cmd/geth/les_test.go b/cmd/geth/les_test.go
index 053ce96aa..151c12c68 100644
--- a/cmd/geth/les_test.go
+++ b/cmd/geth/les_test.go
@@ -137,14 +137,18 @@ func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
name: name,
geth: runGeth(t, args...),
}
- // wait before we can attach to it. TODO: probe for it properly
- time.Sleep(1 * time.Second)
- var err error
ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
- if g.rpc, err = rpc.Dial(ipcpath); err != nil {
- t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
+ // We can't know exactly how long geth will take to start, so we try 10
+ // times over a 5 second period.
+ var err error
+ for i := 0; i < 10; i++ {
+ time.Sleep(500 * time.Millisecond)
+ if g.rpc, err = rpc.Dial(ipcpath); err == nil {
+ return g
+ }
}
- return g
+ t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
+ return nil
}
func initGeth(t *testing.T) string {
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index bef31b517..581fc0352 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -150,8 +150,6 @@ var (
utils.GpoPercentileFlag,
utils.GpoMaxGasPriceFlag,
utils.GpoIgnoreGasPriceFlag,
- utils.EWASMInterpreterFlag,
- utils.EVMInterpreterFlag,
utils.MinerNotifyFullFlag,
configFileFlag,
utils.CatalystFlag,
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 44444bdd5..dea0b7c08 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -203,8 +203,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
Name: "VIRTUAL MACHINE",
Flags: []cli.Flag{
utils.VMEnableDebugFlag,
- utils.EVMInterpreterFlag,
- utils.EWASMInterpreterFlag,
},
},
{
diff --git a/cmd/puppeth/module_dashboard.go b/cmd/puppeth/module_dashboard.go
index b238af031..35cfada66 100644
--- a/cmd/puppeth/module_dashboard.go
+++ b/cmd/puppeth/module_dashboard.go
@@ -482,7 +482,7 @@ ADD puppeth.png /dashboard/puppeth.png
EXPOSE 80
-CMD ["node", "/server.js"]
+CMD ["node", "./server.js"]
`
// dashboardComposefile is the docker-compose.yml file required to deploy and
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index cc5af5625..7ed5907db 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -760,16 +760,6 @@ var (
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
Value: metrics.DefaultConfig.InfluxDBTags,
}
- EWASMInterpreterFlag = cli.StringFlag{
- Name: "vm.ewasm",
- Usage: "External ewasm configuration (default = built-in interpreter)",
- Value: "",
- }
- EVMInterpreterFlag = cli.StringFlag{
- Name: "vm.evm",
- Usage: "External EVM configuration (default = built-in interpreter)",
- Value: "",
- }
CatalystFlag = cli.BoolFlag{
Name: "catalyst",
@@ -1302,8 +1292,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
// If we are running the light client, apply another group
// settings for gas oracle.
if light {
- cfg.Blocks = ethconfig.LightClientGPO.Blocks
- cfg.Percentile = ethconfig.LightClientGPO.Percentile
+ *cfg = ethconfig.LightClientGPO
}
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
@@ -1587,13 +1576,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
}
- if ctx.GlobalIsSet(EWASMInterpreterFlag.Name) {
- cfg.EWASMInterpreter = ctx.GlobalString(EWASMInterpreterFlag.Name)
- }
-
- if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
- cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
- }
if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) {
cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name)
}
diff --git a/consensus/clique/api.go b/consensus/clique/api.go
index 8e9a1e7de..6129b5cc5 100644
--- a/consensus/clique/api.go
+++ b/consensus/clique/api.go
@@ -17,11 +17,14 @@
package clique
import (
+ "encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -175,3 +178,51 @@ func (api *API) Status() (*status, error) {
NumBlocks: numBlocks,
}, nil
}
+
+type blockNumberOrHashOrRLP struct {
+ *rpc.BlockNumberOrHash
+ RLP hexutil.Bytes `json:"rlp,omitempty"`
+}
+
+func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
+ bnOrHash := new(rpc.BlockNumberOrHash)
+ // Try to unmarshal bNrOrHash
+ if err := bnOrHash.UnmarshalJSON(data); err == nil {
+ sb.BlockNumberOrHash = bnOrHash
+ return nil
+ }
+ // Try to unmarshal RLP
+ var input string
+ if err := json.Unmarshal(data, &input); err != nil {
+ return err
+ }
+ sb.RLP = hexutil.MustDecode(input)
+ return nil
+}
+
+// GetSigner returns the signer for a specific clique block.
+// Can be called with either a blocknumber, blockhash or an rlp encoded blob.
+// The RLP encoded blob can either be a block or a header.
+func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) {
+ if len(rlpOrBlockNr.RLP) == 0 {
+ blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash
+ var header *types.Header
+ if blockNrOrHash == nil {
+ header = api.chain.CurrentHeader()
+ } else if hash, ok := blockNrOrHash.Hash(); ok {
+ header = api.chain.GetHeaderByHash(hash)
+ } else if number, ok := blockNrOrHash.Number(); ok {
+ header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
+ }
+ return api.clique.Author(header)
+ }
+ block := new(types.Block)
+ if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, block); err == nil {
+ return api.clique.Author(block.Header())
+ }
+ header := new(types.Header)
+ if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, header); err != nil {
+ return common.Address{}, err
+ }
+ return api.clique.Author(header)
+}
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index b693e8051..449095e72 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -710,7 +710,7 @@ func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
func SealHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewLegacyKeccak256()
encodeSigHeader(hasher, header)
- hasher.Sum(hash[:0])
+ hasher.(crypto.KeccakState).Read(hash[:])
return hash
}
diff --git a/consensus/clique/clique_test.go b/consensus/clique/clique_test.go
index 4b8ff807f..1bd32acd3 100644
--- a/consensus/clique/clique_test.go
+++ b/consensus/clique/clique_test.go
@@ -112,3 +112,16 @@ func TestReimportMirroredState(t *testing.T) {
t.Fatalf("chain head mismatch: have %d, want %d", head, 3)
}
}
+
+func TestSealHash(t *testing.T) {
+ have := SealHash(&types.Header{
+ Difficulty: new(big.Int),
+ Number: new(big.Int),
+ Extra: make([]byte, 32+65),
+ BaseFee: new(big.Int),
+ })
+ want := common.HexToHash("0xbd3d1fa43fbc4c5bfcc91b179ec92e2861df3654de60468beb908ff805359e8f")
+ if have != want {
+ t.Errorf("have %x, want %x", have, want)
+ }
+}
diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go
index 333411db5..fd400b688 100644
--- a/consensus/misc/eip1559_test.go
+++ b/consensus/misc/eip1559_test.go
@@ -44,7 +44,6 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig {
MuirGlacierBlock: original.MuirGlacierBlock,
BerlinBlock: original.BerlinBlock,
LondonBlock: original.LondonBlock,
- EWASMBlock: original.EWASMBlock,
CatalystBlock: original.CatalystBlock,
Ethash: original.Ethash,
Clique: original.Clique,
diff --git a/core/chain_makers.go b/core/chain_makers.go
index dc207f202..929a2aa3a 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -102,7 +102,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
if b.gasPool == nil {
b.SetCoinbase(common.Address{})
}
- b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
+ b.statedb.Prepare(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go
index 5829e863e..916bffadc 100644
--- a/core/forkid/forkid_test.go
+++ b/core/forkid/forkid_test.go
@@ -61,8 +61,10 @@ func TestCreation(t *testing.T) {
{9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block
{9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block
{12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block
- {12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 0}}, // First Berlin block
- {20000000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 0}}, // Future Berlin block
+ {12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block
+ {12964999, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block
+ {12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // First London block
+ {20000000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // Future London block
},
},
// Ropsten test cases
@@ -203,11 +205,11 @@ func TestValidation(t *testing.T) {
// Local is mainnet Petersburg, remote is Rinkeby Petersburg.
{7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale},
- // Local is mainnet Berlin, far in the future. Remote announces Gopherium (non existing fork)
+ // Local is mainnet London, far in the future. Remote announces Gopherium (non existing fork)
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
//
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
- {88888888, ID{Hash: checksumToBytes(0x0eb440f6), Next: 88888888}, ErrLocalIncompatibleOrStale},
+ {88888888, ID{Hash: checksumToBytes(0xb715077d), Next: 88888888}, ErrLocalIncompatibleOrStale},
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
// fork) at block 7279999, before Petersburg. Local is incompatible.
diff --git a/core/genesis.go b/core/genesis.go
index 19890406e..dc50ff475 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -415,7 +415,10 @@ func DefaultCalaverasGenesisBlock() *Genesis {
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges
- config.Clique.Period = period
+ config.Clique = ¶ms.CliqueConfig{
+ Period: period,
+ Epoch: config.Clique.Epoch,
+ }
// Assemble and return the genesis with the precompiles and faucet pre-funded
return &Genesis{
diff --git a/core/headerchain.go b/core/headerchain.go
index 1dbf95878..07307c710 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -165,6 +165,7 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
)
batch := hc.chainDb.NewBatch()
+ parentKnown := true // Set to true to force hc.HasHeader check the first iteration
for i, header := range headers {
var hash common.Hash
// The headers have already been validated at this point, so we already
@@ -178,8 +179,10 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
number := header.Number.Uint64()
newTD.Add(newTD, header.Difficulty)
+ // If the parent was not present, store it
// If the header is already known, skip it, otherwise store
- if !hc.HasHeader(hash, number) {
+ alreadyKnown := parentKnown && hc.HasHeader(hash, number)
+ if !alreadyKnown {
// Irrelevant of the canonical status, write the TD and header to the database.
rawdb.WriteTd(batch, hash, number, newTD)
hc.tdCache.Add(hash, new(big.Int).Set(newTD))
@@ -192,6 +195,7 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
firstInserted = i
}
}
+ parentKnown = alreadyKnown
lastHeader, lastHash, lastNumber = header, hash, number
}
@@ -570,7 +574,7 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
if parent == nil {
parent = hc.genesisHeader
}
- parentHash = hdr.ParentHash
+ parentHash = parent.Hash()
// Notably, since geth has the possibility for setting the head to a low
// height which is even lower than ancient head.
diff --git a/core/state/state_object.go b/core/state/state_object.go
index f93f47d5f..38621ffb6 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -450,9 +450,6 @@ func (s *stateObject) setBalance(amount *big.Int) {
s.data.Balance = amount
}
-// Return the gas back to the origin. Used by the Virtual machine or Closures
-func (s *stateObject) ReturnGas(gas *big.Int) {}
-
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
stateObject := newObject(db, s.address, s.data)
if s.trie != nil {
diff --git a/core/state/statedb.go b/core/state/statedb.go
index b6811b5a5..ad4d9ff93 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -89,10 +89,10 @@ type StateDB struct {
// The refund counter, also used by state transitioning.
refund uint64
- thash, bhash common.Hash
- txIndex int
- logs map[common.Hash][]*types.Log
- logSize uint
+ thash common.Hash
+ txIndex int
+ logs map[common.Hash][]*types.Log
+ logSize uint
preimages map[common.Hash][]byte
@@ -186,15 +186,18 @@ func (s *StateDB) AddLog(log *types.Log) {
s.journal.append(addLogChange{txhash: s.thash})
log.TxHash = s.thash
- log.BlockHash = s.bhash
log.TxIndex = uint(s.txIndex)
log.Index = s.logSize
s.logs[s.thash] = append(s.logs[s.thash], log)
s.logSize++
}
-func (s *StateDB) GetLogs(hash common.Hash) []*types.Log {
- return s.logs[hash]
+func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log {
+ logs := s.logs[hash]
+ for _, l := range logs {
+ l.BlockHash = blockHash
+ }
+ return logs
}
func (s *StateDB) Logs() []*types.Log {
@@ -272,11 +275,6 @@ func (s *StateDB) TxIndex() int {
return s.txIndex
}
-// BlockHash returns the current block hash set by Prepare.
-func (s *StateDB) BlockHash() common.Hash {
- return s.bhash
-}
-
func (s *StateDB) GetCode(addr common.Address) []byte {
stateObject := s.getStateObject(addr)
if stateObject != nil {
@@ -333,17 +331,6 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
return proof, err
}
-// GetStorageProofByHash returns the Merkle proof for given storage slot.
-func (s *StateDB) GetStorageProofByHash(a common.Address, key common.Hash) ([][]byte, error) {
- var proof proofList
- trie := s.StorageTrie(a)
- if trie == nil {
- return proof, errors.New("storage trie for requested address does not exist")
- }
- err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
- return proof, err
-}
-
// GetCommittedState retrieves a value from the given account's committed storage trie.
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
stateObject := s.getStateObject(addr)
@@ -597,7 +584,6 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
}
}
newobj = newObject(s, addr, Account{})
- newobj.setNonce(0) // sets the object to dirty
if prev == nil {
s.journal.append(createObjectChange{account: &addr})
} else {
@@ -894,9 +880,8 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Prepare sets the current transaction hash and index and block hash which is
// used when the EVM emits new state logs.
-func (s *StateDB) Prepare(thash, bhash common.Hash, ti int) {
+func (s *StateDB) Prepare(thash common.Hash, ti int) {
s.thash = thash
- s.bhash = bhash
s.txIndex = ti
s.accessList = newAccessList()
}
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index f81455312..e9576d4dc 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -463,9 +463,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
state.GetRefund(), checkstate.GetRefund())
}
- if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
+ if !reflect.DeepEqual(state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{})) {
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
- state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
+ state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{}))
}
return nil
}
diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go
index ac5e95c5c..25c3730e3 100644
--- a/core/state/trie_prefetcher.go
+++ b/core/state/trie_prefetcher.go
@@ -312,12 +312,11 @@ func (sf *subfetcher) loop() {
default:
// No termination request yet, prefetch the next entry
- taskid := string(task)
- if _, ok := sf.seen[taskid]; ok {
+ if _, ok := sf.seen[string(task)]; ok {
sf.dups++
} else {
sf.trie.TryGet(task)
- sf.seen[taskid] = struct{}{}
+ sf.seen[string(task)] = struct{}{}
}
}
}
diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go
index ecdfa67f0..10a172294 100644
--- a/core/state_prefetcher.go
+++ b/core/state_prefetcher.go
@@ -67,7 +67,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
if err != nil {
return // Also invalid block, bail out
}
- statedb.Prepare(tx.Hash(), block.Hash(), i)
+ statedb.Prepare(tx.Hash(), i)
if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil {
return // Ugh, something went horribly wrong, bail out
}
diff --git a/core/state_processor.go b/core/state_processor.go
index 6d91b2db2..09f80380d 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -18,6 +18,7 @@ package core
import (
"fmt"
+ "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@@ -57,11 +58,13 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
var (
- receipts types.Receipts
- usedGas = new(uint64)
- header = block.Header()
- allLogs []*types.Log
- gp = new(GasPool).AddGas(block.GasLimit())
+ receipts types.Receipts
+ usedGas = new(uint64)
+ header = block.Header()
+ blockHash = block.Hash()
+ blockNumber = block.Number()
+ allLogs []*types.Log
+ gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
@@ -77,9 +80,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
pluginBlockProcessingError(tx, block, err)
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
- statedb.Prepare(tx.Hash(), block.Hash(), i)
+ statedb.Prepare(tx.Hash(), i)
pluginPreProcessTransaction(tx, block, i)
- receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, header, tx, usedGas, vmenv)
+ receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
if err != nil {
pluginBlockProcessingError(tx, block, err)
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
@@ -94,7 +97,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return receipts, allLogs, *usedGas, nil
}
-func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
+func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb)
@@ -107,10 +110,10 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
// Update the state with pending changes.
var root []byte
- if config.IsByzantium(header.Number) {
+ if config.IsByzantium(blockNumber) {
statedb.Finalise(true)
} else {
- root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
}
*usedGas += result.UsedGas
@@ -131,10 +134,10 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
}
// Set the receipt logs and create the bloom filter.
- receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
- receipt.BlockHash = statedb.BlockHash()
- receipt.BlockNumber = header.Number
+ receipt.BlockHash = blockHash
+ receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, err
}
@@ -151,5 +154,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
- return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv)
+ return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
diff --git a/core/tx_journal.go b/core/tx_journal.go
index 41b5156d4..d282126a0 100644
--- a/core/tx_journal.go
+++ b/core/tx_journal.go
@@ -138,7 +138,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
journal.writer = nil
}
// Generate a new journal with the contents of the current pool
- replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
+ replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
@@ -158,7 +158,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
if err = os.Rename(journal.path+".new", journal.path); err != nil {
return err
}
- sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755)
+ sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
diff --git a/core/tx_pool.go b/core/tx_pool.go
index a583c3d53..c5b604748 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -494,6 +494,23 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
return pending, queued
}
+// ContentFrom retrieves the data content of the transaction pool, returning the
+// pending as well as queued transactions of this address, grouped by nonce.
+func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ var pending types.Transactions
+ if list, ok := pool.pending[addr]; ok {
+ pending = list.Flatten()
+ }
+ var queued types.Transactions
+ if list, ok := pool.queue[addr]; ok {
+ queued = list.Flatten()
+ }
+ return pending, queued
+}
+
// Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
diff --git a/core/types/block.go b/core/types/block.go
index d189c86c2..360f1eb47 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -129,6 +129,11 @@ func (h *Header) SanityCheck() error {
if eLen := len(h.Extra); eLen > 100*1024 {
return fmt.Errorf("too large block extradata: size %d", eLen)
}
+ if h.BaseFee != nil {
+ if bfLen := h.BaseFee.BitLen(); bfLen > 256 {
+ return fmt.Errorf("too large base fee: bitlen %d", bfLen)
+ }
+ }
return nil
}
diff --git a/core/types/gen_log_json.go b/core/types/gen_log_json.go
index 90e1c14d9..3ffa9c2fe 100644
--- a/core/types/gen_log_json.go
+++ b/core/types/gen_log_json.go
@@ -18,12 +18,12 @@ func (l Log) MarshalJSON() ([]byte, error) {
Address common.Address `json:"address" gencodec:"required"`
Topics []common.Hash `json:"topics" gencodec:"required"`
Data hexutil.Bytes `json:"data" gencodec:"required"`
- BlockNumber hexutil.Uint64 `json:"blockNumber"`
- TxHash common.Hash `json:"transactionHash" gencodec:"required"`
- TxIndex hexutil.Uint `json:"transactionIndex"`
- BlockHash common.Hash `json:"blockHash"`
- Index hexutil.Uint `json:"logIndex"`
- Removed bool `json:"removed"`
+ BlockNumber hexutil.Uint64 `json:"blockNumber" rlp:"-"`
+ TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
+ TxIndex hexutil.Uint `json:"transactionIndex" rlp:"-"`
+ BlockHash common.Hash `json:"blockHash" rlp:"-"`
+ Index hexutil.Uint `json:"logIndex" rlp:"-"`
+ Removed bool `json:"removed" rlp:"-"`
}
var enc Log
enc.Address = l.Address
@@ -44,12 +44,12 @@ func (l *Log) UnmarshalJSON(input []byte) error {
Address *common.Address `json:"address" gencodec:"required"`
Topics []common.Hash `json:"topics" gencodec:"required"`
Data *hexutil.Bytes `json:"data" gencodec:"required"`
- BlockNumber *hexutil.Uint64 `json:"blockNumber"`
- TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
- TxIndex *hexutil.Uint `json:"transactionIndex"`
- BlockHash *common.Hash `json:"blockHash"`
- Index *hexutil.Uint `json:"logIndex"`
- Removed *bool `json:"removed"`
+ BlockNumber *hexutil.Uint64 `json:"blockNumber" rlp:"-"`
+ TxHash *common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
+ TxIndex *hexutil.Uint `json:"transactionIndex" rlp:"-"`
+ BlockHash *common.Hash `json:"blockHash" rlp:"-"`
+ Index *hexutil.Uint `json:"logIndex" rlp:"-"`
+ Removed *bool `json:"removed" rlp:"-"`
}
var dec Log
if err := json.Unmarshal(input, &dec); err != nil {
diff --git a/core/types/log.go b/core/types/log.go
index 87865bdb2..067708db3 100644
--- a/core/types/log.go
+++ b/core/types/log.go
@@ -17,11 +17,8 @@
package types
import (
- "io"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/rlp"
)
//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
@@ -40,19 +37,19 @@ type Log struct {
// Derived fields. These fields are filled in by the node
// but not secured by consensus.
// block in which the transaction was included
- BlockNumber uint64 `json:"blockNumber"`
+ BlockNumber uint64 `json:"blockNumber" rlp:"-"`
// hash of the transaction
- TxHash common.Hash `json:"transactionHash" gencodec:"required"`
+ TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
// index of the transaction in the block
- TxIndex uint `json:"transactionIndex"`
+ TxIndex uint `json:"transactionIndex" rlp:"-"`
// hash of the block in which the transaction was included
- BlockHash common.Hash `json:"blockHash"`
+ BlockHash common.Hash `json:"blockHash" rlp:"-"`
// index of the log in the block
- Index uint `json:"logIndex"`
+ Index uint `json:"logIndex" rlp:"-"`
// The Removed field is true if this log was reverted due to a chain reorganisation.
// You must pay attention to this field if you receive logs through a filter query.
- Removed bool `json:"removed"`
+ Removed bool `json:"removed" rlp:"-"`
}
type logMarshaling struct {
@@ -61,52 +58,3 @@ type logMarshaling struct {
TxIndex hexutil.Uint
Index hexutil.Uint
}
-
-type rlpLog struct {
- Address common.Address
- Topics []common.Hash
- Data []byte
-}
-
-// rlpStorageLog is the storage encoding of a log.
-type rlpStorageLog rlpLog
-
-// EncodeRLP implements rlp.Encoder.
-func (l *Log) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
-}
-
-// DecodeRLP implements rlp.Decoder.
-func (l *Log) DecodeRLP(s *rlp.Stream) error {
- var dec rlpLog
- err := s.Decode(&dec)
- if err == nil {
- l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
- }
- return err
-}
-
-// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
-// a log including non-consensus fields.
-type LogForStorage Log
-
-// EncodeRLP implements rlp.Encoder.
-func (l *LogForStorage) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, rlpStorageLog{
- Address: l.Address,
- Topics: l.Topics,
- Data: l.Data,
- })
-}
-
-// DecodeRLP implements rlp.Decoder.
-//
-// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later.
-func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
- var dec rlpStorageLog
- if err := s.Decode(&dec); err != nil {
- return err
- }
- *l = LogForStorage{Address: dec.Address, Topics: dec.Topics, Data: dec.Data}
- return nil
-}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index b949bd2bd..d9029ca2e 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -94,7 +94,7 @@ type receiptRLP struct {
type storedReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
- Logs []*LogForStorage
+ Logs []*Log
}
// NewReceipt creates a barebone transaction receipt, copying the init fields.
@@ -217,10 +217,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
enc := &storedReceiptRLP{
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
CumulativeGasUsed: r.CumulativeGasUsed,
- Logs: make([]*LogForStorage, len(r.Logs)),
- }
- for i, log := range r.Logs {
- enc.Logs[i] = (*LogForStorage)(log)
+ Logs: r.Logs,
}
return rlp.Encode(w, enc)
}
@@ -235,10 +232,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
return err
}
r.CumulativeGasUsed = stored.CumulativeGasUsed
- r.Logs = make([]*Log, len(stored.Logs))
- for i, log := range stored.Logs {
- r.Logs[i] = (*Log)(log)
- }
+ r.Logs = stored.Logs
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
return nil
}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 920318a16..a556f4b57 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -67,7 +67,7 @@ func NewTx(inner TxData) *Transaction {
// TxData is the underlying data of a transaction.
//
-// This is implemented by LegacyTx and AccessListTx.
+// This is implemented by DynamicFeeTx, LegacyTx and AccessListTx.
type TxData interface {
txType() byte // returns the type ID
copy() TxData // creates a deep copy and initializes all fields
diff --git a/core/vm/evm.go b/core/vm/evm.go
index c7e25eb30..896476673 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -17,7 +17,6 @@
package vm
import (
- "errors"
"math/big"
"sync/atomic"
"time"
@@ -58,24 +57,6 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
return p, ok
}
-// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
-func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
- for _, interpreter := range evm.interpreters {
- if interpreter.CanRun(contract.Code) {
- if evm.interpreter != interpreter {
- // Ensure that the interpreter pointer is set back
- // to its current value upon return.
- defer func(i Interpreter) {
- evm.interpreter = i
- }(evm.interpreter)
- evm.interpreter = interpreter
- }
- return interpreter.Run(contract, input, readOnly)
- }
- }
- return nil, errors.New("no compatible interpreter")
-}
-
// BlockContext provides the EVM with auxiliary information. Once provided
// it shouldn't be modified.
type BlockContext struct {
@@ -131,8 +112,7 @@ type EVM struct {
Config Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
- interpreters []Interpreter
- interpreter Interpreter
+ interpreter *EVMInterpreter
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
abort int32
@@ -146,36 +126,14 @@ type EVM struct {
// only ever be used *once*.
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
- Context: blockCtx,
- TxContext: txCtx,
- StateDB: statedb,
- Config: config,
- chainConfig: chainConfig,
- chainRules: chainConfig.Rules(blockCtx.BlockNumber),
- interpreters: make([]Interpreter, 0, 1),
+ Context: blockCtx,
+ TxContext: txCtx,
+ StateDB: statedb,
+ Config: config,
+ chainConfig: chainConfig,
+ chainRules: chainConfig.Rules(blockCtx.BlockNumber),
}
-
- if chainConfig.IsEWASM(blockCtx.BlockNumber) {
- // to be implemented by EVM-C and Wagon PRs.
- // if vmConfig.EWASMInterpreter != "" {
- // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":")
- // path := extIntOpts[0]
- // options := []string{}
- // if len(extIntOpts) > 1 {
- // options = extIntOpts[1..]
- // }
- // evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
- // } else {
- // evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
- // }
- panic("No supported ewasm interpreter yet.")
- }
-
- // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
- // as we always want to have the built-in EVM as the failover option.
- evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config))
- evm.interpreter = evm.interpreters[0]
-
+ evm.interpreter = NewEVMInterpreter(evm, config)
return evm
}
@@ -198,7 +156,7 @@ func (evm *EVM) Cancelled() bool {
}
// Interpreter returns the current interpreter
-func (evm *EVM) Interpreter() Interpreter {
+func (evm *EVM) Interpreter() *EVMInterpreter {
return evm.interpreter
}
@@ -256,7 +214,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
- ret, err = run(evm, contract, input, false)
+ ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
}
@@ -308,7 +266,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
- ret, err = run(evm, contract, input, false)
+ ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
if err != nil {
@@ -343,7 +301,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
- ret, err = run(evm, contract, input, false)
+ ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
if err != nil {
@@ -394,7 +352,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
- ret, err = run(evm, contract, input, true)
+ ret, err = evm.interpreter.Run(contract, input, true)
gas = contract.Gas
}
if err != nil {
@@ -462,7 +420,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
start := time.Now()
- ret, err := run(evm, contract, nil, false)
+ ret, err := evm.interpreter.Run(contract, nil, false)
// Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index ac04afe8b..365f3b791 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -4,11 +4,11 @@ package vm
import (
"encoding/json"
- "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
)
var _ = (*structLogMarshaling)(nil)
@@ -22,8 +22,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
- Stack []*math.HexOrDecimal256 `json:"stack"`
- ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
+ Stack []uint256.Int `json:"stack"`
ReturnData hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
@@ -39,12 +38,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.GasCost = math.HexOrDecimal64(s.GasCost)
enc.Memory = s.Memory
enc.MemorySize = s.MemorySize
- if s.Stack != nil {
- enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack))
- for k, v := range s.Stack {
- enc.Stack[k] = (*math.HexOrDecimal256)(v)
- }
- }
+ enc.Stack = s.Stack
enc.ReturnData = s.ReturnData
enc.Storage = s.Storage
enc.Depth = s.Depth
@@ -64,7 +58,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
- Stack []*math.HexOrDecimal256 `json:"stack"`
+ Stack []uint256.Int `json:"stack"`
ReturnData *hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
@@ -94,10 +88,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.MemorySize = *dec.MemorySize
}
if dec.Stack != nil {
- s.Stack = make([]*big.Int, len(dec.Stack))
- for k, v := range dec.Stack {
- s.Stack[k] = (*big.Int)(v)
- }
+ s.Stack = dec.Stack
}
if dec.ReturnData != nil {
s.ReturnData = *dec.ReturnData
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 575de1376..560d26a0b 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -96,7 +96,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
- evmInterpreter = env.interpreter.(*EVMInterpreter)
+ evmInterpreter = env.interpreter
)
for i, test := range tests {
@@ -234,7 +234,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
- interpreter = env.interpreter.(*EVMInterpreter)
+ interpreter = env.interpreter
)
result := make([]TwoOperandTestcase, len(args))
for i, param := range args {
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 0efa78c97..9cf0c4e2c 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -35,34 +35,9 @@ type Config struct {
JumpTable [256]*operation // EVM instruction table, automatically populated if unset
- EWASMInterpreter string // External EWASM interpreter options
- EVMInterpreter string // External EVM interpreter options
-
ExtraEips []int // Additional EIPS that are to be enabled
}
-// Interpreter is used to run Ethereum based contracts and will utilise the
-// passed environment to query external sources for state information.
-// The Interpreter will run the byte code VM based on the passed
-// configuration.
-type Interpreter interface {
- // Run loops and evaluates the contract's code with the given input data and returns
- // the return byte-slice and an error if one occurred.
- Run(contract *Contract, input []byte, static bool) ([]byte, error)
- // CanRun tells if the contract, passed as an argument, can be
- // run by the current interpreter. This is meant so that the
- // caller can do something like:
- //
- // ```golang
- // for _, interpreter := range interpreters {
- // if interpreter.CanRun(contract.code) {
- // interpreter.Run(contract.code, input)
- // }
- // }
- // ```
- CanRun([]byte) bool
-}
-
// ScopeContext contains the things that are per-call, such as stack and memory,
// but not transients like pc and gas
type ScopeContext struct {
@@ -303,9 +278,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
return nil, nil
}
-
-// CanRun tells if the contract, passed as an argument, can be
-// run by the current interpreter.
-func (in *EVMInterpreter) CanRun(code []byte) bool {
- return true
-}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 9ccaafc77..900a5e585 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
+ "github.com/holiman/uint256"
)
// Storage represents a contract's storage.
@@ -66,7 +67,7 @@ type StructLog struct {
GasCost uint64 `json:"gasCost"`
Memory []byte `json:"memory"`
MemorySize int `json:"memSize"`
- Stack []*big.Int `json:"stack"`
+ Stack []uint256.Int `json:"stack"`
ReturnData []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
@@ -76,7 +77,6 @@ type StructLog struct {
// overrides for gencodec
type structLogMarshaling struct {
- Stack []*math.HexOrDecimal256
Gas math.HexOrDecimal64
GasCost math.HexOrDecimal64
Memory hexutil.Bytes
@@ -135,6 +135,14 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
return logger
}
+// Reset clears the data held by the logger.
+func (l *StructLogger) Reset() {
+ l.storage = make(map[common.Address]Storage)
+ l.output = make([]byte, 0)
+ l.logs = l.logs[:0]
+ l.err = nil
+}
+
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
@@ -157,16 +165,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
copy(mem, memory.Data())
}
// Copy a snapshot of the current stack state to a new buffer
- var stck []*big.Int
+ var stck []uint256.Int
if !l.cfg.DisableStack {
- stck = make([]*big.Int, len(stack.Data()))
+ stck = make([]uint256.Int, len(stack.Data()))
for i, item := range stack.Data() {
- stck[i] = new(big.Int).Set(item.ToBig())
+ stck[i] = item
}
}
// Copy a snapshot of the current storage to a new container
var storage Storage
- if !l.cfg.DisableStorage {
+ if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) {
// initialise new changed values storage container for this contract
// if not present.
if l.storage[contract.Address()] == nil {
@@ -179,16 +187,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
value = env.StateDB.GetState(contract.Address(), address)
)
l.storage[contract.Address()][address] = value
- }
- // capture SSTORE opcodes and record the written entry in the local storage.
- if op == SSTORE && stack.len() >= 2 {
+ storage = l.storage[contract.Address()].Copy()
+ } else if op == SSTORE && stack.len() >= 2 {
+ // capture SSTORE opcodes and record the written entry in the local storage.
var (
value = common.Hash(stack.data[stack.len()-2].Bytes32())
address = common.Hash(stack.data[stack.len()-1].Bytes32())
)
l.storage[contract.Address()][address] = value
+ storage = l.storage[contract.Address()].Copy()
}
- storage = l.storage[contract.Address()].Copy()
}
var rdata []byte
if !l.cfg.DisableReturnData {
@@ -238,7 +246,7 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
if len(log.Stack) > 0 {
fmt.Fprintln(writer, "Stack:")
for i := len(log.Stack) - 1; i >= 0; i-- {
- fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
+ fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex())
}
}
if len(log.Memory) > 0 {
@@ -314,7 +322,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
// format stack
var a []string
for _, elem := range stack.data {
- a = append(a, fmt.Sprintf("%v", elem.String()))
+ a = append(a, elem.Hex())
}
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
fmt.Fprintf(t.out, "%10v |", b)
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 93878b980..5210f479f 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -57,7 +57,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
Gas: gas,
GasCost: cost,
MemorySize: memory.Len(),
- Storage: nil,
Depth: depth,
RefundCounter: env.StateDB.GetRefund(),
Err: err,
@@ -66,12 +65,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
- //TODO(@holiman) improve this
- logstack := make([]*big.Int, len(stack.Data()))
- for i, item := range stack.Data() {
- logstack[i] = item.ToBig()
- }
- log.Stack = logstack
+ log.Stack = stack.data
}
if !l.cfg.DisableReturnData {
log.ReturnData = rData
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 0bbfd4469..730d8374b 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -30,7 +30,6 @@ type dummyContractRef struct {
calledForEach bool
}
-func (dummyContractRef) ReturnGas(*big.Int) {}
func (dummyContractRef) Address() common.Address { return common.Address{} }
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
func (dummyContractRef) SetCode(common.Hash, []byte) {}
diff --git a/crypto/secp256k1/panic_cb.go b/crypto/secp256k1/panic_cb.go
index 262846fd8..5da2bea37 100644
--- a/crypto/secp256k1/panic_cb.go
+++ b/crypto/secp256k1/panic_cb.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
-// +build !gofuzz cgo
+// +build !gofuzz
+// +build cgo
package secp256k1
diff --git a/crypto/secp256k1/scalar_mult_cgo.go b/crypto/secp256k1/scalar_mult_cgo.go
index 34998ad1a..f28a1c782 100644
--- a/crypto/secp256k1/scalar_mult_cgo.go
+++ b/crypto/secp256k1/scalar_mult_cgo.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
-// +build !gofuzz cgo
+// +build !gofuzz
+// +build cgo
package secp256k1
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index 9e942ac6f..a1bcf7796 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
-// +build !gofuzz cgo
+// +build !gofuzz
+// +build cgo
// Package secp256k1 wraps the bitcoin secp256k1 C library.
package secp256k1
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
index 1fe84509e..843360298 100644
--- a/crypto/signature_cgo.go
+++ b/crypto/signature_cgo.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build !nacl,!js,cgo
+// +build !nacl,!js,cgo,!gofuzz
package crypto
diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go
index 067d32e13..77c8a1db0 100644
--- a/crypto/signature_nocgo.go
+++ b/crypto/signature_nocgo.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build nacl js !cgo
+// +build nacl js !cgo gofuzz
package crypto
diff --git a/eth/api.go b/eth/api.go
index 6a22c9e41..8b96d1f31 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -129,6 +129,12 @@ func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
return true
}
+// SetGasLimit sets the gaslimit to target towards during mining.
+func (api *PrivateMinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool {
+ api.e.Miner().SetGasCeil(uint64(gasLimit))
+ return true
+}
+
// SetEtherbase sets the etherbase of the miner
func (api *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool {
api.e.SetEtherbase(etherbase)
diff --git a/eth/api_backend.go b/eth/api_backend.go
index e6810f2a9..f22462c7c 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -133,6 +133,10 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
return nil, errors.New("invalid arguments; neither block nor hash specified")
}
+func (b *EthAPIBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ return b.eth.miner.PendingBlockAndReceipts()
+}
+
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
// Pending state is only known by the miner
if number == rpc.PendingBlockNumber {
@@ -263,6 +267,10 @@ func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions,
return b.eth.TxPool().Content()
}
+func (b *EthAPIBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ return b.eth.TxPool().ContentFrom(addr)
+}
+
func (b *EthAPIBackend) TxPool() *core.TxPool {
return b.eth.TxPool()
}
@@ -279,6 +287,10 @@ func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error)
return b.gpo.SuggestTipCap(ctx)
}
+func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {
+ return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
+}
+
func (b *EthAPIBackend) ChainDb() ethdb.Database {
return b.eth.ChainDb()
}
diff --git a/eth/backend.go b/eth/backend.go
index c2480b378..78cbe7a9e 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -175,8 +175,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
var (
vmConfig = vm.Config{
EnablePreimageRecording: config.EnablePreimageRecording,
- EWASMInterpreter: config.EWASMInterpreter,
- EVMInterpreter: config.EVMInterpreter,
}
cacheConfig = &core.CacheConfig{
TrieCleanLimit: config.TrieCleanCache,
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index c074ac5af..2622c4a14 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -178,7 +178,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD
from, _ := types.Sender(signer, tx)
// Execute the transaction
- env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
+ env.state.Prepare(tx.Hash(), env.tcount)
err = env.commitTransaction(tx, coinbase)
switch err {
case core.ErrGasLimitReached:
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index ac7edc2c6..04ec12cfa 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -40,10 +40,10 @@ const (
)
var (
- blockCacheMaxItems = 8192 // Maximum number of blocks to cache before throttling the download
- blockCacheInitialItems = 2048 // Initial number of blocks to start fetching, before we know the sizes of the blocks
- blockCacheMemory = 64 * 1024 * 1024 // Maximum amount of memory to use for block caching
- blockCacheSizeWeight = 0.1 // Multiplier to approximate the average block size based on past ones
+ blockCacheMaxItems = 8192 // Maximum number of blocks to cache before throttling the download
+ blockCacheInitialItems = 2048 // Initial number of blocks to start fetching, before we know the sizes of the blocks
+ blockCacheMemory = 256 * 1024 * 1024 // Maximum amount of memory to use for block caching
+ blockCacheSizeWeight = 0.1 // Multiplier to approximate the average block size based on past ones
)
var (
@@ -783,8 +783,9 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
+ trieHasher := trie.NewStackTrie(nil)
validate := func(index int, header *types.Header) error {
- if types.DeriveSha(types.Transactions(txLists[index]), trie.NewStackTrie(nil)) != header.TxHash {
+ if types.DeriveSha(types.Transactions(txLists[index]), trieHasher) != header.TxHash {
return errInvalidBody
}
if types.CalcUncleHash(uncleLists[index]) != header.UncleHash {
@@ -808,8 +809,9 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi
func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
+ trieHasher := trie.NewStackTrie(nil)
validate := func(index int, header *types.Header) error {
- if types.DeriveSha(types.Receipts(receiptList[index]), trie.NewStackTrie(nil)) != header.ReceiptHash {
+ if types.DeriveSha(types.Receipts(receiptList[index]), trieHasher) != header.ReceiptHash {
return errInvalidReceipt
}
return nil
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index 349c8da6c..0913b69d7 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -41,18 +41,22 @@ import (
// FullNodeGPO contains default gasprice oracle settings for full node.
var FullNodeGPO = gasprice.Config{
- Blocks: 20,
- Percentile: 60,
- MaxPrice: gasprice.DefaultMaxPrice,
- IgnorePrice: gasprice.DefaultIgnorePrice,
+ Blocks: 20,
+ Percentile: 60,
+ MaxHeaderHistory: 0,
+ MaxBlockHistory: 0,
+ MaxPrice: gasprice.DefaultMaxPrice,
+ IgnorePrice: gasprice.DefaultIgnorePrice,
}
// LightClientGPO contains default gasprice oracle settings for light client.
var LightClientGPO = gasprice.Config{
- Blocks: 2,
- Percentile: 60,
- MaxPrice: gasprice.DefaultMaxPrice,
- IgnorePrice: gasprice.DefaultIgnorePrice,
+ Blocks: 2,
+ Percentile: 60,
+ MaxHeaderHistory: 300,
+ MaxBlockHistory: 5,
+ MaxPrice: gasprice.DefaultMaxPrice,
+ IgnorePrice: gasprice.DefaultIgnorePrice,
}
// Defaults contains default settings for use on the Ethereum main net.
@@ -182,12 +186,6 @@ type Config struct {
// Miscellaneous options
DocRoot string `toml:"-"`
- // Type of the EWASM interpreter ("" for default)
- EWASMInterpreter string
-
- // Type of the EVM interpreter ("" for default)
- EVMInterpreter string
-
// RPCGasCap is the global gas cap for eth-call variants.
RPCGasCap uint64
diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go
index ca93b2ad0..2310dd449 100644
--- a/eth/ethconfig/gen_config.go
+++ b/eth/ethconfig/gen_config.go
@@ -3,6 +3,7 @@
package ethconfig
import (
+ "math/big"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -53,12 +54,11 @@ func (c Config) MarshalTOML() (interface{}, error) {
GPO gasprice.Config
EnablePreimageRecording bool
DocRoot string `toml:"-"`
- EWASMInterpreter string
- EVMInterpreter string
- RPCGasCap uint64 `toml:",omitempty"`
- RPCTxFeeCap float64 `toml:",omitempty"`
+ RPCGasCap uint64
+ RPCTxFeeCap float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
+ OverrideLondon *big.Int `toml:",omitempty"`
}
var enc Config
enc.Genesis = c.Genesis
@@ -97,12 +97,11 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.GPO = c.GPO
enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.DocRoot = c.DocRoot
- enc.EWASMInterpreter = c.EWASMInterpreter
- enc.EVMInterpreter = c.EVMInterpreter
enc.RPCGasCap = c.RPCGasCap
enc.RPCTxFeeCap = c.RPCTxFeeCap
enc.Checkpoint = c.Checkpoint
enc.CheckpointOracle = c.CheckpointOracle
+ enc.OverrideLondon = c.OverrideLondon
return &enc, nil
}
@@ -145,12 +144,11 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
GPO *gasprice.Config
EnablePreimageRecording *bool
DocRoot *string `toml:"-"`
- EWASMInterpreter *string
- EVMInterpreter *string
- RPCGasCap *uint64 `toml:",omitempty"`
- RPCTxFeeCap *float64 `toml:",omitempty"`
+ RPCGasCap *uint64
+ RPCTxFeeCap *float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
+ OverrideLondon *big.Int `toml:",omitempty"`
}
var dec Config
if err := unmarshal(&dec); err != nil {
@@ -264,12 +262,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.DocRoot != nil {
c.DocRoot = *dec.DocRoot
}
- if dec.EWASMInterpreter != nil {
- c.EWASMInterpreter = *dec.EWASMInterpreter
- }
- if dec.EVMInterpreter != nil {
- c.EVMInterpreter = *dec.EVMInterpreter
- }
if dec.RPCGasCap != nil {
c.RPCGasCap = *dec.RPCGasCap
}
@@ -282,5 +274,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.CheckpointOracle != nil {
c.CheckpointOracle = dec.CheckpointOracle
}
+ if dec.OverrideLondon != nil {
+ c.OverrideLondon = dec.OverrideLondon
+ }
return nil
}
diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go
index 3177a877e..45983c97c 100644
--- a/eth/fetcher/block_fetcher.go
+++ b/eth/fetcher/block_fetcher.go
@@ -833,15 +833,17 @@ func (f *BlockFetcher) importBlocks(peer string, block *types.Block) {
// internal state.
func (f *BlockFetcher) forgetHash(hash common.Hash) {
// Remove all pending announces and decrement DOS counters
- for _, announce := range f.announced[hash] {
- f.announces[announce.origin]--
- if f.announces[announce.origin] <= 0 {
- delete(f.announces, announce.origin)
+ if announceMap, ok := f.announced[hash]; ok {
+ for _, announce := range announceMap {
+ f.announces[announce.origin]--
+ if f.announces[announce.origin] <= 0 {
+ delete(f.announces, announce.origin)
+ }
+ }
+ delete(f.announced, hash)
+ if f.announceChangeHook != nil {
+ f.announceChangeHook(hash, false)
}
- }
- delete(f.announced, hash)
- if f.announceChangeHook != nil {
- f.announceChangeHook(hash, false)
}
// Remove any pending fetches and decrement the DOS counters
if announce := f.fetching[hash]; announce != nil {
diff --git a/eth/fetcher/block_fetcher_test.go b/eth/fetcher/block_fetcher_test.go
index 54b5b13fb..b6d1125b5 100644
--- a/eth/fetcher/block_fetcher_test.go
+++ b/eth/fetcher/block_fetcher_test.go
@@ -698,6 +698,7 @@ func testInvalidNumberAnnouncement(t *testing.T, light bool) {
badBodyFetcher := tester.makeBodyFetcher("bad", blocks, 0)
imported := make(chan interface{})
+ announced := make(chan interface{})
tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
if light {
if header == nil {
@@ -712,9 +713,23 @@ func testInvalidNumberAnnouncement(t *testing.T, light bool) {
}
}
// Announce a block with a bad number, check for immediate drop
+ tester.fetcher.announceChangeHook = func(hash common.Hash, b bool) {
+ announced <- nil
+ }
tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), badHeaderFetcher, badBodyFetcher)
+ verifyAnnounce := func() {
+ for i := 0; i < 2; i++ {
+ select {
+ case <-announced:
+ continue
+ case <-time.After(1 * time.Second):
+ t.Fatal("announce timeout")
+ return
+ }
+ }
+ }
+ verifyAnnounce()
verifyImportEvent(t, imported, false)
-
tester.lock.RLock()
dropped := tester.drops["bad"]
tester.lock.RUnlock()
@@ -722,11 +737,11 @@ func testInvalidNumberAnnouncement(t *testing.T, light bool) {
if !dropped {
t.Fatalf("peer with invalid numbered announcement not dropped")
}
-
goodHeaderFetcher := tester.makeHeaderFetcher("good", blocks, -gatherSlack)
goodBodyFetcher := tester.makeBodyFetcher("good", blocks, 0)
// Make sure a good announcement passes without a drop
tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), goodHeaderFetcher, goodBodyFetcher)
+ verifyAnnounce()
verifyImportEvent(t, imported, true)
tester.lock.RLock()
diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go
new file mode 100644
index 000000000..a14dd594b
--- /dev/null
+++ b/eth/gasprice/feehistory.go
@@ -0,0 +1,300 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package gasprice
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/big"
+ "sort"
+ "sync/atomic"
+
+ "github.com/ethereum/go-ethereum/consensus/misc"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+var (
+ errInvalidPercentile = errors.New("invalid reward percentile")
+ errRequestBeyondHead = errors.New("request beyond head block")
+)
+
+const (
+ // maxFeeHistory is the maximum number of blocks that can be retrieved for a
+ // fee history request.
+ maxFeeHistory = 1024
+
+ // maxBlockFetchers is the max number of goroutines to spin up to pull blocks
+ // for the fee history calculation (mostly relevant for LES).
+ maxBlockFetchers = 4
+)
+
+// blockFees represents a single block for processing
+type blockFees struct {
+ // set by the caller
+ blockNumber rpc.BlockNumber
+ header *types.Header
+ block *types.Block // only set if reward percentiles are requested
+ receipts types.Receipts
+ // filled by processBlock
+ reward []*big.Int
+ baseFee, nextBaseFee *big.Int
+ gasUsedRatio float64
+ err error
+}
+
+// txGasAndReward is sorted in ascending order based on reward
+type (
+ txGasAndReward struct {
+ gasUsed uint64
+ reward *big.Int
+ }
+ sortGasAndReward []txGasAndReward
+)
+
+func (s sortGasAndReward) Len() int { return len(s) }
+func (s sortGasAndReward) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+func (s sortGasAndReward) Less(i, j int) bool {
+ return s[i].reward.Cmp(s[j].reward) < 0
+}
+
+// processBlock takes a blockFees structure with the blockNumber, the header and optionally
+// the block field filled in, retrieves the block from the backend if not present yet and
+// fills in the rest of the fields.
+func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
+ chainconfig := oracle.backend.ChainConfig()
+ if bf.baseFee = bf.header.BaseFee; bf.baseFee == nil {
+ bf.baseFee = new(big.Int)
+ }
+ if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
+ bf.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header)
+ } else {
+ bf.nextBaseFee = new(big.Int)
+ }
+ bf.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
+ if len(percentiles) == 0 {
+ // rewards were not requested, return null
+ return
+ }
+ if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
+ log.Error("Block or receipts are missing while reward percentiles are requested")
+ return
+ }
+
+ bf.reward = make([]*big.Int, len(percentiles))
+ if len(bf.block.Transactions()) == 0 {
+ // return an all zero row if there are no transactions to gather data from
+ for i := range bf.reward {
+ bf.reward[i] = new(big.Int)
+ }
+ return
+ }
+
+ sorter := make(sortGasAndReward, len(bf.block.Transactions()))
+ for i, tx := range bf.block.Transactions() {
+ reward, _ := tx.EffectiveGasTip(bf.block.BaseFee())
+ sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward}
+ }
+ sort.Sort(sorter)
+
+ var txIndex int
+ sumGasUsed := sorter[0].gasUsed
+
+ for i, p := range percentiles {
+ thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100)
+ for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 {
+ txIndex++
+ sumGasUsed += sorter[txIndex].gasUsed
+ }
+ bf.reward[i] = sorter[txIndex].reward
+ }
+}
+
+// resolveBlockRange resolves the specified block range to absolute block numbers while also
+// enforcing backend specific limitations. The pending block and corresponding receipts are
+// also returned if requested and available.
+// Note: an error is only returned if retrieving the head header has failed. If there are no
+// retrievable blocks in the specified range then zero block count is returned with no error.
+func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks, maxHistory int) (*types.Block, []*types.Receipt, rpc.BlockNumber, int, error) {
+ var (
+ headBlock rpc.BlockNumber
+ pendingBlock *types.Block
+ pendingReceipts types.Receipts
+ )
+ // query either pending block or head header and set headBlock
+ if lastBlock == rpc.PendingBlockNumber {
+ if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
+ lastBlock = rpc.BlockNumber(pendingBlock.NumberU64())
+ headBlock = lastBlock - 1
+ } else {
+ // pending block not supported by backend, process until latest block
+ lastBlock = rpc.LatestBlockNumber
+ blocks--
+ if blocks == 0 {
+ return nil, nil, 0, 0, nil
+ }
+ }
+ }
+ if pendingBlock == nil {
+ // if pending block is not fetched then we retrieve the head header to get the head block number
+ if latestHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err == nil {
+ headBlock = rpc.BlockNumber(latestHeader.Number.Uint64())
+ } else {
+ return nil, nil, 0, 0, err
+ }
+ }
+ if lastBlock == rpc.LatestBlockNumber {
+ lastBlock = headBlock
+ } else if pendingBlock == nil && lastBlock > headBlock {
+ return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock)
+ }
+ if maxHistory != 0 {
+ // limit retrieval to the given number of latest blocks
+ if tooOldCount := int64(headBlock) - int64(maxHistory) - int64(lastBlock) + int64(blocks); tooOldCount > 0 {
+ // tooOldCount is the number of requested blocks that are too old to be served
+ if int64(blocks) > tooOldCount {
+ blocks -= int(tooOldCount)
+ } else {
+ return nil, nil, 0, 0, nil
+ }
+ }
+ }
+ // ensure not trying to retrieve before genesis
+ if rpc.BlockNumber(blocks) > lastBlock+1 {
+ blocks = int(lastBlock + 1)
+ }
+ return pendingBlock, pendingReceipts, lastBlock, blocks, nil
+}
+
+// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
+// The range can be specified either with absolute block numbers or ending with the latest
+// or pending block. Backends may or may not support gathering data from the pending block
+// or blocks older than a certain age (specified in maxHistory). The first block of the
+// actually processed range is returned to avoid ambiguity when parts of the requested range
+// are not available or when the head has changed during processing this request.
+// Three arrays are returned based on the processed blocks:
+// - reward: the requested percentiles of effective priority fees per gas of transactions in each
+// block, sorted in ascending order and weighted by gas used.
+// - baseFee: base fee per gas in the given block
+// - gasUsedRatio: gasUsed/gasLimit in the given block
+// Note: baseFee includes the next block after the newest of the returned range, because this
+// value can be derived from the newest block.
+func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (rpc.BlockNumber, [][]*big.Int, []*big.Int, []float64, error) {
+ if blocks < 1 {
+ return 0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
+ }
+ if blocks > maxFeeHistory {
+ log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
+ blocks = maxFeeHistory
+ }
+ for i, p := range rewardPercentiles {
+ if p < 0 || p > 100 {
+ return 0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
+ }
+ if i > 0 && p < rewardPercentiles[i-1] {
+ return 0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
+ }
+ }
+ // Only process blocks if reward percentiles were requested
+ maxHistory := oracle.maxHeaderHistory
+ if len(rewardPercentiles) != 0 {
+ maxHistory = oracle.maxBlockHistory
+ }
+ var (
+ pendingBlock *types.Block
+ pendingReceipts []*types.Receipt
+ err error
+ )
+ pendingBlock, pendingReceipts, lastBlock, blocks, err = oracle.resolveBlockRange(ctx, lastBlock, blocks, maxHistory)
+ if err != nil || blocks == 0 {
+ return 0, nil, nil, nil, err
+ }
+ oldestBlock := lastBlock + 1 - rpc.BlockNumber(blocks)
+
+ var (
+ next = int64(oldestBlock)
+ results = make(chan *blockFees, blocks)
+ )
+ for i := 0; i < maxBlockFetchers && i < blocks; i++ {
+ go func() {
+ for {
+ // Retrieve the next block number to fetch with this goroutine
+ blockNumber := rpc.BlockNumber(atomic.AddInt64(&next, 1) - 1)
+ if blockNumber > lastBlock {
+ return
+ }
+
+ fees := &blockFees{blockNumber: blockNumber}
+ if pendingBlock != nil && blockNumber >= rpc.BlockNumber(pendingBlock.NumberU64()) {
+ fees.block, fees.receipts = pendingBlock, pendingReceipts
+ } else {
+ if len(rewardPercentiles) != 0 {
+ fees.block, fees.err = oracle.backend.BlockByNumber(ctx, blockNumber)
+ if fees.block != nil && fees.err == nil {
+ fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
+ }
+ } else {
+ fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, blockNumber)
+ }
+ }
+ if fees.block != nil {
+ fees.header = fees.block.Header()
+ }
+ if fees.header != nil {
+ oracle.processBlock(fees, rewardPercentiles)
+ }
+ // send to results even if empty to guarantee that blocks items are sent in total
+ results <- fees
+ }
+ }()
+ }
+ var (
+ reward = make([][]*big.Int, blocks)
+ baseFee = make([]*big.Int, blocks+1)
+ gasUsedRatio = make([]float64, blocks)
+ firstMissing = blocks
+ )
+ for ; blocks > 0; blocks-- {
+ fees := <-results
+ if fees.err != nil {
+ return 0, nil, nil, nil, fees.err
+ }
+ i := int(fees.blockNumber - oldestBlock)
+ if fees.header != nil {
+ reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.reward, fees.baseFee, fees.nextBaseFee, fees.gasUsedRatio
+ } else {
+ // getting no block and no error means we are requesting into the future (might happen because of a reorg)
+ if i < firstMissing {
+ firstMissing = i
+ }
+ }
+ }
+ if firstMissing == 0 {
+ return 0, nil, nil, nil, nil
+ }
+ if len(rewardPercentiles) != 0 {
+ reward = reward[:firstMissing]
+ } else {
+ reward = nil
+ }
+ baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing]
+ return oldestBlock, reward, baseFee, gasUsedRatio, nil
+}
diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go
new file mode 100644
index 000000000..57cfb260c
--- /dev/null
+++ b/eth/gasprice/feehistory_test.go
@@ -0,0 +1,89 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package gasprice
+
+import (
+ "context"
+ "errors"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+func TestFeeHistory(t *testing.T) {
+ var cases = []struct {
+ pending bool
+ maxHeader, maxBlock int
+ count int
+ last rpc.BlockNumber
+ percent []float64
+ expFirst rpc.BlockNumber
+ expCount int
+ expErr error
+ }{
+ {false, 0, 0, 10, 30, nil, 21, 10, nil},
+ {false, 0, 0, 10, 30, []float64{0, 10}, 21, 10, nil},
+ {false, 0, 0, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile},
+ {false, 0, 0, 1000000000, 30, nil, 0, 31, nil},
+ {false, 0, 0, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil},
+ {false, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead},
+ {true, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead},
+ {false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil},
+ {false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil},
+ {false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil},
+ {false, 0, 0, 1, rpc.PendingBlockNumber, nil, 0, 0, nil},
+ {false, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
+ {true, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
+ {true, 0, 0, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
+ }
+ for i, c := range cases {
+ config := Config{
+ MaxHeaderHistory: c.maxHeader,
+ MaxBlockHistory: c.maxBlock,
+ }
+ backend := newTestBackend(t, big.NewInt(16), c.pending)
+ oracle := NewOracle(backend, config)
+
+ first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
+
+ expReward := c.expCount
+ if len(c.percent) == 0 {
+ expReward = 0
+ }
+ expBaseFee := c.expCount
+ if expBaseFee != 0 {
+ expBaseFee++
+ }
+
+ if first != c.expFirst {
+ t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first)
+ }
+ if len(reward) != expReward {
+ t.Fatalf("Test case %d: reward array length mismatch, want %d, got %d", i, expReward, len(reward))
+ }
+ if len(baseFee) != expBaseFee {
+ t.Fatalf("Test case %d: baseFee array length mismatch, want %d, got %d", i, expBaseFee, len(baseFee))
+ }
+ if len(ratio) != c.expCount {
+ t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio))
+ }
+ if err != c.expErr && !errors.Is(err, c.expErr) {
+ t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err)
+ }
+ }
+}
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index 9a800877c..407eeaa28 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -37,17 +37,21 @@ var (
)
type Config struct {
- Blocks int
- Percentile int
- Default *big.Int `toml:",omitempty"`
- MaxPrice *big.Int `toml:",omitempty"`
- IgnorePrice *big.Int `toml:",omitempty"`
+ Blocks int
+ Percentile int
+ MaxHeaderHistory int
+ MaxBlockHistory int
+ Default *big.Int `toml:",omitempty"`
+ MaxPrice *big.Int `toml:",omitempty"`
+ IgnorePrice *big.Int `toml:",omitempty"`
}
// OracleBackend includes all necessary background APIs for oracle.
type OracleBackend interface {
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
+ GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
+ PendingBlockAndReceipts() (*types.Block, types.Receipts)
ChainConfig() *params.ChainConfig
}
@@ -62,8 +66,8 @@ type Oracle struct {
cacheLock sync.RWMutex
fetchLock sync.Mutex
- checkBlocks int
- percentile int
+ checkBlocks, percentile int
+ maxHeaderHistory, maxBlockHistory int
}
// NewOracle returns a new gasprice oracle which can recommend suitable
@@ -96,12 +100,14 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
}
return &Oracle{
- backend: backend,
- lastPrice: params.Default,
- maxPrice: maxPrice,
- ignorePrice: ignorePrice,
- checkBlocks: blocks,
- percentile: percent,
+ backend: backend,
+ lastPrice: params.Default,
+ maxPrice: maxPrice,
+ ignorePrice: ignorePrice,
+ checkBlocks: blocks,
+ percentile: percent,
+ maxHeaderHistory: params.MaxHeaderHistory,
+ maxBlockHistory: params.MaxBlockHistory,
}
}
@@ -111,36 +117,36 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be
// necessary to add the basefee to the returned number to fall back to the legacy
// behavior.
-func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
- head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
+ head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
headHash := head.Hash()
// If the latest gasprice is still available, return it.
- gpo.cacheLock.RLock()
- lastHead, lastPrice := gpo.lastHead, gpo.lastPrice
- gpo.cacheLock.RUnlock()
+ oracle.cacheLock.RLock()
+ lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
+ oracle.cacheLock.RUnlock()
if headHash == lastHead {
return new(big.Int).Set(lastPrice), nil
}
- gpo.fetchLock.Lock()
- defer gpo.fetchLock.Unlock()
+ oracle.fetchLock.Lock()
+ defer oracle.fetchLock.Unlock()
// Try checking the cache again, maybe the last fetch fetched what we need
- gpo.cacheLock.RLock()
- lastHead, lastPrice = gpo.lastHead, gpo.lastPrice
- gpo.cacheLock.RUnlock()
+ oracle.cacheLock.RLock()
+ lastHead, lastPrice = oracle.lastHead, oracle.lastPrice
+ oracle.cacheLock.RUnlock()
if headHash == lastHead {
return new(big.Int).Set(lastPrice), nil
}
var (
sent, exp int
number = head.Number.Uint64()
- result = make(chan results, gpo.checkBlocks)
+ result = make(chan results, oracle.checkBlocks)
quit = make(chan struct{})
results []*big.Int
)
- for sent < gpo.checkBlocks && number > 0 {
- go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
+ for sent < oracle.checkBlocks && number > 0 {
+ go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
sent++
exp++
number--
@@ -155,15 +161,15 @@ func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
// Nothing returned. There are two special cases here:
// - The block is empty
// - All the transactions included are sent by the miner itself.
- // In these cases, use the latest calculated price for samping.
+ // In these cases, use the latest calculated price for sampling.
if len(res.values) == 0 {
res.values = []*big.Int{lastPrice}
}
// Besides, in order to collect enough data for sampling, if nothing
// meaningful returned, try to query more blocks. But the maximum
// is 2*checkBlocks.
- if len(res.values) == 1 && len(results)+1+exp < gpo.checkBlocks*2 && number > 0 {
- go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
+ if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
+ go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
sent++
exp++
number--
@@ -173,15 +179,15 @@ func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
price := lastPrice
if len(results) > 0 {
sort.Sort(bigIntArray(results))
- price = results[(len(results)-1)*gpo.percentile/100]
+ price = results[(len(results)-1)*oracle.percentile/100]
}
- if price.Cmp(gpo.maxPrice) > 0 {
- price = new(big.Int).Set(gpo.maxPrice)
+ if price.Cmp(oracle.maxPrice) > 0 {
+ price = new(big.Int).Set(oracle.maxPrice)
}
- gpo.cacheLock.Lock()
- gpo.lastHead = headHash
- gpo.lastPrice = price
- gpo.cacheLock.Unlock()
+ oracle.cacheLock.Lock()
+ oracle.lastHead = headHash
+ oracle.lastPrice = price
+ oracle.cacheLock.Unlock()
return new(big.Int).Set(price), nil
}
@@ -219,8 +225,8 @@ func (s *txSorter) Less(i, j int) bool {
// and sends it to the result channel. If the block is empty or all transactions
// are sent by the miner itself(it doesn't make any sense to include this kind of
// transaction prices for sampling), nil gasprice is returned.
-func (gpo *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
- block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
+func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
+ block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
if block == nil {
select {
case result <- results{nil, err}:
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
index f86449c5a..dea8fea95 100644
--- a/eth/gasprice/gasprice_test.go
+++ b/eth/gasprice/gasprice_test.go
@@ -33,29 +33,64 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
+const testHead = 32
+
type testBackend struct {
- chain *core.BlockChain
+ chain *core.BlockChain
+ pending bool // pending block available
}
func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
+ if number > testHead {
+ return nil, nil
+ }
if number == rpc.LatestBlockNumber {
- return b.chain.CurrentBlock().Header(), nil
+ number = testHead
+ }
+ if number == rpc.PendingBlockNumber {
+ if b.pending {
+ number = testHead + 1
+ } else {
+ return nil, nil
+ }
}
return b.chain.GetHeaderByNumber(uint64(number)), nil
}
func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
+ if number > testHead {
+ return nil, nil
+ }
if number == rpc.LatestBlockNumber {
- return b.chain.CurrentBlock(), nil
+ number = testHead
+ }
+ if number == rpc.PendingBlockNumber {
+ if b.pending {
+ number = testHead + 1
+ } else {
+ return nil, nil
+ }
}
return b.chain.GetBlockByNumber(uint64(number)), nil
}
+func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
+ return b.chain.GetReceiptsByHash(hash), nil
+}
+
+func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ if b.pending {
+ block := b.chain.GetBlockByNumber(testHead + 1)
+ return block, b.chain.GetReceiptsByHash(block.Hash())
+ }
+ return nil, nil
+}
+
func (b *testBackend) ChainConfig() *params.ChainConfig {
return b.chain.Config()
}
-func newTestBackend(t *testing.T, londonBlock *big.Int) *testBackend {
+func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend {
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey)
@@ -76,7 +111,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int) *testBackend {
genesis, _ := gspec.Commit(db)
// Generate testing blocks
- blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 32, func(i int, b *core.BlockGen) {
+ blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
var tx *types.Transaction
@@ -116,7 +151,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int) *testBackend {
t.Fatalf("Failed to create local chain, %v", err)
}
chain.InsertChain(blocks)
- return &testBackend{chain: chain}
+ return &testBackend{chain: chain, pending: pending}
}
func (b *testBackend) CurrentHeader() *types.Header {
@@ -144,7 +179,7 @@ func TestSuggestTipCap(t *testing.T) {
{big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future
}
for _, c := range cases {
- backend := newTestBackend(t, c.fork)
+ backend := newTestBackend(t, c.fork, false)
oracle := NewOracle(backend, config)
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index 8d5373972..eb178311f 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -170,7 +170,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
- statedb.Prepare(tx.Hash(), block.Hash(), idx)
+ statedb.Prepare(tx.Hash(), idx)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 39591ae23..d8d290454 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -179,13 +179,6 @@ type StdTraceConfig struct {
TxHash common.Hash
}
-// txTraceContext is the contextual infos about a transaction before it gets run.
-type txTraceContext struct {
- index int // Index of the transaction within the block
- hash common.Hash // Hash of the transaction
- block common.Hash // Hash of the block containing the transaction
-}
-
// txTraceResult is the result of a single transaction trace.
type txTraceResult struct {
Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
@@ -273,10 +266,10 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
msg, _ := tx.AsMessage(signer, task.block.BaseFee())
- txctx := &txTraceContext{
- index: i,
- hash: tx.Hash(),
- block: task.block.Hash(),
+ txctx := &Context{
+ BlockHash: task.block.Hash(),
+ TxIndex: i,
+ TxHash: tx.Hash(),
}
res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
if err != nil {
@@ -525,10 +518,10 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// Fetch and execute the next transaction trace tasks
for task := range jobs {
msg, _ := txs[task.index].AsMessage(signer, block.BaseFee())
- txctx := &txTraceContext{
- index: task.index,
- hash: txs[task.index].Hash(),
- block: blockHash,
+ txctx := &Context{
+ BlockHash: blockHash,
+ TxIndex: task.index,
+ TxHash: txs[task.index].Hash(),
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
if err != nil {
@@ -547,7 +540,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// Generate the next state snapshot fast without tracing
msg, _ := tx.AsMessage(signer, block.BaseFee())
- statedb.Prepare(tx.Hash(), block.Hash(), i)
+ statedb.Prepare(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
@@ -661,7 +654,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
}
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
- statedb.Prepare(tx.Hash(), block.Hash(), i)
+ statedb.Prepare(tx.Hash(), i)
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if writer != nil {
writer.Flush()
@@ -719,10 +712,10 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
if err != nil {
return nil, err
}
- txctx := &txTraceContext{
- index: int(index),
- hash: hash,
- block: blockHash,
+ txctx := &Context{
+ BlockHash: blockHash,
+ TxIndex: int(index),
+ TxHash: hash,
}
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
}
@@ -778,13 +771,13 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
Reexec: config.Reexec,
}
}
- return api.traceTx(ctx, msg, new(txTraceContext), vmctx, statedb, traceConfig)
+ return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
}
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
-func (api *API) traceTx(ctx context.Context, message core.Message, txctx *txTraceContext, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
+func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
tracer vm.Tracer
@@ -805,7 +798,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *txTrac
tracer = tr(statedb)
} else {
// Constuct the JavaScript tracer to execute with
- if tracer, err = New(*config.Tracer, txContext); err != nil {
+ if tracer, err = New(*config.Tracer, txctx); err != nil {
return nil, err
}
// Handle timeouts and RPC cancellations
@@ -829,7 +822,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *txTrac
vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
// Call Prepare to clear out the statedb access list
- statedb.Prepare(txctx.hash, txctx.block, txctx.index)
+ statedb.Prepare(txctx.TxHash, txctx.TxIndex)
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
if err != nil {
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index 218903dd9..2c52761f5 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -310,12 +310,22 @@ type Tracer struct {
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
+
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+}
+
+// Context contains some contextual infos for a transaction execution that is not
+// available from within the EVM object.
+type Context struct {
+ BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call)
+ TxIndex int // Index of the transaction within a block (zero if dangling tx or call)
+ TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
}
// New instantiates a new tracer instance. code specifies a Javascript snippet,
// which must evaluate to an expression returning an object with 'step', 'fault'
// and 'result' functions.
-func New(code string, txCtx vm.TxContext) (*Tracer, error) {
+func New(code string, ctx *Context) (*Tracer, error) {
// Resolve any tracers by name and assemble the tracer object
if tracer, ok := tracer(code); ok {
code = tracer
@@ -334,8 +344,14 @@ func New(code string, txCtx vm.TxContext) (*Tracer, error) {
depthValue: new(uint),
refundValue: new(uint),
}
- tracer.ctx["gasPrice"] = txCtx.GasPrice
+ if ctx.BlockHash != (common.Hash{}) {
+ tracer.ctx["blockHash"] = ctx.BlockHash
+ if ctx.TxHash != (common.Hash{}) {
+ tracer.ctx["txIndex"] = ctx.TxIndex
+ tracer.ctx["txHash"] = ctx.TxHash
+ }
+ }
// Set up builtins for this environment
tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
ctx.PushString(hexutil.Encode(popSlice(ctx)))
@@ -400,8 +416,14 @@ func New(code string, txCtx vm.TxContext) (*Tracer, error) {
return 1
})
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
- _, ok := vm.PrecompiledContractsIstanbul[common.BytesToAddress(popSlice(ctx))]
- ctx.PushBoolean(ok)
+ addr := common.BytesToAddress(popSlice(ctx))
+ for _, p := range tracer.activePrecompiles {
+ if p == addr {
+ ctx.PushBoolean(true)
+ return 1
+ }
+ }
+ ctx.PushBoolean(false)
return 1
})
tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
@@ -550,11 +572,16 @@ func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
jst.ctx["to"] = to
jst.ctx["input"] = input
jst.ctx["gas"] = gas
+ jst.ctx["gasPrice"] = env.TxContext.GasPrice
jst.ctx["value"] = value
// Initialize the context
jst.ctx["block"] = env.Context.BlockNumber.Uint64()
jst.dbWrapper.db = env.StateDB
+ // Update list of precompiles based on current block
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber)
+ jst.activePrecompiles = vm.ActivePrecompiles(rules)
+
// Compute intrinsic gas
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
@@ -646,6 +673,13 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
case *big.Int:
pushBigInt(val, jst.vm)
+ case int:
+ jst.vm.PushInt(val)
+
+ case common.Hash:
+ ptr := jst.vm.PushFixedBuffer(32)
+ copy(makeSlice(ptr, 32), val[:])
+
default:
panic(fmt.Sprintf("unsupported type: %T", val))
}
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index 033824474..d2d3e57c4 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -39,7 +39,6 @@ func (account) SetBalance(*big.Int) {}
func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil }
func (account) Address() common.Address { return common.Address{} }
-func (account) ReturnGas(*big.Int) {}
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
@@ -59,8 +58,8 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
}
-func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
- env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+func runTrace(tracer *Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
+ env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer})
var (
startGas uint64 = 10000
value = big.NewInt(0)
@@ -80,12 +79,14 @@ func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
func TestTracer(t *testing.T) {
execTracer := func(code string) ([]byte, string) {
t.Helper()
- ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
- tracer, err := New(code, ctx.txCtx)
+ tracer, err := New(code, new(Context))
if err != nil {
t.Fatal(err)
}
- ret, err := runTrace(tracer, ctx)
+ ret, err := runTrace(tracer, &vmContext{
+ blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)},
+ txCtx: vm.TxContext{GasPrice: big.NewInt(100000)},
+ }, params.TestChainConfig)
if err != nil {
return nil, err.Error() // Stringify to allow comparison without nil checks
}
@@ -132,25 +133,21 @@ func TestHalt(t *testing.T) {
t.Skip("duktape doesn't support abortion")
timeout := errors.New("stahp")
- vmctx := testCtx()
- tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", vmctx.txCtx)
+ tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context))
if err != nil {
t.Fatal(err)
}
-
go func() {
time.Sleep(1 * time.Second)
tracer.Stop(timeout)
}()
-
- if _, err = runTrace(tracer, vmctx); err.Error() != "stahp in server-side tracer function 'step'" {
+ if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" {
t.Errorf("Expected timeout error, got %v", err)
}
}
func TestHaltBetweenSteps(t *testing.T) {
- vmctx := testCtx()
- tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", vmctx.txCtx)
+ tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context))
if err != nil {
t.Fatal(err)
}
@@ -158,7 +155,6 @@ func TestHaltBetweenSteps(t *testing.T) {
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
}
-
tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
timeout := errors.New("stahp")
tracer.Stop(timeout)
@@ -182,12 +178,14 @@ func TestNoStepExec(t *testing.T) {
}
execTracer := func(code string) []byte {
t.Helper()
- ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
- tracer, err := New(code, ctx.txCtx)
+ tracer, err := New(code, new(Context))
if err != nil {
t.Fatal(err)
}
- ret, err := runEmptyTrace(tracer, ctx)
+ ret, err := runEmptyTrace(tracer, &vmContext{
+ blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)},
+ txCtx: vm.TxContext{GasPrice: big.NewInt(100000)},
+ })
if err != nil {
t.Fatal(err)
}
@@ -207,3 +205,34 @@ func TestNoStepExec(t *testing.T) {
}
}
}
+
+func TestIsPrecompile(t *testing.T) {
+ chaincfg := ¶ms.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), CatalystBlock: nil, Ethash: new(params.EthashConfig), Clique: nil}
+ chaincfg.ByzantiumBlock = big.NewInt(100)
+ chaincfg.IstanbulBlock = big.NewInt(200)
+ chaincfg.BerlinBlock = big.NewInt(300)
+ txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
+ tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ blockCtx := vm.BlockContext{BlockNumber: big.NewInt(150)}
+ res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
+ if err != nil {
+ t.Error(err)
+ }
+ if string(res) != "false" {
+ t.Errorf("Tracer should not consider blake2f as precompile in byzantium")
+ }
+
+ tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context))
+ blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
+ res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
+ if err != nil {
+ t.Error(err)
+ }
+ if string(res) != "true" {
+ t.Errorf("Tracer should consider blake2f as precompile in istanbul")
+ }
+}
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 8b01edd7b..8fbbf154b 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -173,7 +173,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
// Create the tracer, the EVM environment and run it
- tracer, err := New("prestateTracer", txContext)
+ tracer, err := New("prestateTracer", new(Context))
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -248,7 +248,7 @@ func TestCallTracer(t *testing.T) {
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
// Create the tracer, the EVM environment and run it
- tracer, err := New("callTracer", txContext)
+ tracer, err := New("callTracer", new(Context))
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -300,3 +300,81 @@ func jsonEqual(x, y interface{}) bool {
}
return reflect.DeepEqual(xTrace, yTrace)
}
+
+func BenchmarkTransactionTrace(b *testing.B) {
+ key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ from := crypto.PubkeyToAddress(key.PublicKey)
+ gas := uint64(1000000) // 1M gas
+ to := common.HexToAddress("0x00000000000000000000000000000000deadbeef")
+ signer := types.LatestSignerForChainID(big.NewInt(1337))
+ tx, err := types.SignNewTx(key, signer,
+ &types.LegacyTx{
+ Nonce: 1,
+ GasPrice: big.NewInt(500),
+ Gas: gas,
+ To: &to,
+ })
+ if err != nil {
+ b.Fatal(err)
+ }
+ txContext := vm.TxContext{
+ Origin: from,
+ GasPrice: tx.GasPrice(),
+ }
+ context := vm.BlockContext{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ Coinbase: common.Address{},
+ BlockNumber: new(big.Int).SetUint64(uint64(5)),
+ Time: new(big.Int).SetUint64(uint64(5)),
+ Difficulty: big.NewInt(0xffffffff),
+ GasLimit: gas,
+ }
+ alloc := core.GenesisAlloc{}
+ // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
+ // the address
+ loop := []byte{
+ byte(vm.JUMPDEST), // [ count ]
+ byte(vm.PUSH1), 0, // jumpdestination
+ byte(vm.JUMP),
+ }
+ alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
+ Nonce: 1,
+ Code: loop,
+ Balance: big.NewInt(1),
+ }
+ alloc[from] = core.GenesisAccount{
+ Nonce: 1,
+ Code: []byte{},
+ Balance: big.NewInt(500000000000000),
+ }
+ _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
+ // Create the tracer, the EVM environment and run it
+ tracer := vm.NewStructLogger(&vm.LogConfig{
+ Debug: false,
+ //DisableStorage: true,
+ //DisableMemory: true,
+ //DisableReturnData: true,
+ })
+ evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil)
+ if err != nil {
+ b.Fatalf("failed to prepare transaction for tracing: %v", err)
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ snap := statedb.Snapshot()
+ st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ _, err = st.TransitionDb()
+ if err != nil {
+ b.Fatal(err)
+ }
+ statedb.RevertToSnapshot(snap)
+ if have, want := len(tracer.StructLogs()), 244752; have != want {
+ b.Fatalf("trace wrong, want %d steps, have %d", want, have)
+ }
+ tracer.Reset()
+ }
+}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 80240dcb9..9f6832313 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -284,17 +284,6 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*
return r, err
}
-func toBlockNumArg(number *big.Int) string {
- if number == nil {
- return "latest"
- }
- pending := big.NewInt(-1)
- if number.Cmp(pending) == 0 {
- return "pending"
- }
- return hexutil.EncodeBig(number)
-}
-
type rpcProgress struct {
StartingBlock hexutil.Uint64
CurrentBlock hexutil.Uint64
@@ -462,8 +451,6 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
return uint(num), err
}
-// TODO: SubscribePendingTransactions (needs server side)
-
// Contract Calling
// CallContract executes a message call transaction, which is directly executed in the VM
@@ -537,6 +524,17 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data))
}
+func toBlockNumArg(number *big.Int) string {
+ if number == nil {
+ return "latest"
+ }
+ pending := big.NewInt(-1)
+ if number.Cmp(pending) == 0 {
+ return "pending"
+ }
+ return hexutil.EncodeBig(number)
+}
+
func toCallArg(msg ethereum.CallMsg) interface{} {
arg := map[string]interface{}{
"from": msg.From,
diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go
new file mode 100644
index 000000000..538e23727
--- /dev/null
+++ b/ethclient/gethclient/gethclient.go
@@ -0,0 +1,235 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package gethclient provides an RPC client for geth-specific APIs.
+package gethclient
+
+import (
+ "context"
+ "math/big"
+ "runtime"
+ "runtime/debug"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+// Client is a wrapper around rpc.Client that implements geth-specific functionality.
+//
+// If you want to use the standardized Ethereum RPC functionality, use ethclient.Client instead.
+type Client struct {
+ c *rpc.Client
+}
+
+// New creates a client that uses the given RPC client.
+func New(c *rpc.Client) *Client {
+ return &Client{c}
+}
+
+// CreateAccessList tries to create an access list for a specific transaction based on the
+// current pending state of the blockchain.
+func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*types.AccessList, uint64, string, error) {
+ type accessListResult struct {
+ Accesslist *types.AccessList `json:"accessList"`
+ Error string `json:"error,omitempty"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ }
+ var result accessListResult
+ if err := ec.c.CallContext(ctx, &result, "eth_createAccessList", toCallArg(msg)); err != nil {
+ return nil, 0, "", err
+ }
+ return result.Accesslist, uint64(result.GasUsed), result.Error, nil
+}
+
+// AccountResult is the result of a GetProof operation.
+type AccountResult struct {
+ Address common.Address `json:"address"`
+ AccountProof []string `json:"accountProof"`
+ Balance *big.Int `json:"balance"`
+ CodeHash common.Hash `json:"codeHash"`
+ Nonce uint64 `json:"nonce"`
+ StorageHash common.Hash `json:"storageHash"`
+ StorageProof []StorageResult `json:"storageProof"`
+}
+
+// StorageResult provides a proof for a key-value pair.
+type StorageResult struct {
+ Key string `json:"key"`
+ Value *big.Int `json:"value"`
+ Proof []string `json:"proof"`
+}
+
+// GetProof returns the account and storage values of the specified account including the Merkle-proof.
+// The block number can be nil, in which case the value is taken from the latest known block.
+func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []string, blockNumber *big.Int) (*AccountResult, error) {
+
+ type storageResult struct {
+ Key string `json:"key"`
+ Value *hexutil.Big `json:"value"`
+ Proof []string `json:"proof"`
+ }
+
+ type accountResult struct {
+ Address common.Address `json:"address"`
+ AccountProof []string `json:"accountProof"`
+ Balance *hexutil.Big `json:"balance"`
+ CodeHash common.Hash `json:"codeHash"`
+ Nonce hexutil.Uint64 `json:"nonce"`
+ StorageHash common.Hash `json:"storageHash"`
+ StorageProof []storageResult `json:"storageProof"`
+ }
+
+ var res accountResult
+ err := ec.c.CallContext(ctx, &res, "eth_getProof", account, keys, toBlockNumArg(blockNumber))
+ // Turn hexutils back to normal datatypes
+ storageResults := make([]StorageResult, 0, len(res.StorageProof))
+ for _, st := range res.StorageProof {
+ storageResults = append(storageResults, StorageResult{
+ Key: st.Key,
+ Value: st.Value.ToInt(),
+ Proof: st.Proof,
+ })
+ }
+ result := AccountResult{
+ Address: res.Address,
+ AccountProof: res.AccountProof,
+ Balance: res.Balance.ToInt(),
+ Nonce: uint64(res.Nonce),
+ CodeHash: res.CodeHash,
+ StorageHash: res.StorageHash,
+ }
+ return &result, err
+}
+
+// OverrideAccount specifies the state of an account to be overridden.
+type OverrideAccount struct {
+ Nonce uint64 `json:"nonce"`
+ Code []byte `json:"code"`
+ Balance *big.Int `json:"balance"`
+ State map[common.Hash]common.Hash `json:"state"`
+ StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
+}
+
+// CallContract executes a message call transaction, which is directly executed in the VM
+// of the node, but never mined into the blockchain.
+//
+// blockNumber selects the block height at which the call runs. It can be nil, in which
+// case the code is taken from the latest known block. Note that state from very old
+// blocks might not be available.
+//
+// overrides specifies a map of contract states that should be overwritten before executing
+// the message call.
+// Please use ethclient.CallContract instead if you don't need the override functionality.
+func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount) ([]byte, error) {
+ var hex hexutil.Bytes
+ err := ec.c.CallContext(
+ ctx, &hex, "eth_call", toCallArg(msg),
+ toBlockNumArg(blockNumber), toOverrideMap(overrides),
+ )
+ return hex, err
+}
+
+// GCStats retrieves the current garbage collection stats from a geth node.
+func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
+ var result debug.GCStats
+ err := ec.c.CallContext(ctx, &result, "debug_gcStats")
+ return &result, err
+}
+
+// MemStats retrieves the current memory stats from a geth node.
+func (ec *Client) MemStats(ctx context.Context) (*runtime.MemStats, error) {
+ var result runtime.MemStats
+ err := ec.c.CallContext(ctx, &result, "debug_memStats")
+ return &result, err
+}
+
+// SetHead sets the current head of the local chain by block number.
+// Note, this is a destructive action and may severely damage your chain.
+// Use with extreme caution.
+func (ec *Client) SetHead(ctx context.Context, number *big.Int) error {
+ return ec.c.CallContext(ctx, nil, "debug_setHead", toBlockNumArg(number))
+}
+
+// GetNodeInfo retrieves the node info of a geth node.
+func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) {
+ var result p2p.NodeInfo
+ err := ec.c.CallContext(ctx, &result, "admin_nodeInfo")
+ return &result, err
+}
+
+// SubscribePendingTransactions subscribes to new pending transactions.
+func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) {
+ return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
+}
+
+func toBlockNumArg(number *big.Int) string {
+ if number == nil {
+ return "latest"
+ }
+ pending := big.NewInt(-1)
+ if number.Cmp(pending) == 0 {
+ return "pending"
+ }
+ return hexutil.EncodeBig(number)
+}
+
+func toCallArg(msg ethereum.CallMsg) interface{} {
+ arg := map[string]interface{}{
+ "from": msg.From,
+ "to": msg.To,
+ }
+ if len(msg.Data) > 0 {
+ arg["data"] = hexutil.Bytes(msg.Data)
+ }
+ if msg.Value != nil {
+ arg["value"] = (*hexutil.Big)(msg.Value)
+ }
+ if msg.Gas != 0 {
+ arg["gas"] = hexutil.Uint64(msg.Gas)
+ }
+ if msg.GasPrice != nil {
+ arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
+ }
+ return arg
+}
+
+func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
+ if overrides == nil {
+ return nil
+ }
+ type overrideAccount struct {
+ Nonce hexutil.Uint64 `json:"nonce"`
+ Code hexutil.Bytes `json:"code"`
+ Balance *hexutil.Big `json:"balance"`
+ State map[common.Hash]common.Hash `json:"state"`
+ StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
+ }
+ result := make(map[common.Address]overrideAccount)
+ for addr, override := range *overrides {
+ result[addr] = overrideAccount{
+ Nonce: hexutil.Uint64(override.Nonce),
+ Code: override.Code,
+ Balance: (*hexutil.Big)(override.Balance),
+ State: override.State,
+ StateDiff: override.StateDiff,
+ }
+ }
+ return &result
+}
diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go
new file mode 100644
index 000000000..3c408c225
--- /dev/null
+++ b/ethclient/gethclient/gethclient_test.go
@@ -0,0 +1,305 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package gethclient
+
+import (
+ "bytes"
+ "context"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/eth/ethconfig"
+ "github.com/ethereum/go-ethereum/ethclient"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+var (
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
+ testBalance = big.NewInt(2e15)
+)
+
+func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
+ // Generate test chain.
+ genesis, blocks := generateTestChain()
+ // Create node
+ n, err := node.New(&node.Config{})
+ if err != nil {
+ t.Fatalf("can't create new node: %v", err)
+ }
+ // Create Ethereum Service
+ config := ðconfig.Config{Genesis: genesis}
+ config.Ethash.PowMode = ethash.ModeFake
+ ethservice, err := eth.New(n, config)
+ if err != nil {
+ t.Fatalf("can't create new ethereum service: %v", err)
+ }
+ // Import the test chain.
+ if err := n.Start(); err != nil {
+ t.Fatalf("can't start test node: %v", err)
+ }
+ if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil {
+ t.Fatalf("can't import test blocks: %v", err)
+ }
+ return n, blocks
+}
+
+func generateTestChain() (*core.Genesis, []*types.Block) {
+ db := rawdb.NewMemoryDatabase()
+ config := params.AllEthashProtocolChanges
+ genesis := &core.Genesis{
+ Config: config,
+ Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
+ ExtraData: []byte("test genesis"),
+ Timestamp: 9000,
+ }
+ generate := func(i int, g *core.BlockGen) {
+ g.OffsetTime(5)
+ g.SetExtra([]byte("test"))
+ }
+ gblock := genesis.ToBlock(db)
+ engine := ethash.NewFaker()
+ blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate)
+ blocks = append([]*types.Block{gblock}, blocks...)
+ return genesis, blocks
+}
+
+func TestGethClient(t *testing.T) {
+ backend, _ := newTestBackend(t)
+ client, err := backend.Attach()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer backend.Close()
+ defer client.Close()
+
+ tests := map[string]struct {
+ test func(t *testing.T)
+ }{
+ "TestAccessList": {
+ func(t *testing.T) { testAccessList(t, client) },
+ },
+ "TestGetProof": {
+ func(t *testing.T) { testGetProof(t, client) },
+ },
+ "TestGCStats": {
+ func(t *testing.T) { testGCStats(t, client) },
+ },
+ "TestMemStats": {
+ func(t *testing.T) { testMemStats(t, client) },
+ },
+ "TestGetNodeInfo": {
+ func(t *testing.T) { testGetNodeInfo(t, client) },
+ },
+ "TestSetHead": {
+ func(t *testing.T) { testSetHead(t, client) },
+ },
+ "TestSubscribePendingTxs": {
+ func(t *testing.T) { testSubscribePendingTransactions(t, client) },
+ },
+ "TestCallContract": {
+ func(t *testing.T) { testCallContract(t, client) },
+ },
+ }
+ t.Parallel()
+ for name, tt := range tests {
+ t.Run(name, tt.test)
+ }
+}
+
+func testAccessList(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ // Test transfer
+ msg := ethereum.CallMsg{
+ From: testAddr,
+ To: &common.Address{},
+ Gas: 21000,
+ GasPrice: big.NewInt(1),
+ Value: big.NewInt(1),
+ }
+ al, gas, vmErr, err := ec.CreateAccessList(context.Background(), msg)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if vmErr != "" {
+ t.Fatalf("unexpected vm error: %v", vmErr)
+ }
+ if gas != 21000 {
+ t.Fatalf("unexpected gas used: %v", gas)
+ }
+ if len(*al) != 0 {
+ t.Fatalf("unexpected length of accesslist: %v", len(*al))
+ }
+ // Test reverting transaction
+ msg = ethereum.CallMsg{
+ From: testAddr,
+ To: nil,
+ Gas: 100000,
+ GasPrice: big.NewInt(1000000000),
+ Value: big.NewInt(1),
+ Data: common.FromHex("0x608060806080608155fd"),
+ }
+ al, gas, vmErr, err = ec.CreateAccessList(context.Background(), msg)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if vmErr == "" {
+ t.Fatalf("wanted vmErr, got none")
+ }
+ if gas == 21000 {
+ t.Fatalf("unexpected gas used: %v", gas)
+ }
+ if len(*al) != 1 || al.StorageKeys() != 1 {
+ t.Fatalf("unexpected length of accesslist: %v", len(*al))
+ }
+ // address changes between calls, so we can't test for it.
+ if (*al)[0].Address == common.HexToAddress("0x0") {
+ t.Fatalf("unexpected address: %v", (*al)[0].Address)
+ }
+ if (*al)[0].StorageKeys[0] != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000081") {
+ t.Fatalf("unexpected storage key: %v", (*al)[0].StorageKeys[0])
+ }
+}
+
+func testGetProof(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ ethcl := ethclient.NewClient(client)
+ result, err := ec.GetProof(context.Background(), testAddr, []string{}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(result.Address[:], testAddr[:]) {
+ t.Fatalf("unexpected address, want: %v got: %v", testAddr, result.Address)
+ }
+ // test nonce
+ nonce, _ := ethcl.NonceAt(context.Background(), result.Address, nil)
+ if result.Nonce != nonce {
+ t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce)
+ }
+ // test balance
+ balance, _ := ethcl.BalanceAt(context.Background(), result.Address, nil)
+ if result.Balance.Cmp(balance) != 0 {
+ t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance)
+ }
+}
+
+func testGCStats(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ _, err := ec.GCStats(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func testMemStats(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ stats, err := ec.MemStats(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if stats.Alloc == 0 {
+ t.Fatal("Invalid mem stats retrieved")
+ }
+}
+
+func testGetNodeInfo(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ info, err := ec.GetNodeInfo(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if info.Name == "" {
+ t.Fatal("Invalid node info retrieved")
+ }
+}
+
+func testSetHead(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ err := ec.SetHead(context.Background(), big.NewInt(0))
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ ethcl := ethclient.NewClient(client)
+ // Subscribe to Transactions
+ ch := make(chan common.Hash)
+ ec.SubscribePendingTransactions(context.Background(), ch)
+ // Send a transaction
+ chainID, err := ethcl.ChainID(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Create transaction
+ tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil)
+ signer := types.LatestSignerForChainID(chainID)
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
+ if err != nil {
+ t.Fatal(err)
+ }
+ signedTx, err := tx.WithSignature(signer, signature)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Send transaction
+ err = ethcl.SendTransaction(context.Background(), signedTx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Check that the transaction was send over the channel
+ hash := <-ch
+ if hash != signedTx.Hash() {
+ t.Fatalf("Invalid tx hash received, got %v, want %v", hash, signedTx.Hash())
+ }
+}
+
+func testCallContract(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ msg := ethereum.CallMsg{
+ From: testAddr,
+ To: &common.Address{},
+ Gas: 21000,
+ GasPrice: big.NewInt(1000000000),
+ Value: big.NewInt(1),
+ }
+ // CallContract without override
+ if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), nil); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ // CallContract with override
+ override := OverrideAccount{
+ Nonce: 1,
+ }
+ mapAcc := make(map[common.Address]OverrideAccount)
+ mapAcc[testAddr] = override
+ if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), &mapAcc); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 42d88f6db..148359110 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -77,7 +77,7 @@ type fullNodeBackend interface {
Miner() *miner.Miner
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
CurrentBlock() *types.Block
- SuggestPrice(ctx context.Context) (*big.Int, error)
+ SuggestGasTipCap(ctx context.Context) (*big.Int, error)
}
// Service implements an Ethereum netstats reporting daemon that pushes local
@@ -780,8 +780,11 @@ func (s *Service) reportStats(conn *connWrapper) error {
sync := fullBackend.Downloader().Progress()
syncing = fullBackend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
- price, _ := fullBackend.SuggestPrice(context.Background())
+ price, _ := fullBackend.SuggestGasTipCap(context.Background())
gasprice = int(price.Uint64())
+ if basefee := fullBackend.CurrentHeader().BaseFee; basefee != nil {
+ gasprice += int(basefee.Uint64())
+ }
} else {
sync := s.backend.Downloader().Progress()
syncing = s.backend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
diff --git a/go.mod b/go.mod
index d35ac1c57..046578016 100644
--- a/go.mod
+++ b/go.mod
@@ -37,7 +37,7 @@ require (
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.0
- github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88
+ github.com/huin/goupnp v1.0.1-0.20210626160114-33cdcbb30dda
github.com/influxdata/influxdb v1.8.3
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
diff --git a/go.sum b/go.sum
index 889d47e20..8fde7ce0f 100644
--- a/go.sum
+++ b/go.sum
@@ -213,8 +213,8 @@ github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iU
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 h1:bcAj8KroPf552TScjFPIakjH2/tdIrIH8F+cc4v4SRo=
-github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo=
+github.com/huin/goupnp v1.0.1-0.20210626160114-33cdcbb30dda h1:Vofqyy/Ysqit++X33unU0Gr08b6P35hKm3juytDrBVI=
+github.com/huin/goupnp v1.0.1-0.20210626160114-33cdcbb30dda/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -422,7 +422,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/graphql/graphql.go b/graphql/graphql.go
index 0c11a3a0e..d35994234 100644
--- a/graphql/graphql.go
+++ b/graphql/graphql.go
@@ -29,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/filters"
@@ -94,7 +93,11 @@ func (a *Account) Balance(ctx context.Context) (hexutil.Big, error) {
if err != nil {
return hexutil.Big{}, err
}
- return hexutil.Big(*state.GetBalance(a.address)), nil
+ balance := state.GetBalance(a.address)
+ if balance == nil {
+ return hexutil.Big{}, fmt.Errorf("failed to load balance %x", a.address)
+ }
+ return hexutil.Big(*balance), nil
}
func (a *Account) TransactionCount(ctx context.Context) (hexutil.Uint64, error) {
@@ -179,8 +182,9 @@ type Transaction struct {
// resolve returns the internal transaction object, fetching it if needed.
func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) {
if t.tx == nil {
- tx, blockHash, _, index := rawdb.ReadTransaction(t.backend.ChainDb(), t.hash)
- if tx != nil {
+ // Try to return an already finalized transaction
+ tx, blockHash, _, index, err := t.backend.GetTransaction(ctx, t.hash)
+ if err == nil && tx != nil {
t.tx = tx
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
t.block = &Block{
@@ -188,9 +192,10 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) {
numberOrHash: &blockNrOrHash,
}
t.index = index
- } else {
- t.tx = t.backend.GetPoolTransaction(t.hash)
+ return t.tx, nil
}
+ // No finalized transaction, try to retrieve it from the pool
+ t.tx = t.backend.GetPoolTransaction(t.hash)
}
return t.tx, nil
}
@@ -286,6 +291,9 @@ func (t *Transaction) Value(ctx context.Context) (hexutil.Big, error) {
if err != nil || tx == nil {
return hexutil.Big{}, err
}
+ if tx.Value() == nil {
+ return hexutil.Big{}, fmt.Errorf("invalid transaction value %x", t.hash)
+ }
return hexutil.Big(*tx.Value()), nil
}
@@ -721,7 +729,11 @@ func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) {
}
h = header.Hash()
}
- return hexutil.Big(*b.backend.GetTd(ctx, h)), nil
+ td := b.backend.GetTd(ctx, h)
+ if td == nil {
+ return hexutil.Big{}, fmt.Errorf("total difficulty not found %x", b.hash)
+ }
+ return hexutil.Big(*td), nil
}
// BlockNumberArgs encapsulates arguments to accessors that specify a block number.
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 1674dcb0a..b65f98836 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -71,7 +71,7 @@ func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error)
return (*hexutil.Big)(tipcap), err
}
-// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic transactions.
+// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee transactions.
func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) {
tipcap, err := s.b.SuggestGasTipCap(ctx)
if err != nil {
@@ -80,6 +80,40 @@ func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.
return (*hexutil.Big)(tipcap), err
}
+type feeHistoryResult struct {
+ OldestBlock rpc.BlockNumber `json:"oldestBlock"`
+ Reward [][]*hexutil.Big `json:"reward,omitempty"`
+ BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
+ GasUsedRatio []float64 `json:"gasUsedRatio"`
+}
+
+func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
+ oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
+ if err != nil {
+ return nil, err
+ }
+ results := &feeHistoryResult{
+ OldestBlock: oldest,
+ GasUsedRatio: gasUsed,
+ }
+ if reward != nil {
+ results.Reward = make([][]*hexutil.Big, len(reward))
+ for i, w := range reward {
+ results.Reward[i] = make([]*hexutil.Big, len(w))
+ for j, v := range w {
+ results.Reward[i][j] = (*hexutil.Big)(v)
+ }
+ }
+ }
+ if baseFee != nil {
+ results.BaseFee = make([]*hexutil.Big, len(baseFee))
+ for i, v := range baseFee {
+ results.BaseFee[i] = (*hexutil.Big)(v)
+ }
+ }
+ return results, nil
+}
+
// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
// yet received the latest block headers from its pears. In case it is synchronizing:
// - startingBlock: block number this node started to synchronise from
@@ -141,6 +175,29 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac
return content
}
+// ContentFrom returns the transactions contained within the transaction pool.
+func (s *PublicTxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCTransaction {
+ content := make(map[string]map[string]*RPCTransaction, 2)
+ pending, queue := s.b.TxPoolContentFrom(addr)
+ curHeader := s.b.CurrentHeader()
+
+ // Build the pending transactions
+ dump := make(map[string]*RPCTransaction, len(pending))
+ for _, tx := range pending {
+ dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ }
+ content["pending"] = dump
+
+ // Build the queued transactions
+ dump = make(map[string]*RPCTransaction, len(queue))
+ for _, tx := range queue {
+ dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ }
+ content["queued"] = dump
+
+ return content
+}
+
// Status returns the number of pending and queued transaction in the pool.
func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint {
pending, queue := s.b.Stats()
@@ -414,14 +471,15 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args Transactio
if args.Gas == nil {
return nil, fmt.Errorf("gas not specified")
}
- if args.GasPrice == nil {
- return nil, fmt.Errorf("gasPrice not specified")
+ if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) {
+ return nil, fmt.Errorf("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
}
if args.Nonce == nil {
return nil, fmt.Errorf("nonce not specified")
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
- if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil {
+ tx := args.toTransaction()
+ if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err
}
signed, err := s.signTransaction(ctx, &args, passwd)
@@ -1083,7 +1141,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
if trace.Stack != nil {
stack := make([]string, len(trace.Stack))
for i, stackValue := range trace.Stack {
- stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
+ stack[i] = stackValue.Hex()
}
formatted[index].Stack = &stack
}
@@ -1383,11 +1441,11 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
}
// Copy the original db so we don't modify it
statedb := db.Copy()
- msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), nil, nil, args.data(), accessList, false)
+ msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), big.NewInt(0), big.NewInt(0), args.data(), accessList, false)
// Apply the transaction with the access list tracer
tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles)
- config := vm.Config{Tracer: tracer, Debug: true}
+ config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true}
vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config)
if err != nil {
return nil, 0, nil, err
@@ -1663,8 +1721,9 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra
return SubmitTransaction(ctx, s.b, signed)
}
-// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction,
-// and returns it to the caller for further processing (signing + broadcast)
+// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields)
+// on a given unsigned transaction, and returns it to the caller for further
+// processing (signing + broadcast).
func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
// Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); err != nil {
@@ -1727,8 +1786,8 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra
if args.Gas == nil {
return nil, fmt.Errorf("gas not specified")
}
- if args.GasPrice == nil {
- return nil, fmt.Errorf("gasPrice not specified")
+ if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
+ return nil, fmt.Errorf("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
}
if args.Nonce == nil {
return nil, fmt.Errorf("nonce not specified")
@@ -1737,18 +1796,19 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra
return nil, err
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
- if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil {
+ tx := args.toTransaction()
+ if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err
}
- tx, err := s.sign(args.from(), args.toTransaction())
+ signed, err := s.sign(args.from(), tx)
if err != nil {
return nil, err
}
- data, err := tx.MarshalBinary()
+ data, err := signed.MarshalBinary()
if err != nil {
return nil, err
}
- return &SignTransactionResult{data, tx}, nil
+ return &SignTransactionResult{data, signed}, nil
}
// PendingTransactions returns the transactions that are in the transaction pool
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index a0e2cf089..fe55ec59c 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -42,6 +42,7 @@ type Backend interface {
// General Ethereum API
Downloader() *downloader.Downloader
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
+ FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (rpc.BlockNumber, [][]*big.Int, []*big.Int, []float64, error)
ChainDb() ethdb.Database
AccountManager() *accounts.Manager
ExtRPCEnabled() bool
@@ -76,6 +77,7 @@ type Backend interface {
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
Stats() (pending int, queued int)
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
+ TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions)
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
// Filter API
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 220eb22b7..1fbaaeacb 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -49,7 +49,7 @@ type TransactionArgs struct {
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
- // For non-legacy transactions
+ // Introduced by AccessListTxType transaction.
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
}
@@ -108,6 +108,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
return err
}
if b.ChainConfig().IsLondon(head.Number) {
+ // The legacy tx gas price suggestion should not add 2x base fee
+ // because all fees are consumed, so it would result in a spiral
+ // upwards.
price.Add(price, head.BaseFee)
}
args.GasPrice = (*hexutil.Big)(price)
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 8072a3dab..927dba189 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -18,55 +18,20 @@
package web3ext
var Modules = map[string]string{
- "accounting": AccountingJs,
- "admin": AdminJs,
- "chequebook": ChequebookJs,
- "clique": CliqueJs,
- "ethash": EthashJs,
- "debug": DebugJs,
- "eth": EthJs,
- "miner": MinerJs,
- "net": NetJs,
- "personal": PersonalJs,
- "rpc": RpcJs,
- "shh": ShhJs,
- "swarmfs": SwarmfsJs,
- "txpool": TxpoolJs,
- "les": LESJs,
- "vflux": VfluxJs,
+ "admin": AdminJs,
+ "clique": CliqueJs,
+ "ethash": EthashJs,
+ "debug": DebugJs,
+ "eth": EthJs,
+ "miner": MinerJs,
+ "net": NetJs,
+ "personal": PersonalJs,
+ "rpc": RpcJs,
+ "txpool": TxpoolJs,
+ "les": LESJs,
+ "vflux": VfluxJs,
}
-const ChequebookJs = `
-web3._extend({
- property: 'chequebook',
- methods: [
- new web3._extend.Method({
- name: 'deposit',
- call: 'chequebook_deposit',
- params: 1,
- inputFormatter: [null]
- }),
- new web3._extend.Property({
- name: 'balance',
- getter: 'chequebook_balance',
- outputFormatter: web3._extend.utils.toDecimal
- }),
- new web3._extend.Method({
- name: 'cash',
- call: 'chequebook_cash',
- params: 1,
- inputFormatter: [null]
- }),
- new web3._extend.Method({
- name: 'issue',
- call: 'chequebook_issue',
- params: 2,
- inputFormatter: [null, null]
- }),
- ]
-});
-`
-
const CliqueJs = `
web3._extend({
property: 'clique',
@@ -108,6 +73,12 @@ web3._extend({
call: 'clique_status',
params: 0
}),
+ new web3._extend.Method({
+ name: 'getSigner',
+ call: 'clique_getSigner',
+ params: 1,
+ inputFormatter: [null]
+ }),
],
properties: [
new web3._extend.Property({
@@ -581,6 +552,12 @@ web3._extend({
params: 2,
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
}),
+ new web3._extend.Method({
+ name: 'feeHistory',
+ call: 'eth_feeHistory',
+ params: 3,
+ inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null]
+ }),
],
properties: [
new web3._extend.Property({
@@ -635,6 +612,12 @@ web3._extend({
params: 1,
inputFormatter: [web3._extend.utils.fromDecimal]
}),
+ new web3._extend.Method({
+ name: 'setGasLimit',
+ call: 'miner_setGasLimit',
+ params: 1,
+ inputFormatter: [web3._extend.utils.fromDecimal]
+ }),
new web3._extend.Method({
name: 'setRecommitInterval',
call: 'miner_setRecommitInterval',
@@ -731,50 +714,6 @@ web3._extend({
});
`
-const ShhJs = `
-web3._extend({
- property: 'shh',
- methods: [
- ],
- properties:
- [
- new web3._extend.Property({
- name: 'version',
- getter: 'shh_version',
- outputFormatter: web3._extend.utils.toDecimal
- }),
- new web3._extend.Property({
- name: 'info',
- getter: 'shh_info'
- }),
- ]
-});
-`
-
-const SwarmfsJs = `
-web3._extend({
- property: 'swarmfs',
- methods:
- [
- new web3._extend.Method({
- name: 'mount',
- call: 'swarmfs_mount',
- params: 2
- }),
- new web3._extend.Method({
- name: 'unmount',
- call: 'swarmfs_unmount',
- params: 1
- }),
- new web3._extend.Method({
- name: 'listmounts',
- call: 'swarmfs_listmounts',
- params: 0
- }),
- ]
-});
-`
-
const TxpoolJs = `
web3._extend({
property: 'txpool',
@@ -798,49 +737,10 @@ web3._extend({
return status;
}
}),
- ]
-});
-`
-
-const AccountingJs = `
-web3._extend({
- property: 'accounting',
- methods: [
- new web3._extend.Property({
- name: 'balance',
- getter: 'account_balance'
- }),
- new web3._extend.Property({
- name: 'balanceCredit',
- getter: 'account_balanceCredit'
- }),
- new web3._extend.Property({
- name: 'balanceDebit',
- getter: 'account_balanceDebit'
- }),
- new web3._extend.Property({
- name: 'bytesCredit',
- getter: 'account_bytesCredit'
- }),
- new web3._extend.Property({
- name: 'bytesDebit',
- getter: 'account_bytesDebit'
- }),
- new web3._extend.Property({
- name: 'msgCredit',
- getter: 'account_msgCredit'
- }),
- new web3._extend.Property({
- name: 'msgDebit',
- getter: 'account_msgDebit'
- }),
- new web3._extend.Property({
- name: 'peerDrops',
- getter: 'account_peerDrops'
- }),
- new web3._extend.Property({
- name: 'selfDrops',
- getter: 'account_selfDrops'
+ new web3._extend.Method({
+ name: 'contentFrom',
+ call: 'txpool_contentFrom',
+ params: 1,
}),
]
});
diff --git a/les/api_backend.go b/les/api_backend.go
index a6ad7a38e..2a2d406d1 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -60,7 +60,10 @@ func (b *LesApiBackend) SetHead(number uint64) {
}
func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
- if number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber {
+ if number == rpc.PendingBlockNumber {
+ return nil, nil
+ }
+ if number == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentHeader(), nil
}
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number))
@@ -122,6 +125,10 @@ func (b *LesApiBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
return nil, errors.New("invalid arguments; neither block nor hash specified")
}
+func (b *LesApiBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ return nil, nil
+}
+
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
header, err := b.HeaderByNumber(ctx, number)
if err != nil {
@@ -212,6 +219,10 @@ func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions,
return b.eth.txPool.Content()
}
+func (b *LesApiBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ return b.eth.txPool.ContentFrom(addr)
+}
+
func (b *LesApiBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return b.eth.txPool.SubscribeNewTxsEvent(ch)
}
@@ -255,6 +266,10 @@ func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error)
return b.gpo.SuggestTipCap(ctx)
}
+func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {
+ return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
+}
+
func (b *LesApiBackend) ChainDb() ethdb.Database {
return b.eth.chainDb
}
diff --git a/les/servingqueue.go b/les/servingqueue.go
index 16e064cb3..10c7e6f48 100644
--- a/les/servingqueue.go
+++ b/les/servingqueue.go
@@ -159,27 +159,23 @@ func (sq *servingQueue) newTask(peer *clientPeer, maxTime uint64, priority int64
// run tokens from the token channel and allow the corresponding tasks to run
// without entering the priority queue.
func (sq *servingQueue) threadController() {
+ defer sq.wg.Done()
for {
token := make(runToken)
select {
case best := <-sq.queueBestCh:
best.tokenCh <- token
case <-sq.stopThreadCh:
- sq.wg.Done()
return
case <-sq.quit:
- sq.wg.Done()
return
}
- <-token
select {
case <-sq.stopThreadCh:
- sq.wg.Done()
return
case <-sq.quit:
- sq.wg.Done()
return
- default:
+ case <-token:
}
}
}
@@ -298,6 +294,7 @@ func (sq *servingQueue) addTask(task *servingTask) {
// and always tries to send the highest priority task to queueBestCh. Successfully sent
// tasks are removed from the queue.
func (sq *servingQueue) queueLoop() {
+ defer sq.wg.Done()
for {
if sq.best != nil {
expTime := sq.best.expTime
@@ -316,7 +313,6 @@ func (sq *servingQueue) queueLoop() {
sq.best, _ = sq.queue.PopItem().(*servingTask)
}
case <-sq.quit:
- sq.wg.Done()
return
}
} else {
@@ -324,7 +320,6 @@ func (sq *servingQueue) queueLoop() {
case task := <-sq.queueAddCh:
sq.addTask(task)
case <-sq.quit:
- sq.wg.Done()
return
}
}
@@ -335,6 +330,7 @@ func (sq *servingQueue) queueLoop() {
// of active thread controller goroutines.
func (sq *servingQueue) threadCountLoop() {
var threadCountTarget int
+ defer sq.wg.Done()
for {
for threadCountTarget > sq.threadCount {
sq.wg.Add(1)
@@ -347,14 +343,12 @@ func (sq *servingQueue) threadCountLoop() {
case sq.stopThreadCh <- struct{}{}:
sq.threadCount--
case <-sq.quit:
- sq.wg.Done()
return
}
} else {
select {
case threadCountTarget = <-sq.setThreadsCh:
case <-sq.quit:
- sq.wg.Done()
return
}
}
diff --git a/les/state_accessor.go b/les/state_accessor.go
index e276b06dc..112e6fd44 100644
--- a/les/state_accessor.go
+++ b/les/state_accessor.go
@@ -58,7 +58,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
msg, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil)
- statedb.Prepare(tx.Hash(), block.Hash(), idx)
+ statedb.Prepare(tx.Hash(), idx)
if idx == txIndex {
return msg, context, statedb, nil
}
diff --git a/light/txpool.go b/light/txpool.go
index 1296389e3..a7df4aeec 100644
--- a/light/txpool.go
+++ b/light/txpool.go
@@ -505,6 +505,25 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
return pending, queued
}
+// ContentFrom retrieves the data content of the transaction pool, returning the
+// pending as well as queued transactions of this address, grouped by nonce.
+func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ // Retrieve the pending transactions and sort by nonce
+ var pending types.Transactions
+ for _, tx := range pool.pending {
+ account, _ := types.Sender(pool.signer, tx)
+ if account != addr {
+ continue
+ }
+ pending = append(pending, tx)
+ }
+ // There are no queued transactions in a light pool, just return an empty map
+ return pending, types.Transactions{}
+}
+
// RemoveTransactions removes all given transactions from the pool.
func (pool *TxPool) RemoveTransactions(txs types.Transactions) {
pool.mu.Lock()
diff --git a/miner/miner.go b/miner/miner.go
index 00c3d0cb5..a4a01b9f4 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -194,11 +194,22 @@ func (miner *Miner) PendingBlock() *types.Block {
return miner.worker.pendingBlock()
}
+// PendingBlockAndReceipts returns the currently pending block and corresponding receipts.
+func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ return miner.worker.pendingBlockAndReceipts()
+}
+
func (miner *Miner) SetEtherbase(addr common.Address) {
miner.coinbase = addr
miner.worker.setEtherbase(addr)
}
+// SetGasCeil sets the gaslimit to strive for when mining blocks post 1559.
+// For pre-1559 blocks, it sets the ceiling.
+func (miner *Miner) SetGasCeil(ceil uint64) {
+ miner.worker.setGasCeil(ceil)
+}
+
// EnablePreseal turns on the preseal mining feature. It's enabled by default.
// Note this function shouldn't be exposed to API, it's unnecessary for users
// (miners) to actually know the underlying detail. It's only for outside project
diff --git a/miner/worker.go b/miner/worker.go
index b0b676ad0..accf3dac9 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -162,9 +162,10 @@ type worker struct {
pendingMu sync.RWMutex
pendingTasks map[common.Hash]*task
- snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot
- snapshotBlock *types.Block
- snapshotState *state.StateDB
+ snapshotMu sync.RWMutex // The lock used to protect the snapshots below
+ snapshotBlock *types.Block
+ snapshotReceipts types.Receipts
+ snapshotState *state.StateDB
// atomic status counters
running int32 // The indicator whether the consensus engine is running or not.
@@ -243,6 +244,12 @@ func (w *worker) setEtherbase(addr common.Address) {
w.coinbase = addr
}
+func (w *worker) setGasCeil(ceil uint64) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ w.config.GasCeil = ceil
+}
+
// setExtra sets the content used to initialize the block extra field.
func (w *worker) setExtra(extra []byte) {
w.mu.Lock()
@@ -284,6 +291,14 @@ func (w *worker) pendingBlock() *types.Block {
return w.snapshotBlock
}
+// pendingBlockAndReceipts returns pending block and corresponding receipts.
+func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ // return a snapshot to avoid contention on currentMu mutex
+ w.snapshotMu.RLock()
+ defer w.snapshotMu.RUnlock()
+ return w.snapshotBlock, w.snapshotReceipts
+}
+
// start sets the running status as 1 and triggers new work submitting.
func (w *worker) start() {
atomic.StoreInt32(&w.running, 1)
@@ -730,6 +745,7 @@ func (w *worker) updateSnapshot() {
w.current.receipts,
trie.NewStackTrie(nil),
)
+ w.snapshotReceipts = copyReceipts(w.current.receipts)
w.snapshotState = w.current.state.Copy()
}
@@ -805,7 +821,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
continue
}
// Start executing the transaction
- w.current.state.Prepare(tx.Hash(), common.Hash{}, w.current.tcount)
+ w.current.state.Prepare(tx.Hash(), w.current.tcount)
logs, err := w.commitTransaction(tx, coinbase)
switch {
diff --git a/oss-fuzz.sh b/oss-fuzz.sh
index 081a8e1d5..9a24f6b17 100644
--- a/oss-fuzz.sh
+++ b/oss-fuzz.sh
@@ -102,7 +102,7 @@ compile_fuzzer tests/fuzzers/stacktrie Fuzz fuzzStackTrie
compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
compile_fuzzer tests/fuzzers/abi Fuzz fuzzAbi
compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
-compile_fuzzer tests/fuzzers/secp265k1 Fuzz fuzzSecp256k1
+compile_fuzzer tests/fuzzers/secp256k1 Fuzz fuzzSecp256k1
compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add
diff --git a/p2p/enode/node.go b/p2p/enode/node.go
index c2429e0e8..d747ca331 100644
--- a/p2p/enode/node.go
+++ b/p2p/enode/node.go
@@ -121,7 +121,7 @@ func (n *Node) UDP() int {
return int(port)
}
-// UDP returns the TCP port of the node.
+// TCP returns the TCP port of the node.
func (n *Node) TCP() int {
var port enr.TCP
n.Load(&port)
diff --git a/p2p/peer_error.go b/p2p/peer_error.go
index ab61bfef0..393cc86b0 100644
--- a/p2p/peer_error.go
+++ b/p2p/peer_error.go
@@ -89,7 +89,7 @@ var discReasonToString = [...]string{
}
func (d DiscReason) String() string {
- if len(discReasonToString) < int(d) {
+ if len(discReasonToString) <= int(d) {
return fmt.Sprintf("unknown disconnect reason %d", d)
}
return discReasonToString[d]
diff --git a/p2p/server.go b/p2p/server.go
index f70ebf721..04fdecaec 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -370,7 +370,7 @@ func (srv *Server) RemoveTrustedPeer(node *enode.Node) {
}
}
-// SubscribePeers subscribes the given channel to peer events
+// SubscribeEvents subscribes the given channel to peer events
func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
return srv.peerFeed.Subscribe(ch)
}
diff --git a/params/config.go b/params/config.go
index eb99e9dda..aa155c50d 100644
--- a/params/config.go
+++ b/params/config.go
@@ -69,15 +69,16 @@ var (
IstanbulBlock: big.NewInt(9_069_000),
MuirGlacierBlock: big.NewInt(9_200_000),
BerlinBlock: big.NewInt(12_244_000),
+ LondonBlock: big.NewInt(12_965_000),
Ethash: new(EthashConfig),
}
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
MainnetTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 384,
- SectionHead: common.HexToHash("0xb583a0ead70324849c4caf923476de3645c0d2f707c86221ec8e40078bdd6884"),
- CHTRoot: common.HexToHash("0x6ecc993baad0c9f77fe9c4c13b89360112e5a0accae4d8502470b911211618b7"),
- BloomRoot: common.HexToHash("0x66a30d8885c19921711704921de7b4bcbd1b49191b197ee79e34dafeed9a04d9"),
+ SectionIndex: 389,
+ SectionHead: common.HexToHash("0x8f96e510cf64abf34095c5aa3937acdf5316de5540945b9688f4a2e083cddc73"),
+ CHTRoot: common.HexToHash("0xa2362493848d6dbc50dcbbf74c017ea808b8938bfb129217d507bd276950d7ac"),
+ BloomRoot: common.HexToHash("0x72fc78a841bde7e08e1fb7c187b622c49dc8271db12db748ff5d0f27bdb41413"),
}
// MainnetCheckpointOracle contains a set of configs for the main network oracle.
@@ -115,10 +116,10 @@ var (
// RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
RopstenTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 279,
- SectionHead: common.HexToHash("0x4a4912848d4c06090097073357c10015d11c6f4544a0f93cbdd584701c3b7d58"),
- CHTRoot: common.HexToHash("0x9053b7867ae921e80a4e2f5a4b15212e4af3d691ca712fb33dc150e9c6ea221c"),
- BloomRoot: common.HexToHash("0x3dc04cb1be7ddc271f3f83469b47b76184a79d7209ef51d85b1539ea6d25a645"),
+ SectionIndex: 322,
+ SectionHead: common.HexToHash("0xe3f2fb70acd752bbcac06b67688db8430815c788a31213011ed51b966108a5f4"),
+ CHTRoot: common.HexToHash("0xb2993a6bc28b23b84159cb477c38c0ec5607434faae6b3657ad44cbcf116f288"),
+ BloomRoot: common.HexToHash("0x871841e5c2ada9dab2011a550d38e9fe0a30047cfc81f1ffc7ebc09f4f230732"),
}
// RopstenCheckpointOracle contains a set of configs for the Ropsten test network oracle.
@@ -159,10 +160,10 @@ var (
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 266,
- SectionHead: common.HexToHash("0xf5655caa59689790e9d7a8ed50081f0c4f447730d279f069ca64b29f075c8688"),
- CHTRoot: common.HexToHash("0x2af6a663aa8c89d72f4140567c99b3c93c25ac3bb21f80a5d8380bd7c801f422"),
- BloomRoot: common.HexToHash("0x367d2a2eb41e48c7c4983b93ed87781ef29bd2c6fcb7d4fd4bee96f1775f783f"),
+ SectionIndex: 270,
+ SectionHead: common.HexToHash("0x03ef8982c93bbf18c859bc1b20ae05b439f04cf1ff592656e941d2c3fcff5d68"),
+ CHTRoot: common.HexToHash("0x9eb80685e8ece479e105b170439779bc0f89997ab7f4dee425f85c4234e8a6b5"),
+ BloomRoot: common.HexToHash("0xc3673721c5697efe5fe4cb825d178f4a335dbfeda6a197fb75c9256a767379dc"),
}
// RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle.
@@ -201,10 +202,10 @@ var (
// GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network.
GoerliTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 150,
- SectionHead: common.HexToHash("0x5294a0cf00895e94f2e5c556ebed1cacab400ab5b8e62bf5ffc3e1d8d3def23e"),
- CHTRoot: common.HexToHash("0x4cd8776300a2e8ea37d99d3a2e6381642dac53b0d3c5d5b1acaa1188601fd1ce"),
- BloomRoot: common.HexToHash("0x2bd3b7626c6a23060ed2ea0f92fe5d8f7ee8adc689079e3ad6595b2354981691"),
+ SectionIndex: 154,
+ SectionHead: common.HexToHash("0xf4cb74cc0e3683589f4992902184241fb892d7c3859d0044c16ec864605ff80d"),
+ CHTRoot: common.HexToHash("0xead95f9f2504b2c7c6d82c51d30e50b40631c3ea2f590cddcc9721cfc0ae79de"),
+ BloomRoot: common.HexToHash("0xc6dd6cfe88ac9c4a6d19c9a8651944fa9d941a2340a8f5ddaf673d4d39779d81"),
}
// GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle.
@@ -246,16 +247,16 @@ var (
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
- AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
+ AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
- AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
+ AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
- TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
+ TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
TestRules = TestChainConfig.Rules(new(big.Int))
)
@@ -335,7 +336,6 @@ type ChainConfig struct {
BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin)
LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london)
- EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst)
// Various consensus engines
@@ -459,11 +459,6 @@ func (c *ChainConfig) IsCatalyst(num *big.Int) bool {
return isForked(c.CatalystBlock, num)
}
-// IsEWASM returns whether num represents a block number after the EWASM fork
-func (c *ChainConfig) IsEWASM(num *big.Int) bool {
- return isForked(c.EWASMBlock, num)
-}
-
// CheckCompatible checks whether scheduled fork transitions have been imported
// with a mismatching chain configuration.
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
@@ -573,9 +568,6 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) {
return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
}
- if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
- return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
- }
return nil
}
diff --git a/params/protocol_params.go b/params/protocol_params.go
index a49c4489f..7abb2441b 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -38,7 +38,7 @@ const (
Sha3Gas uint64 = 30 // Once per SHA3 operation.
Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data.
- SstoreSetGas uint64 = 20000 // Once per SLOAD operation.
+ SstoreSetGas uint64 = 20000 // Once per SSTORE operation.
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change.
SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
diff --git a/params/version.go b/params/version.go
index 9ce389168..6e4e7fccc 100644
--- a/params/version.go
+++ b/params/version.go
@@ -23,7 +23,7 @@ import (
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 10 // Minor version component of the current release
- VersionPatch = 4 // Patch version component of the current release
+ VersionPatch = 5 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string
)
diff --git a/tests/init_test.go b/tests/init_test.go
index dc923dc75..1638f863e 100644
--- a/tests/init_test.go
+++ b/tests/init_test.go
@@ -18,7 +18,6 @@ package tests
import (
"encoding/json"
- "flag"
"fmt"
"io"
"io/ioutil"
@@ -34,17 +33,6 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-// Command line flags to configure the interpreters.
-var (
- testEVM = flag.String("vm.evm", "", "EVM configuration")
- testEWASM = flag.String("vm.ewasm", "", "EWASM configuration")
-)
-
-func TestMain(m *testing.M) {
- flag.Parse()
- os.Exit(m.Run())
-}
-
var (
baseDir = filepath.Join(".", "testdata")
blockTestDir = filepath.Join(baseDir, "BlockchainTests")
diff --git a/tests/state_test.go b/tests/state_test.go
index 43009afdd..2ed98b650 100644
--- a/tests/state_test.go
+++ b/tests/state_test.go
@@ -74,8 +74,10 @@ func TestState(t *testing.T) {
t.Run(key+"/snap", func(t *testing.T) {
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
snaps, statedb, err := test.Run(subtest, vmconfig, true)
- if _, err := snaps.Journal(statedb.IntermediateRoot(false)); err != nil {
- return err
+ if snaps != nil && statedb != nil {
+ if _, err := snaps.Journal(statedb.IntermediateRoot(false)); err != nil {
+ return err
+ }
}
return st.checkFailure(t, err)
})
@@ -90,7 +92,7 @@ const traceErrorLimit = 400000
func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
// Use config from command line arguments.
- config := vm.Config{EVMInterpreter: *testEVM, EWASMInterpreter: *testEWASM}
+ config := vm.Config{}
err := test(config)
if err == nil {
return
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 227ef77cc..97fd3fb6a 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -328,6 +328,9 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (core.Messa
gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee),
tx.MaxFeePerGas)
}
+ if gasPrice == nil {
+ return nil, fmt.Errorf("no gas price provided")
+ }
msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, gasPrice,
tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, true)
diff --git a/trie/sync_bloom.go b/trie/sync_bloom.go
index 1afcce21d..49986fcf0 100644
--- a/trie/sync_bloom.go
+++ b/trie/sync_bloom.go
@@ -45,11 +45,12 @@ var (
// provided disk database on creation in a background thread and will only start
// returning live results once that's finished.
type SyncBloom struct {
- bloom *bloomfilter.Filter
- inited uint32
- closer sync.Once
- closed uint32
- pend sync.WaitGroup
+ bloom *bloomfilter.Filter
+ inited uint32
+ closer sync.Once
+ closed uint32
+ pend sync.WaitGroup
+ closeCh chan struct{}
}
// NewSyncBloom creates a new bloom filter of the given size (in megabytes) and
@@ -64,7 +65,8 @@ func NewSyncBloom(memory uint64, database ethdb.Iteratee) *SyncBloom {
// Assemble the fast sync bloom and init it from previous sessions
b := &SyncBloom{
- bloom: bloom,
+ bloom: bloom,
+ closeCh: make(chan struct{}),
}
b.pend.Add(2)
go func() {
@@ -125,16 +127,15 @@ func (b *SyncBloom) init(database ethdb.Iteratee) {
// meter periodically recalculates the false positive error rate of the bloom
// filter and reports it in a metric.
func (b *SyncBloom) meter() {
+ // check every second
+ tick := time.NewTicker(1 * time.Second)
for {
- // Report the current error ration. No floats, lame, scale it up.
- bloomErrorGauge.Update(int64(b.bloom.FalsePosititveProbability() * 100000))
-
- // Wait one second, but check termination more frequently
- for i := 0; i < 10; i++ {
- if atomic.LoadUint32(&b.closed) == 1 {
- return
- }
- time.Sleep(100 * time.Millisecond)
+ select {
+ case <-tick.C:
+ // Report the current error ration. No floats, lame, scale it up.
+ bloomErrorGauge.Update(int64(b.bloom.FalsePosititveProbability() * 100000))
+ case <-b.closeCh:
+ return
}
}
}
@@ -145,6 +146,7 @@ func (b *SyncBloom) Close() error {
b.closer.Do(func() {
// Ensure the initializer is stopped
atomic.StoreUint32(&b.closed, 1)
+ close(b.closeCh)
b.pend.Wait()
// Wipe the bloom, but mark it "uninited" just in case someone attempts an access
diff --git a/trie/trie.go b/trie/trie.go
index 7ed235fa8..e492a532c 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -405,6 +405,14 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
n.flags = t.newFlag()
n.Children[key[0]] = nn
+ // Because n is a full node, it must've contained at least two children
+ // before the delete operation. If the new child value is non-nil, n still
+ // has at least two children after the deletion, and cannot be reduced to
+ // a short node.
+ if nn != nil {
+ return true, n, nil
+ }
+ // Reduction:
// Check how many non-nil entries are left after deleting and
// reduce the full node to a short node if only one entry is
// left. Since n must've contained at least two children