mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-06 12:03:47 +00:00
Merge branch 'gethmaster' into selfportal
This commit is contained in:
commit
1446e66d30
158 changed files with 10388 additions and 838 deletions
22
.travis.yml
22
.travis.yml
|
|
@ -9,8 +9,7 @@ jobs:
|
|||
- azure-osx
|
||||
|
||||
include:
|
||||
# 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
|
||||
# This builder create and push the Docker images for all architectures
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
|
|
@ -26,24 +25,7 @@ jobs:
|
|||
before_install:
|
||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
script:
|
||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
||||
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: arm64
|
||||
dist: focal
|
||||
go: 1.23.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 ethereum/client-go
|
||||
- go run build/ci.go dockerx -platform "linux/amd64,linux/arm64" -upload ethereum/client-go
|
||||
|
||||
# This builder does the Linux Azure uploads
|
||||
- stage: build
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
|||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.23-alpine as builder
|
||||
FROM golang:1.23-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
|||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.23-alpine as builder
|
||||
FROM golang:1.23-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
|
@ -14,6 +14,13 @@ COPY go.sum /go-ethereum/
|
|||
RUN cd /go-ethereum && go mod download
|
||||
|
||||
ADD . /go-ethereum
|
||||
|
||||
# This is not strictly necessary, but it matches the "Dockerfile" steps, thus
|
||||
# makes it so that under certain circumstances, the docker layer can be cached,
|
||||
# and the builder can jump to the next (build all) command, with the go cache fully loaded.
|
||||
#
|
||||
RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth
|
||||
|
||||
RUN cd /go-ethereum && go run build/ci.go install -static
|
||||
|
||||
# Pull all binaries into a second stage deploy alpine container
|
||||
|
|
|
|||
|
|
@ -69,12 +69,13 @@ func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
|||
// slot or same slot and more signers) then ValidatedOptimistic is updated.
|
||||
// The boolean return flag signals if ValidatedOptimistic has been changed.
|
||||
func (h *HeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
if err := update.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
replace, err := h.validate(update.SignedHeader(), h.optimisticUpdate.SignedHeader())
|
||||
if replace {
|
||||
h.optimisticUpdate, h.hasOptimisticUpdate = update, true
|
||||
|
|
@ -88,12 +89,13 @@ func (h *HeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, e
|
|||
// slot or same slot and more signers) then ValidatedFinality is updated.
|
||||
// The boolean return flag signals if ValidatedFinality has been changed.
|
||||
func (h *HeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
if err := update.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
replace, err := h.validate(update.SignedHeader(), h.finalityUpdate.SignedHeader())
|
||||
if replace {
|
||||
h.finalityUpdate, h.hasFinalityUpdate = update, true
|
||||
|
|
|
|||
|
|
@ -5,56 +5,56 @@
|
|||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||
|
||||
# version:golang 1.23.1
|
||||
# version:golang 1.23.2
|
||||
# https://go.dev/dl/
|
||||
6ee44e298379d146a5e5aa6b1c5b5d5f5d0a3365eabdd70741e6e21340ec3b0d go1.23.1.src.tar.gz
|
||||
f17f2791717c15728ec63213a014e244c35f9c8846fb29f5a1b63d0c0556f756 go1.23.1.aix-ppc64.tar.gz
|
||||
dd9e772686ed908bcff94b6144322d4e2473a7dcd7c696b7e8b6d12f23c887fd go1.23.1.darwin-amd64.pkg
|
||||
488d9e4ca3e3ed513ee4edd91bef3a2360c65fa6d6be59cf79640bf840130a58 go1.23.1.darwin-amd64.tar.gz
|
||||
be34b488157ec69d94e26e1554558219a2c90789bcb7e3686965a7f9c8cfcbe7 go1.23.1.darwin-arm64.pkg
|
||||
e223795ca340e285a760a6446ce57a74500b30e57469a4109961d36184d3c05a go1.23.1.darwin-arm64.tar.gz
|
||||
6af626176923a6ae6c5de6dc1c864f38365793c0e4ecd0d6eab847bdc23953e5 go1.23.1.dragonfly-amd64.tar.gz
|
||||
cc957c1a019702e6cdc2e257202d42799011ebc1968b6c3bcd6b1965952607d5 go1.23.1.freebsd-386.tar.gz
|
||||
a7d57781c50bb80886a8f04066791956d45aa3eea0f83070c5268b6223afb2ff go1.23.1.freebsd-amd64.tar.gz
|
||||
c7b09f3fef456048e596db9bea746eb66796aeb82885622b0388feee18f36a3e go1.23.1.freebsd-arm.tar.gz
|
||||
b05cd6a77995a0c8439d88df124811c725fb78b942d0b6dd1643529d7ba62f1f go1.23.1.freebsd-arm64.tar.gz
|
||||
56236ae70be1613f2915943b94f53c96be5bffc0719314078facd778a89bc57e go1.23.1.freebsd-riscv64.tar.gz
|
||||
8644c52df4e831202114fd67c9fcaf1f7233ad27bf945ac53fa7217cf1a0349f go1.23.1.illumos-amd64.tar.gz
|
||||
cdee2f4e2efa001f7ee75c90f2efc310b63346cfbba7b549987e9139527c6b17 go1.23.1.linux-386.tar.gz
|
||||
49bbb517cfa9eee677e1e7897f7cf9cfdbcf49e05f61984a2789136de359f9bd go1.23.1.linux-amd64.tar.gz
|
||||
faec7f7f8ae53fda0f3d408f52182d942cc89ef5b7d3d9f23ff117437d4b2d2f go1.23.1.linux-arm64.tar.gz
|
||||
6c7832c7dcd8fb6d4eb308f672a725393403c74ee7be1aeccd8a443015df99de go1.23.1.linux-armv6l.tar.gz
|
||||
649ce3856ddc808c00b14a46232eab0bf95e7911cdf497010b17d76656f5ca4e go1.23.1.linux-loong64.tar.gz
|
||||
201911048f234e5a0c51ec94b1a11d4e47062fee4398b1d2faa6c820dc026724 go1.23.1.linux-mips.tar.gz
|
||||
2bce3743df463915e45d2612f9476ffb03d0b3750b1cb3879347de08715b5fc6 go1.23.1.linux-mips64.tar.gz
|
||||
54e301f266e33431b0703136e0bbd4cf02461b1ecedd37b7cbd90cb862a98e5f go1.23.1.linux-mips64le.tar.gz
|
||||
8efd495e93d17408c0803595cdc3bf13cb28e0f957aeabd9cc18245fb8e64019 go1.23.1.linux-mipsle.tar.gz
|
||||
52bd68689095831ad9af7160844c23b28bb8d0acd268de7e300ff5f0662b7a07 go1.23.1.linux-ppc64.tar.gz
|
||||
042888cae54b5fbfd9dd1e3b6bc4a5134879777fe6497fc4c62ec394b5ecf2da go1.23.1.linux-ppc64le.tar.gz
|
||||
1a4a609f0391bea202d9095453cbfaf7368fa88a04c206bf9dd715a738664dc3 go1.23.1.linux-riscv64.tar.gz
|
||||
47dc49ad45c45e192efa0df7dc7bc5403f5f2d15b5d0dc74ef3018154b616f4d go1.23.1.linux-s390x.tar.gz
|
||||
fbfbd5efa6a5d581ea7f5e65015f927db0e52135cab057e43d39d5482da54b61 go1.23.1.netbsd-386.tar.gz
|
||||
e96e1cc5cf36113ee6099d1a7306b22cd9c3f975a36bdff954c59f104f22b853 go1.23.1.netbsd-amd64.tar.gz
|
||||
c394dfc06bfc276a591209a37e09cd39089ec9a9cc3db30b94814ce2e39eb1d4 go1.23.1.netbsd-arm.tar.gz
|
||||
b3b35d64f32821a68b3e2994032dbefb81978f2ec3f218c7a770623b82d36b8e go1.23.1.netbsd-arm64.tar.gz
|
||||
3c775c4c16c182e33c2c4ac090d9a247a93b3fb18a3df01d87d490f29599faff go1.23.1.openbsd-386.tar.gz
|
||||
5edbe53b47c57b32707fd7154536fbe9eaa79053fea01650c93b54cdba13fc0f go1.23.1.openbsd-amd64.tar.gz
|
||||
c30903dd8fa98b8aca8e9db0962ce9f55502aed93e0ef41e5ae148aaa0088de1 go1.23.1.openbsd-arm.tar.gz
|
||||
12da183489e58f9c6b357bc1b626f85ed7d4220cab31a49d6a49e6ac6a718b67 go1.23.1.openbsd-arm64.tar.gz
|
||||
9cc9aad37696a4a10c31dcec9e35a308de0b369dad354d54cf07406ac6fa7c6f go1.23.1.openbsd-ppc64.tar.gz
|
||||
e1d740dda062ce5a276a0c3ed7d8b6353238bc8ff405f63e2e3480bfd26a5ec5 go1.23.1.openbsd-riscv64.tar.gz
|
||||
da2a37f9987f01f096859230aa13ecc4ad2e7884465bce91004bc78c64435d65 go1.23.1.plan9-386.tar.gz
|
||||
fd8fff8b0697d55c4a4d02a8dc998192b80a9dc2a057647373d6ff607cad29de go1.23.1.plan9-amd64.tar.gz
|
||||
52efbc5804c1c86ba7868aa8ebbc31cc8c2a27b62a60fd57944970d48fc67525 go1.23.1.plan9-arm.tar.gz
|
||||
f54205f21e2143f2ada1bf1c00ddf64590f5139d5c3fb77cc06175f0d8cc7567 go1.23.1.solaris-amd64.tar.gz
|
||||
369a17f0cfd29e5c848e58ffe0d772da20abe334d1c7ca01dbcd55bb3db0b440 go1.23.1.windows-386.msi
|
||||
ab866f47d7be56e6b1c67f1d529bf4c23331a339fb0785f435a0552d352cb257 go1.23.1.windows-386.zip
|
||||
e99dac215ee437b9bb8f8b14bbfe0e8756882c1ed291f30818e8363bc9c047a5 go1.23.1.windows-amd64.msi
|
||||
32dedf277c86610e380e1765593edb66876f00223df71690bd6be68ee17675c0 go1.23.1.windows-amd64.zip
|
||||
23169c79dc6b54e0dffb25be6b67425ad9759392a58309bc057430a9bf4c8f6a go1.23.1.windows-arm.msi
|
||||
1a57615a09f13534f88e9f2d7efd5743535d1a5719b19e520eef965a634f8efb go1.23.1.windows-arm.zip
|
||||
313e1a543931ad8735b4df8969e00f5f4c2ef07be21f54015ede961a70263d35 go1.23.1.windows-arm64.msi
|
||||
64ad0954d2c33f556fb1018d62de091254aa6e3a94f1c8a8b16af0d3701d194e go1.23.1.windows-arm64.zip
|
||||
36930162a93df417d90bd22c6e14daff4705baac2b02418edda671cdfa9cd07f go1.23.2.src.tar.gz
|
||||
025d77f1780906142023a364c31a572afd7d56d3a3be1e4e562e367ca88d3267 go1.23.2.freebsd-amd64.tar.gz
|
||||
0d50bade977b84e173cb350946087f5de8c75f8df19456c3b60c5d58e186089d go1.23.2.windows-arm64.zip
|
||||
0edd985dbd6de64d9c88dbc8835bae21203c58444bf26fce0739cbec4eb1b610 go1.23.2.windows-arm64.msi
|
||||
2283d12dfe7c8c8a46a41bbf7d11fe007434e7590cd1b89e221e478640b7ee3a go1.23.2.linux-mips64le.tar.gz
|
||||
2293c5c3ffc595418308b4059ce214b99f0383cba83232e47a1a8c3b710c24e8 go1.23.2.linux-loong64.tar.gz
|
||||
23b93144e754bbcf5eda700e9decbdbd44d29ceedb1bf1de75f95e8a6ea986bb go1.23.2.openbsd-arm64.tar.gz
|
||||
2734a5b54905cea45f136c28249e626d0241b865b0637fa1db64bf533d9d843e go1.23.2.netbsd-amd64.tar.gz
|
||||
28af3c40687afdda6b33b300833b6d662716cc2d624fb9fd61a49bdad44cd869 go1.23.2.freebsd-arm.tar.gz
|
||||
367d522b47c7ce7761a671efcb8b12c8af8f509db1cd6160c91f410ef3201987 go1.23.2.windows-arm.msi
|
||||
36b7228bae235eee6c8193f5a956e1a9a17874955affb86b3564709b0fab5874 go1.23.2.linux-mipsle.tar.gz
|
||||
3bd1130a08195d23960b154d2e6eaa80ac7325ebd9d01d74c58b6d12580e6b12 go1.23.2.linux-mips.tar.gz
|
||||
3bf66879b38a233c5cbb5d2eb982004117f05d6bf06279e886e087d7c504427d go1.23.2.openbsd-riscv64.tar.gz
|
||||
3e80b943d70c7e1633822b42c1aa7234e61da14f13ff8efff7ee6e1347f37648 go1.23.2.netbsd-arm64.tar.gz
|
||||
40c0b61971a1a74fd4566c536f682c9d4976fa71d40d9daabc875c06113d0fee go1.23.2.darwin-amd64.pkg
|
||||
445c0ef19d8692283f4c3a92052cc0568f5a048f4e546105f58e991d4aea54f5 go1.23.2.darwin-amd64.tar.gz
|
||||
542d3c1705f1c6a1c5a80d5dc62e2e45171af291e755d591c5e6531ef63b454e go1.23.2.linux-amd64.tar.gz
|
||||
560aff7fe1eeadc32248db35ed5c0a81e190d171b6ecec404cf46d808c13e92f go1.23.2.aix-ppc64.tar.gz
|
||||
5611cd648f5100b73a7d6fd85589a481af18fdbaf9c153a92de9a8e39a6e061f go1.23.2.darwin-arm64.pkg
|
||||
695aac64532da8d9a243601ffa0411cd763be891fcf7fd2e857eea4ab10b8bcc go1.23.2.plan9-386.tar.gz
|
||||
69b31edcd3d4f7d8bbf9aee2b25cafba30b444ef19bc7a033e15026f7d0cc5c2 go1.23.2.netbsd-arm.tar.gz
|
||||
6ffa4ac1f4368a3121a032917577a4e0a3feaf696c3e98f213b74ac04c318bc4 go1.23.2.plan9-arm.tar.gz
|
||||
72a6def70300cc804c70073d8b579603d9b39b39b02b3b5d340968d9e7e0e9d4 go1.23.2.windows-386.msi
|
||||
791ca685ee5ca0f6fe849dc078145cb1323d0ea9dd308e9cca9ba2e7186dbb3d go1.23.2.linux-ppc64.tar.gz
|
||||
86b5de91fdf7bd9b52c77c62f8762518cf3fc256fe912af9bbff1d073054aa5b go1.23.2.plan9-amd64.tar.gz
|
||||
8734c7cd464a0620f6605bd3f9256bed062f262d0d58e4f45099c329a08ed966 go1.23.2.openbsd-amd64.tar.gz
|
||||
980ceb889915695d94b166ca1300250dba76fa37a2d41eca2c5e7727dcb4fb7f go1.23.2.openbsd-arm.tar.gz
|
||||
a0cf25f236a0fa0a465816fe7f5c930f3b0b90c5c247b09c43a6adeff654e6ae go1.23.2.linux-mips64.tar.gz
|
||||
a13cc0d621af4f35afd90b886c60b1bf66f771939d226dc36fa61a337d90eb30 go1.23.2.openbsd-ppc64.tar.gz
|
||||
b29ff163b34cb4943c521fcfc1d956eaa6286561089042051a3fab22e79e9283 go1.23.2.windows-arm.zip
|
||||
bc28fe3002cd65cec65d0e4f6000584dacb8c71bfaff8801dfb532855ca42513 go1.23.2.windows-amd64.zip
|
||||
c164ce7d894b10fd861d7d7b96f1dbea3f993663d9f0c30bc4f8ae3915db8b0c go1.23.2.linux-ppc64le.tar.gz
|
||||
c4ae1087dce4daf45a837f5fca36ac0e29a02ada9addf857f1c426e60bce6f21 go1.23.2.netbsd-386.tar.gz
|
||||
c80cbc5e66d6fb8b0c3300b0dda1fe925c429e199954d3327da2933d9870b041 go1.23.2.windows-amd64.msi
|
||||
cb1ed4410f68d8be1156cee0a74fcfbdcd9bca377c83db3a9e1b07eebc6d71ef go1.23.2.linux-386.tar.gz
|
||||
d1fde255843fec1f7f0611d468effd98e1f4309f589ac13037db07b032f9da35 go1.23.2.openbsd-386.tar.gz
|
||||
d47e40366cd6c6b6ee14b811554cd7dde0351309f4a8a4569ec5ba2bd7689437 go1.23.2.illumos-amd64.tar.gz
|
||||
d87031194fe3e01abdcaf3c7302148ade97a7add6eac3fec26765bcb3207b80f go1.23.2.darwin-arm64.tar.gz
|
||||
de1f94d7dd3548ba3036de1ea97eb8243881c22a88fcc04cc08c704ded769e02 go1.23.2.linux-s390x.tar.gz
|
||||
e3286bdde186077e65e961cbe18874d42a461e5b9c472c26572b8d4a98d15c40 go1.23.2.linux-armv6l.tar.gz
|
||||
e4d9a1319dfdaa827407855e406c43e85c878a1f93f4f3984c85dce969c8bf70 go1.23.2.freebsd-386.tar.gz
|
||||
ea8ab49c5c04c9f94a3f4894d1b030fbce8d10413905fa399f6c39c0a44d5556 go1.23.2.linux-riscv64.tar.gz
|
||||
eaa3bc377badbdcae144633f8b29bf2680475b72dcd4c135343d3bdc0ba7671e go1.23.2.windows-386.zip
|
||||
f11b9b4d4a0679909202fc5e88093d6ff720a8a417bfe6a34d502c3862367039 go1.23.2.freebsd-riscv64.tar.gz
|
||||
f163b99b03e4bbc64cd30363f1694a08fcd44094415db1f092f13f9d1bb7c28e go1.23.2.dragonfly-amd64.tar.gz
|
||||
f45af3e1434175ff85620a74c07fb41d6844655f1f2cd2389c5fca6de000f58c go1.23.2.freebsd-arm64.tar.gz
|
||||
f626cdd92fc21a88b31c1251f419c17782933a42903db87a174ce74eeecc66a9 go1.23.2.linux-arm64.tar.gz
|
||||
fa70d39ddeb6b55241a30b48d7af4e681c6a7d7104e8326c3bc1b12a75e091cc go1.23.2.solaris-amd64.tar.gz
|
||||
|
||||
# version:golangci 1.59.0
|
||||
# https://github.com/golangci/golangci-lint/releases/
|
||||
|
|
|
|||
151
build/ci.go
151
build/ci.go
|
|
@ -50,7 +50,6 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -159,8 +158,8 @@ func main() {
|
|||
doLint(os.Args[2:])
|
||||
case "archive":
|
||||
doArchive(os.Args[2:])
|
||||
case "docker":
|
||||
doDocker(os.Args[2:])
|
||||
case "dockerx":
|
||||
doDockerBuildx(os.Args[2:])
|
||||
case "debsrc":
|
||||
doDebianSource(os.Args[2:])
|
||||
case "nsis":
|
||||
|
|
@ -723,10 +722,9 @@ func maybeSkipArchive(env build.Environment) {
|
|||
}
|
||||
|
||||
// Builds the docker images and optionally uploads them to Docker Hub.
|
||||
func doDocker(cmdline []string) {
|
||||
func doDockerBuildx(cmdline []string) {
|
||||
var (
|
||||
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")`)
|
||||
platform = flag.String("platform", "", `Push a multi-arch docker image for the specified architectures (usually "linux/amd64,linux/arm64")`)
|
||||
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
|
@ -761,129 +759,26 @@ func doDocker(cmdline []string) {
|
|||
case strings.HasPrefix(env.Tag, "v1."):
|
||||
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
|
||||
}
|
||||
// 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", ".")
|
||||
// Need to create a mult-arch builder
|
||||
build.MustRunCommand("docker", "buildx", "create", "--use", "--name", "multi-arch-builder", "--platform", *platform)
|
||||
|
||||
// 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 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)
|
||||
}
|
||||
}
|
||||
// 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 fails 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)
|
||||
for _, spec := range []struct {
|
||||
file string
|
||||
base string
|
||||
}{
|
||||
{file: "Dockerfile", base: fmt.Sprintf("%s:", *upload)},
|
||||
{file: "Dockerfile.alltools", base: fmt.Sprintf("%s:alltools-", *upload)},
|
||||
} {
|
||||
for _, tag := range tags { // latest, stable etc
|
||||
gethImage := fmt.Sprintf("%s%s", spec.base, tag)
|
||||
build.MustRunCommand("docker", "buildx", "build",
|
||||
"--build-arg", "COMMIT="+env.Commit,
|
||||
"--build-arg", "VERSION="+params.VersionWithMeta,
|
||||
"--build-arg", "BUILDNUM="+env.Buildnum,
|
||||
"--tag", gethImage,
|
||||
"--platform", *platform,
|
||||
"--push",
|
||||
"--file", spec.file, ".")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
200
cmd/evm/eofparse.go
Normal file
200
cmd/evm/eofparse.go
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
jt = vm.NewPragueEOFInstructionSetForTesting()
|
||||
}
|
||||
|
||||
var (
|
||||
jt vm.JumpTable
|
||||
initcode = "INITCODE"
|
||||
)
|
||||
|
||||
func eofParseAction(ctx *cli.Context) error {
|
||||
// If `--test` is set, parse and validate the reference test at the provided path.
|
||||
if ctx.IsSet(refTestFlag.Name) {
|
||||
var (
|
||||
file = ctx.String(refTestFlag.Name)
|
||||
executedTests int
|
||||
passedTests int
|
||||
)
|
||||
err := filepath.Walk(file, func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
log.Debug("Executing test", "name", info.Name())
|
||||
passed, tot, err := executeTest(path)
|
||||
passedTests += passed
|
||||
executedTests += tot
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Executed tests", "passed", passedTests, "total executed", executedTests)
|
||||
return nil
|
||||
}
|
||||
// If `--hex` is set, parse and validate the hex string argument.
|
||||
if ctx.IsSet(hexFlag.Name) {
|
||||
if _, err := parseAndValidate(ctx.String(hexFlag.Name), false); err != nil {
|
||||
return fmt.Errorf("err: %w", err)
|
||||
}
|
||||
fmt.Println("OK")
|
||||
return nil
|
||||
}
|
||||
// If neither are passed in, read input from stdin.
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024)
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(l, "#") || l == "" {
|
||||
continue
|
||||
}
|
||||
if _, err := parseAndValidate(l, false); err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("OK")
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type refTests struct {
|
||||
Vectors map[string]eOFTest `json:"vectors"`
|
||||
}
|
||||
|
||||
type eOFTest struct {
|
||||
Code string `json:"code"`
|
||||
Results map[string]etResult `json:"results"`
|
||||
ContainerKind string `json:"containerKind"`
|
||||
}
|
||||
|
||||
type etResult struct {
|
||||
Result bool `json:"result"`
|
||||
Exception string `json:"exception,omitempty"`
|
||||
}
|
||||
|
||||
func executeTest(path string) (int, int, error) {
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
var testsByName map[string]refTests
|
||||
if err := json.Unmarshal(src, &testsByName); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
passed, total := 0, 0
|
||||
for testsName, tests := range testsByName {
|
||||
for name, tt := range tests.Vectors {
|
||||
for fork, r := range tt.Results {
|
||||
total++
|
||||
_, err := parseAndValidate(tt.Code, tt.ContainerKind == initcode)
|
||||
if r.Result && err != nil {
|
||||
log.Error("Test failure, expected validation success", "name", testsName, "idx", name, "fork", fork, "err", err)
|
||||
continue
|
||||
}
|
||||
if !r.Result && err == nil {
|
||||
log.Error("Test failure, expected validation error", "name", testsName, "idx", name, "fork", fork, "have err", r.Exception, "err", err)
|
||||
continue
|
||||
}
|
||||
passed++
|
||||
}
|
||||
}
|
||||
}
|
||||
return passed, total, nil
|
||||
}
|
||||
|
||||
func parseAndValidate(s string, isInitCode bool) (*vm.Container, error) {
|
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
||||
s = s[2:]
|
||||
}
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode data: %w", err)
|
||||
}
|
||||
return parse(b, isInitCode)
|
||||
}
|
||||
|
||||
func parse(b []byte, isInitCode bool) (*vm.Container, error) {
|
||||
var c vm.Container
|
||||
if err := c.UnmarshalBinary(b, isInitCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ValidateCode(&jt, isInitCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func eofDumpAction(ctx *cli.Context) error {
|
||||
// If `--hex` is set, parse and validate the hex string argument.
|
||||
if ctx.IsSet(hexFlag.Name) {
|
||||
return eofDump(ctx.String(hexFlag.Name))
|
||||
}
|
||||
// Otherwise read from stdin
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024)
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(l, "#") || l == "" {
|
||||
continue
|
||||
}
|
||||
if err := eofDump(l); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
func eofDump(hexdata string) error {
|
||||
if len(hexdata) >= 2 && strings.HasPrefix(hexdata, "0x") {
|
||||
hexdata = hexdata[2:]
|
||||
}
|
||||
b, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode data: %w", err)
|
||||
}
|
||||
var c vm.Container
|
||||
if err := c.UnmarshalBinary(b, false); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(c.String())
|
||||
return nil
|
||||
}
|
||||
166
cmd/evm/eofparse_test.go
Normal file
166
cmd/evm/eofparse_test.go
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
func FuzzEofParsing(f *testing.F) {
|
||||
// Seed with corpus from execution-spec-tests
|
||||
for i := 0; ; i++ {
|
||||
fname := fmt.Sprintf("testdata/eof/eof_corpus_%d.txt", i)
|
||||
corpus, err := os.Open(fname)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
f.Logf("Reading seed data from %v", fname)
|
||||
scanner := bufio.NewScanner(corpus)
|
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
||||
s = s[2:]
|
||||
}
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err) // rotten corpus
|
||||
}
|
||||
f.Add(b)
|
||||
}
|
||||
corpus.Close()
|
||||
if err := scanner.Err(); err != nil {
|
||||
panic(err) // rotten corpus
|
||||
}
|
||||
}
|
||||
// And do the fuzzing
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
var (
|
||||
jt = vm.NewPragueEOFInstructionSetForTesting()
|
||||
c vm.Container
|
||||
)
|
||||
cpy := common.CopyBytes(data)
|
||||
if err := c.UnmarshalBinary(data, true); err == nil {
|
||||
c.ValidateCode(&jt, true)
|
||||
if have := c.MarshalBinary(); !bytes.Equal(have, data) {
|
||||
t.Fatal("Unmarshal-> Marshal failure!")
|
||||
}
|
||||
}
|
||||
if err := c.UnmarshalBinary(data, false); err == nil {
|
||||
c.ValidateCode(&jt, false)
|
||||
if have := c.MarshalBinary(); !bytes.Equal(have, data) {
|
||||
t.Fatal("Unmarshal-> Marshal failure!")
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(cpy, data) {
|
||||
panic("data modified during unmarshalling")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestEofParseInitcode(t *testing.T) {
|
||||
testEofParse(t, true, "testdata/eof/results.initcode.txt")
|
||||
}
|
||||
|
||||
func TestEofParseRegular(t *testing.T) {
|
||||
testEofParse(t, false, "testdata/eof/results.regular.txt")
|
||||
}
|
||||
|
||||
func testEofParse(t *testing.T, isInitCode bool, wantFile string) {
|
||||
var wantFn func() string
|
||||
var wantLoc = 0
|
||||
{ // Configure the want-reader
|
||||
wants, err := os.Open(wantFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scanner := bufio.NewScanner(wants)
|
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
||||
wantFn = func() string {
|
||||
if scanner.Scan() {
|
||||
wantLoc++
|
||||
return scanner.Text()
|
||||
}
|
||||
return "end of file reached"
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
fname := fmt.Sprintf("testdata/eof/eof_corpus_%d.txt", i)
|
||||
corpus, err := os.Open(fname)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
t.Logf("# Reading seed data from %v", fname)
|
||||
scanner := bufio.NewScanner(corpus)
|
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
||||
line := 1
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
||||
s = s[2:]
|
||||
}
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err) // rotten corpus
|
||||
}
|
||||
have := "OK"
|
||||
if _, err := parse(b, isInitCode); err != nil {
|
||||
have = fmt.Sprintf("ERR: %v", err)
|
||||
}
|
||||
if false { // Change this to generate the want-output
|
||||
fmt.Printf("%v\n", have)
|
||||
} else {
|
||||
want := wantFn()
|
||||
if have != want {
|
||||
if len(want) > 100 {
|
||||
want = want[:100]
|
||||
}
|
||||
if len(b) > 100 {
|
||||
b = b[:100]
|
||||
}
|
||||
t.Errorf("%v:%d\n%v\ninput %x\nisInit: %v\nhave: %q\nwant: %q\n",
|
||||
fname, line, fmt.Sprintf("%v:%d", wantFile, wantLoc), b, isInitCode, have, want)
|
||||
}
|
||||
}
|
||||
line++
|
||||
}
|
||||
corpus.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEofParse(b *testing.B) {
|
||||
corpus, err := os.Open("testdata/eof/eof_benches.txt")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer corpus.Close()
|
||||
scanner := bufio.NewScanner(corpus)
|
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
||||
line := 1
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
||||
s = s[2:]
|
||||
}
|
||||
data, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
b.Fatal(err) // rotten corpus
|
||||
}
|
||||
b.Run(fmt.Sprintf("test-%d", line), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(data)))
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = parse(data, false)
|
||||
}
|
||||
})
|
||||
line++
|
||||
}
|
||||
}
|
||||
133
cmd/evm/main.go
133
cmd/evm/main.go
|
|
@ -138,61 +138,90 @@ var (
|
|||
Usage: "enable return data output",
|
||||
Category: flags.VMCategory,
|
||||
}
|
||||
refTestFlag = &cli.StringFlag{
|
||||
Name: "test",
|
||||
Usage: "Path to EOF validation reference test.",
|
||||
}
|
||||
hexFlag = &cli.StringFlag{
|
||||
Name: "hex",
|
||||
Usage: "single container data parse and validation",
|
||||
}
|
||||
)
|
||||
|
||||
var stateTransitionCommand = &cli.Command{
|
||||
Name: "transition",
|
||||
Aliases: []string{"t8n"},
|
||||
Usage: "Executes a full state transition",
|
||||
Action: t8ntool.Transition,
|
||||
Flags: []cli.Flag{
|
||||
t8ntool.TraceFlag,
|
||||
t8ntool.TraceTracerFlag,
|
||||
t8ntool.TraceTracerConfigFlag,
|
||||
t8ntool.TraceEnableMemoryFlag,
|
||||
t8ntool.TraceDisableStackFlag,
|
||||
t8ntool.TraceEnableReturnDataFlag,
|
||||
t8ntool.TraceEnableCallFramesFlag,
|
||||
t8ntool.OutputBasedir,
|
||||
t8ntool.OutputAllocFlag,
|
||||
t8ntool.OutputResultFlag,
|
||||
t8ntool.OutputBodyFlag,
|
||||
t8ntool.InputAllocFlag,
|
||||
t8ntool.InputEnvFlag,
|
||||
t8ntool.InputTxsFlag,
|
||||
t8ntool.ForknameFlag,
|
||||
t8ntool.ChainIDFlag,
|
||||
t8ntool.RewardFlag,
|
||||
},
|
||||
}
|
||||
var (
|
||||
stateTransitionCommand = &cli.Command{
|
||||
Name: "transition",
|
||||
Aliases: []string{"t8n"},
|
||||
Usage: "Executes a full state transition",
|
||||
Action: t8ntool.Transition,
|
||||
Flags: []cli.Flag{
|
||||
t8ntool.TraceFlag,
|
||||
t8ntool.TraceTracerFlag,
|
||||
t8ntool.TraceTracerConfigFlag,
|
||||
t8ntool.TraceEnableMemoryFlag,
|
||||
t8ntool.TraceDisableStackFlag,
|
||||
t8ntool.TraceEnableReturnDataFlag,
|
||||
t8ntool.TraceEnableCallFramesFlag,
|
||||
t8ntool.OutputBasedir,
|
||||
t8ntool.OutputAllocFlag,
|
||||
t8ntool.OutputResultFlag,
|
||||
t8ntool.OutputBodyFlag,
|
||||
t8ntool.InputAllocFlag,
|
||||
t8ntool.InputEnvFlag,
|
||||
t8ntool.InputTxsFlag,
|
||||
t8ntool.ForknameFlag,
|
||||
t8ntool.ChainIDFlag,
|
||||
t8ntool.RewardFlag,
|
||||
},
|
||||
}
|
||||
|
||||
var transactionCommand = &cli.Command{
|
||||
Name: "transaction",
|
||||
Aliases: []string{"t9n"},
|
||||
Usage: "Performs transaction validation",
|
||||
Action: t8ntool.Transaction,
|
||||
Flags: []cli.Flag{
|
||||
t8ntool.InputTxsFlag,
|
||||
t8ntool.ChainIDFlag,
|
||||
t8ntool.ForknameFlag,
|
||||
},
|
||||
}
|
||||
transactionCommand = &cli.Command{
|
||||
Name: "transaction",
|
||||
Aliases: []string{"t9n"},
|
||||
Usage: "Performs transaction validation",
|
||||
Action: t8ntool.Transaction,
|
||||
Flags: []cli.Flag{
|
||||
t8ntool.InputTxsFlag,
|
||||
t8ntool.ChainIDFlag,
|
||||
t8ntool.ForknameFlag,
|
||||
},
|
||||
}
|
||||
|
||||
var blockBuilderCommand = &cli.Command{
|
||||
Name: "block-builder",
|
||||
Aliases: []string{"b11r"},
|
||||
Usage: "Builds a block",
|
||||
Action: t8ntool.BuildBlock,
|
||||
Flags: []cli.Flag{
|
||||
t8ntool.OutputBasedir,
|
||||
t8ntool.OutputBlockFlag,
|
||||
t8ntool.InputHeaderFlag,
|
||||
t8ntool.InputOmmersFlag,
|
||||
t8ntool.InputWithdrawalsFlag,
|
||||
t8ntool.InputTxsRlpFlag,
|
||||
t8ntool.SealCliqueFlag,
|
||||
},
|
||||
}
|
||||
blockBuilderCommand = &cli.Command{
|
||||
Name: "block-builder",
|
||||
Aliases: []string{"b11r"},
|
||||
Usage: "Builds a block",
|
||||
Action: t8ntool.BuildBlock,
|
||||
Flags: []cli.Flag{
|
||||
t8ntool.OutputBasedir,
|
||||
t8ntool.OutputBlockFlag,
|
||||
t8ntool.InputHeaderFlag,
|
||||
t8ntool.InputOmmersFlag,
|
||||
t8ntool.InputWithdrawalsFlag,
|
||||
t8ntool.InputTxsRlpFlag,
|
||||
t8ntool.SealCliqueFlag,
|
||||
},
|
||||
}
|
||||
eofParseCommand = &cli.Command{
|
||||
Name: "eofparse",
|
||||
Aliases: []string{"eof"},
|
||||
Usage: "Parses hex eof container and returns validation errors (if any)",
|
||||
Action: eofParseAction,
|
||||
Flags: []cli.Flag{
|
||||
hexFlag,
|
||||
refTestFlag,
|
||||
},
|
||||
}
|
||||
|
||||
eofDumpCommand = &cli.Command{
|
||||
Name: "eofdump",
|
||||
Usage: "Parses hex eof container and prints out human-readable representation of the container.",
|
||||
Action: eofDumpAction,
|
||||
Flags: []cli.Flag{
|
||||
hexFlag,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// vmFlags contains flags related to running the EVM.
|
||||
var vmFlags = []cli.Flag{
|
||||
|
|
@ -235,6 +264,8 @@ func init() {
|
|||
stateTransitionCommand,
|
||||
transactionCommand,
|
||||
blockBuilderCommand,
|
||||
eofParseCommand,
|
||||
eofDumpCommand,
|
||||
}
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
flags.MigrateGlobalFlags(ctx)
|
||||
|
|
|
|||
19
cmd/evm/testdata/eof/eof_benches.txt
vendored
Normal file
19
cmd/evm/testdata/eof/eof_benches.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
1986
cmd/evm/testdata/eof/eof_corpus_0.txt
vendored
Normal file
1986
cmd/evm/testdata/eof/eof_corpus_0.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
350
cmd/evm/testdata/eof/eof_corpus_1.txt
vendored
Normal file
350
cmd/evm/testdata/eof/eof_corpus_1.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
2336
cmd/evm/testdata/eof/results.initcode.txt
vendored
Normal file
2336
cmd/evm/testdata/eof/results.initcode.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
2336
cmd/evm/testdata/eof/results.regular.txt
vendored
Normal file
2336
cmd/evm/testdata/eof/results.regular.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -221,22 +221,23 @@ func initGenesis(ctx *cli.Context) error {
|
|||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
overrides.OverrideVerkle = &v
|
||||
}
|
||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||
chaindb, err := stack.OpenDatabaseWithFreezer(name, 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||
}
|
||||
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
|
||||
chaindb, err := stack.OpenDatabaseWithFreezer("chaindata", 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||
}
|
||||
|
||||
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -258,29 +259,22 @@ func dumpGenesis(ctx *cli.Context) error {
|
|||
|
||||
// dump whatever already exists in the datadir
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||
db, err := stack.OpenDatabase(name, 0, 0, "", true)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
genesis, err := core.ReadGenesis(db)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis: %s", err)
|
||||
}
|
||||
db.Close()
|
||||
|
||||
if err := json.NewEncoder(os.Stdout).Encode(*genesis); err != nil {
|
||||
utils.Fatalf("could not encode stored genesis: %s", err)
|
||||
}
|
||||
return nil
|
||||
db, err := stack.OpenDatabase("chaindata", 0, 0, "", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.IsSet(utils.DataDirFlag.Name) {
|
||||
utils.Fatalf("no existing datadir at %s", stack.Config().DataDir)
|
||||
defer db.Close()
|
||||
|
||||
genesis, err = core.ReadGenesis(db)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis: %s", err)
|
||||
}
|
||||
utils.Fatalf("no network preset provided, and no genesis exists in the default datadir")
|
||||
|
||||
if err := json.NewEncoder(os.Stdout).Encode(*genesis); err != nil {
|
||||
utils.Fatalf("could not encode stored genesis: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -557,7 +551,7 @@ func parseDumpConfig(ctx *cli.Context, db ethdb.Database) (*state.DumpConfig, co
|
|||
default:
|
||||
return nil, common.Hash{}, fmt.Errorf("invalid start argument: %x. 20 or 32 hex-encoded bytes required", startArg)
|
||||
}
|
||||
var conf = &state.DumpConfig{
|
||||
conf := &state.DumpConfig{
|
||||
SkipCode: ctx.Bool(utils.ExcludeCodeFlag.Name),
|
||||
SkipStorage: ctx.Bool(utils.ExcludeStorageFlag.Name),
|
||||
OnlyWithAddresses: !ctx.Bool(utils.IncludeIncompletesFlag.Name),
|
||||
|
|
|
|||
|
|
@ -2000,7 +2000,7 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {
|
|||
|
||||
// RegisterEthService adds an Ethereum client to the stack.
|
||||
// The second return value is the full node instance.
|
||||
func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) {
|
||||
func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (*eth.EthAPIBackend, *eth.Ethereum) {
|
||||
backend, err := eth.New(stack, cfg)
|
||||
if err != nil {
|
||||
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||
|
|
@ -2010,7 +2010,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend
|
|||
}
|
||||
|
||||
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to the node.
|
||||
func RegisterEthStatsService(stack *node.Node, backend ethapi.Backend, url string) {
|
||||
func RegisterEthStatsService(stack *node.Node, backend *eth.EthAPIBackend, url string) {
|
||||
if err := ethstats.New(stack, backend, backend.Engine(), url); err != nil {
|
||||
Fatalf("Failed to register the Ethereum Stats service: %v", err)
|
||||
}
|
||||
|
|
@ -2136,8 +2136,6 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
|||
break
|
||||
}
|
||||
chainDb = remotedb.New(client)
|
||||
case ctx.String(SyncModeFlag.Name) == "light":
|
||||
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly)
|
||||
default:
|
||||
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,13 @@ import (
|
|||
|
||||
// Iterator for disassembled EVM instructions
|
||||
type instructionIterator struct {
|
||||
code []byte
|
||||
pc uint64
|
||||
arg []byte
|
||||
op vm.OpCode
|
||||
error error
|
||||
started bool
|
||||
code []byte
|
||||
pc uint64
|
||||
arg []byte
|
||||
op vm.OpCode
|
||||
error error
|
||||
started bool
|
||||
eofEnabled bool
|
||||
}
|
||||
|
||||
// NewInstructionIterator creates a new instruction iterator.
|
||||
|
|
@ -41,6 +42,13 @@ func NewInstructionIterator(code []byte) *instructionIterator {
|
|||
return it
|
||||
}
|
||||
|
||||
// NewEOFInstructionIterator creates a new instruction iterator for EOF-code.
|
||||
func NewEOFInstructionIterator(code []byte) *instructionIterator {
|
||||
it := NewInstructionIterator(code)
|
||||
it.eofEnabled = true
|
||||
return it
|
||||
}
|
||||
|
||||
// Next returns true if there is a next instruction and moves on.
|
||||
func (it *instructionIterator) Next() bool {
|
||||
if it.error != nil || uint64(len(it.code)) <= it.pc {
|
||||
|
|
@ -63,13 +71,26 @@ func (it *instructionIterator) Next() bool {
|
|||
// We reached the end.
|
||||
return false
|
||||
}
|
||||
|
||||
it.op = vm.OpCode(it.code[it.pc])
|
||||
if it.op.IsPush() {
|
||||
a := uint64(it.op) - uint64(vm.PUSH0)
|
||||
u := it.pc + 1 + a
|
||||
var a int
|
||||
if !it.eofEnabled { // Legacy code
|
||||
if it.op.IsPush() {
|
||||
a = int(it.op) - int(vm.PUSH0)
|
||||
}
|
||||
} else { // EOF code
|
||||
if it.op == vm.RJUMPV {
|
||||
// RJUMPV is unique as it has a variable sized operand. The total size is
|
||||
// determined by the count byte which immediately follows RJUMPV.
|
||||
maxIndex := int(it.code[it.pc+1])
|
||||
a = (maxIndex+1)*2 + 1
|
||||
} else {
|
||||
a = vm.Immediates(it.op)
|
||||
}
|
||||
}
|
||||
if a > 0 {
|
||||
u := it.pc + 1 + uint64(a)
|
||||
if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
|
||||
it.error = fmt.Errorf("incomplete push instruction at %v", it.pc)
|
||||
it.error = fmt.Errorf("incomplete instruction at %v", it.pc)
|
||||
return false
|
||||
}
|
||||
it.arg = it.code[it.pc+1 : u]
|
||||
|
|
@ -105,7 +126,6 @@ func PrintDisassembled(code string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
it := NewInstructionIterator(script)
|
||||
for it.Next() {
|
||||
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||
|
|
|
|||
|
|
@ -17,42 +17,78 @@
|
|||
package asm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests disassembling instructions
|
||||
func TestInstructionIterator(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
want int
|
||||
code string
|
||||
wantErr string
|
||||
code string
|
||||
legacyWant string
|
||||
eofWant string
|
||||
}{
|
||||
{2, "61000000", ""}, // valid code
|
||||
{0, "6100", "incomplete push instruction at 0"}, // invalid code
|
||||
{2, "5900", ""}, // push0
|
||||
{0, "", ""}, // empty
|
||||
{"", "", ""}, // empty
|
||||
{"6100", `err: incomplete instruction at 0`, `err: incomplete instruction at 0`},
|
||||
{"61000000", `
|
||||
00000: PUSH2 0x0000
|
||||
00003: STOP`, `
|
||||
00000: PUSH2 0x0000
|
||||
00003: STOP`},
|
||||
{"5F00", `
|
||||
00000: PUSH0
|
||||
00001: STOP`, `
|
||||
00000: PUSH0
|
||||
00001: STOP`},
|
||||
{"d1aabb00", `00000: DATALOADN
|
||||
00001: opcode 0xaa not defined
|
||||
00002: opcode 0xbb not defined
|
||||
00003: STOP`, `
|
||||
00000: DATALOADN 0xaabb
|
||||
00003: STOP`}, // DATALOADN(aabb),STOP
|
||||
{"d1aa", `
|
||||
00000: DATALOADN
|
||||
00001: opcode 0xaa not defined`, "err: incomplete instruction at 0\n"}, // DATALOADN(aa) invalid
|
||||
{"e20211223344556600", `
|
||||
00000: RJUMPV
|
||||
00001: MUL
|
||||
00002: GT
|
||||
00003: opcode 0x22 not defined
|
||||
00004: CALLER
|
||||
00005: DIFFICULTY
|
||||
00006: SSTORE
|
||||
err: incomplete instruction at 7`, `
|
||||
00000: RJUMPV 0x02112233445566
|
||||
00008: STOP`}, // RJUMPV( 6 bytes), STOP
|
||||
|
||||
} {
|
||||
var (
|
||||
have int
|
||||
code, _ = hex.DecodeString(tc.code)
|
||||
it = NewInstructionIterator(code)
|
||||
legacy = strings.TrimSpace(disassembly(NewInstructionIterator(code)))
|
||||
eof = strings.TrimSpace(disassembly(NewEOFInstructionIterator(code)))
|
||||
)
|
||||
for it.Next() {
|
||||
have++
|
||||
if want := strings.TrimSpace(tc.legacyWant); legacy != want {
|
||||
t.Errorf("test %d: wrong (legacy) output. have:\n%q\nwant:\n%q\n", i, legacy, want)
|
||||
}
|
||||
var haveErr = ""
|
||||
if it.Error() != nil {
|
||||
haveErr = it.Error().Error()
|
||||
}
|
||||
if haveErr != tc.wantErr {
|
||||
t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr)
|
||||
continue
|
||||
}
|
||||
if have != tc.want {
|
||||
t.Errorf("wrong instruction count, have %d want %d", have, tc.want)
|
||||
if want := strings.TrimSpace(tc.eofWant); eof != want {
|
||||
t.Errorf("test %d: wrong (eof) output. have:\n%q\nwant:\n%q\n", i, eof, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func disassembly(it *instructionIterator) string {
|
||||
var out = new(strings.Builder)
|
||||
for it.Next() {
|
||||
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||
fmt.Fprintf(out, "%05x: %v %#x\n", it.PC(), it.Op(), it.Arg())
|
||||
} else {
|
||||
fmt.Fprintf(out, "%05x: %v\n", it.PC(), it.Op())
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
fmt.Fprintf(out, "err: %v\n", err)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,10 +180,10 @@ func (batch *freezerTableBatch) maybeCommit() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// commit writes the batched items to the backing freezerTable.
|
||||
// commit writes the batched items to the backing freezerTable. Note index
|
||||
// file isn't fsync'd after the file write, the recent write can be lost
|
||||
// after the power failure.
|
||||
func (batch *freezerTableBatch) commit() error {
|
||||
// Write data. The head file is fsync'd after write to ensure the
|
||||
// data is truly transferred to disk.
|
||||
_, err := batch.t.head.Write(batch.dataBuffer)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -194,15 +194,10 @@ func (batch *freezerTableBatch) commit() error {
|
|||
dataSize := int64(len(batch.dataBuffer))
|
||||
batch.dataBuffer = batch.dataBuffer[:0]
|
||||
|
||||
// Write indices. The index file is fsync'd after write to ensure the
|
||||
// data indexes are truly transferred to disk.
|
||||
_, err = batch.t.index.Write(batch.indexBuffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.t.index.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
indexSize := int64(len(batch.indexBuffer))
|
||||
batch.indexBuffer = batch.indexBuffer[:0]
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package rawdb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
|
@ -26,6 +27,7 @@ import (
|
|||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -219,7 +221,13 @@ func (t *freezerTable) repair() error {
|
|||
return err
|
||||
} // New file can't trigger this path
|
||||
}
|
||||
// Retrieve the file sizes and prepare for truncation
|
||||
// Validate the index file as it might contain some garbage data after the
|
||||
// power failures.
|
||||
if err := t.repairIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Retrieve the file sizes and prepare for truncation. Note the file size
|
||||
// might be changed after index validation.
|
||||
if stat, err = t.index.Stat(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -364,6 +372,133 @@ func (t *freezerTable) repair() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// repairIndex validates the integrity of the index file. According to the design,
|
||||
// the initial entry in the file denotes the earliest data file along with the
|
||||
// count of deleted items. Following this, all subsequent entries in the file must
|
||||
// be in order. This function identifies any corrupted entries and truncates items
|
||||
// occurring after the corruption point.
|
||||
//
|
||||
// corruption can occur because of the power failure. In the Linux kernel, the
|
||||
// file metadata update and data update are not necessarily performed at the
|
||||
// same time. Typically, the metadata will be flushed/journalled ahead of the file
|
||||
// data. Therefore, we make the pessimistic assumption that the file is first
|
||||
// extended with invalid "garbage" data (normally zero bytes) and that afterwards
|
||||
// the correct data replaces the garbage. As all the items in index file are
|
||||
// supposed to be in-order, the leftover garbage must be truncated before the
|
||||
// index data is utilized.
|
||||
//
|
||||
// It's important to note an exception that's unfortunately undetectable: when
|
||||
// all index entries in the file are zero. Distinguishing whether they represent
|
||||
// leftover garbage or if all items in the table have zero size is impossible.
|
||||
// In such instances, the file will remain unchanged to prevent potential data
|
||||
// loss or misinterpretation.
|
||||
func (t *freezerTable) repairIndex() error {
|
||||
// Retrieve the file sizes and prepare for validation
|
||||
stat, err := t.index.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size := stat.Size()
|
||||
|
||||
// Move the read cursor to the beginning of the file
|
||||
_, err = t.index.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fr := bufio.NewReader(t.index)
|
||||
|
||||
var (
|
||||
start = time.Now()
|
||||
buff = make([]byte, indexEntrySize)
|
||||
prev indexEntry
|
||||
head indexEntry
|
||||
|
||||
read = func() (indexEntry, error) {
|
||||
n, err := io.ReadFull(fr, buff)
|
||||
if err != nil {
|
||||
return indexEntry{}, err
|
||||
}
|
||||
if n != indexEntrySize {
|
||||
return indexEntry{}, fmt.Errorf("failed to read from index, n: %d", n)
|
||||
}
|
||||
var entry indexEntry
|
||||
entry.unmarshalBinary(buff)
|
||||
return entry, nil
|
||||
}
|
||||
truncate = func(offset int64) error {
|
||||
if t.readonly {
|
||||
return fmt.Errorf("index file is corrupted at %d, size: %d", offset, size)
|
||||
}
|
||||
if err := truncateFreezerFile(t.index, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Warn("Truncated index file", "offset", offset, "truncated", size-offset)
|
||||
return nil
|
||||
}
|
||||
)
|
||||
for offset := int64(0); offset < size; offset += indexEntrySize {
|
||||
entry, err := read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if offset == 0 {
|
||||
head = entry
|
||||
continue
|
||||
}
|
||||
// Ensure that the first non-head index refers to the earliest file,
|
||||
// or the next file if the earliest file has no space to place the
|
||||
// first item.
|
||||
if offset == indexEntrySize {
|
||||
if entry.filenum != head.filenum && entry.filenum != head.filenum+1 {
|
||||
log.Error("Corrupted index item detected", "earliest", head.filenum, "filenumber", entry.filenum)
|
||||
return truncate(offset)
|
||||
}
|
||||
prev = entry
|
||||
continue
|
||||
}
|
||||
// ensure two consecutive index items are in order
|
||||
if err := t.checkIndexItems(prev, entry); err != nil {
|
||||
log.Error("Corrupted index item detected", "err", err)
|
||||
return truncate(offset)
|
||||
}
|
||||
prev = entry
|
||||
}
|
||||
// Move the read cursor to the end of the file. While theoretically, the
|
||||
// cursor should reach the end by reading all the items in the file, perform
|
||||
// the seek operation anyway as a precaution.
|
||||
_, err = t.index.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Verified index file", "items", size/indexEntrySize, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkIndexItems validates the correctness of two consecutive index items based
|
||||
// on the following rules:
|
||||
//
|
||||
// - The file number of two consecutive index items must either be the same or
|
||||
// increase monotonically. If the file number decreases or skips in a
|
||||
// non-sequential manner, the index item is considered invalid.
|
||||
//
|
||||
// - For index items with the same file number, the data offset must be in
|
||||
// non-decreasing order. Note: Two index items with the same file number
|
||||
// and the same data offset are permitted if the entry size is zero.
|
||||
//
|
||||
// - The first index item in a new data file must not have a zero data offset.
|
||||
func (t *freezerTable) checkIndexItems(a, b indexEntry) error {
|
||||
if b.filenum != a.filenum && b.filenum != a.filenum+1 {
|
||||
return fmt.Errorf("index items with inconsistent file number, prev: %d, next: %d", a.filenum, b.filenum)
|
||||
}
|
||||
if b.filenum == a.filenum && b.offset < a.offset {
|
||||
return fmt.Errorf("index items with unordered offset, prev: %d, next: %d", a.offset, b.offset)
|
||||
}
|
||||
if b.filenum == a.filenum+1 && b.offset == 0 {
|
||||
return fmt.Errorf("index items with zero offset, file number: %d", b.filenum)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// preopen opens all files that the freezer will need. This method should be called from an init-context,
|
||||
// since it assumes that it doesn't have to bother with locking
|
||||
// The rationale for doing preopen is to not have to do it from within Retrieve, thus not needing to ever
|
||||
|
|
|
|||
|
|
@ -1367,3 +1367,69 @@ func TestRandom(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexValidation(t *testing.T) {
|
||||
const (
|
||||
items = 30
|
||||
dataSize = 10
|
||||
)
|
||||
garbage := indexEntry{
|
||||
filenum: 100,
|
||||
offset: 200,
|
||||
}
|
||||
var cases = []struct {
|
||||
offset int64
|
||||
data []byte
|
||||
expItems int
|
||||
}{
|
||||
// extend index file with zero bytes at the end
|
||||
{
|
||||
offset: (items + 1) * indexEntrySize,
|
||||
data: make([]byte, indexEntrySize),
|
||||
expItems: 30,
|
||||
},
|
||||
// write garbage in the first non-head item
|
||||
{
|
||||
offset: indexEntrySize,
|
||||
data: garbage.append(nil),
|
||||
expItems: 0,
|
||||
},
|
||||
// write garbage in the first non-head item
|
||||
{
|
||||
offset: (items/2 + 1) * indexEntrySize,
|
||||
data: garbage.append(nil),
|
||||
expItems: items / 2,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
fn := fmt.Sprintf("t-%d", rand.Uint64())
|
||||
f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 100, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeChunks(t, f, items, dataSize)
|
||||
|
||||
// write corrupted data
|
||||
f.index.WriteAt(c.data, c.offset)
|
||||
f.Close()
|
||||
|
||||
// reopen the table, corruption should be truncated
|
||||
f, err = newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 100, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < c.expItems; i++ {
|
||||
exp := getChunk(10, i)
|
||||
got, err := f.Retrieve(uint64(i))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read from table, %v", err)
|
||||
}
|
||||
if !bytes.Equal(exp, got) {
|
||||
t.Fatalf("Unexpected item data, want: %v, got: %v", exp, got)
|
||||
}
|
||||
}
|
||||
if f.items.Load() != uint64(c.expItems) {
|
||||
t.Fatalf("Unexpected item number, want: %d, got: %d", c.expItems, f.items.Load())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -478,7 +478,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
|
|||
log.Error("Rejecting duplicate blob pool entry", "id", id, "hash", tx.Hash())
|
||||
return errors.New("duplicate blob entry")
|
||||
}
|
||||
sender, err := p.signer.Sender(tx)
|
||||
sender, err := types.Sender(p.signer, tx)
|
||||
if err != nil {
|
||||
// This path is impossible unless the signature validity changes across
|
||||
// restarts. For that ever improbable case, recover gracefully by ignoring
|
||||
|
|
@ -892,7 +892,7 @@ func (p *BlobPool) reorg(oldHead, newHead *types.Header) (map[common.Address][]*
|
|||
// and accumulate the transactors and transactions
|
||||
for rem.NumberU64() > add.NumberU64() {
|
||||
for _, tx := range rem.Transactions() {
|
||||
from, _ := p.signer.Sender(tx)
|
||||
from, _ := types.Sender(p.signer, tx)
|
||||
|
||||
discarded[from] = append(discarded[from], tx)
|
||||
transactors[from] = struct{}{}
|
||||
|
|
@ -904,7 +904,7 @@ func (p *BlobPool) reorg(oldHead, newHead *types.Header) (map[common.Address][]*
|
|||
}
|
||||
for add.NumberU64() > rem.NumberU64() {
|
||||
for _, tx := range add.Transactions() {
|
||||
from, _ := p.signer.Sender(tx)
|
||||
from, _ := types.Sender(p.signer, tx)
|
||||
|
||||
included[from] = append(included[from], tx)
|
||||
inclusions[tx.Hash()] = add.NumberU64()
|
||||
|
|
@ -917,7 +917,7 @@ func (p *BlobPool) reorg(oldHead, newHead *types.Header) (map[common.Address][]*
|
|||
}
|
||||
for rem.Hash() != add.Hash() {
|
||||
for _, tx := range rem.Transactions() {
|
||||
from, _ := p.signer.Sender(tx)
|
||||
from, _ := types.Sender(p.signer, tx)
|
||||
|
||||
discarded[from] = append(discarded[from], tx)
|
||||
transactors[from] = struct{}{}
|
||||
|
|
@ -927,7 +927,7 @@ func (p *BlobPool) reorg(oldHead, newHead *types.Header) (map[common.Address][]*
|
|||
return nil, nil
|
||||
}
|
||||
for _, tx := range add.Transactions() {
|
||||
from, _ := p.signer.Sender(tx)
|
||||
from, _ := types.Sender(p.signer, tx)
|
||||
|
||||
included[from] = append(included[from], tx)
|
||||
inclusions[tx.Hash()] = add.NumberU64()
|
||||
|
|
@ -1127,7 +1127,7 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
|
|||
// If the transaction replaces an existing one, ensure that price bumps are
|
||||
// adhered to.
|
||||
var (
|
||||
from, _ = p.signer.Sender(tx) // already validated above
|
||||
from, _ = types.Sender(p.signer, tx) // already validated above
|
||||
next = p.state.GetNonce(from)
|
||||
)
|
||||
if uint64(len(p.index[from])) > tx.Nonce()-next {
|
||||
|
|
|
|||
98
core/vm/analysis_eof.go
Normal file
98
core/vm/analysis_eof.go
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
// eofCodeBitmap collects data locations in code.
|
||||
func eofCodeBitmap(code []byte) bitvec {
|
||||
// The bitmap is 4 bytes longer than necessary, in case the code
|
||||
// ends with a PUSH32, the algorithm will push zeroes onto the
|
||||
// bitvector outside the bounds of the actual code.
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
return eofCodeBitmapInternal(code, bits)
|
||||
}
|
||||
|
||||
// eofCodeBitmapInternal is the internal implementation of codeBitmap for EOF
|
||||
// code validation.
|
||||
func eofCodeBitmapInternal(code, bits bitvec) bitvec {
|
||||
for pc := uint64(0); pc < uint64(len(code)); {
|
||||
var (
|
||||
op = OpCode(code[pc])
|
||||
numbits uint16
|
||||
)
|
||||
pc++
|
||||
|
||||
if op == RJUMPV {
|
||||
// RJUMPV is unique as it has a variable sized operand.
|
||||
// The total size is determined by the count byte which
|
||||
// immediate follows RJUMPV. Truncation will be caught
|
||||
// in other validation steps -- for now, just return a
|
||||
// valid bitmap for as much of the code as is
|
||||
// available.
|
||||
end := uint64(len(code))
|
||||
if pc >= end {
|
||||
// Count missing, no more bits to mark.
|
||||
return bits
|
||||
}
|
||||
numbits = uint16(code[pc])*2 + 3
|
||||
if pc+uint64(numbits) > end {
|
||||
// Jump table is truncated, mark as many bits
|
||||
// as possible.
|
||||
numbits = uint16(end - pc)
|
||||
}
|
||||
} else {
|
||||
numbits = uint16(Immediates(op))
|
||||
if numbits == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if numbits >= 8 {
|
||||
for ; numbits >= 16; numbits -= 16 {
|
||||
bits.set16(pc)
|
||||
pc += 16
|
||||
}
|
||||
for ; numbits >= 8; numbits -= 8 {
|
||||
bits.set8(pc)
|
||||
pc += 8
|
||||
}
|
||||
}
|
||||
switch numbits {
|
||||
case 1:
|
||||
bits.set1(pc)
|
||||
pc += 1
|
||||
case 2:
|
||||
bits.setN(set2BitsMask, pc)
|
||||
pc += 2
|
||||
case 3:
|
||||
bits.setN(set3BitsMask, pc)
|
||||
pc += 3
|
||||
case 4:
|
||||
bits.setN(set4BitsMask, pc)
|
||||
pc += 4
|
||||
case 5:
|
||||
bits.setN(set5BitsMask, pc)
|
||||
pc += 5
|
||||
case 6:
|
||||
bits.setN(set6BitsMask, pc)
|
||||
pc += 6
|
||||
case 7:
|
||||
bits.setN(set7BitsMask, pc)
|
||||
pc += 7
|
||||
}
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
|
@ -105,3 +105,31 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
|||
op = STOP
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
|
||||
func BenchmarkJumpdestOpEOFAnalysis(bench *testing.B) {
|
||||
var op OpCode
|
||||
bencher := func(b *testing.B) {
|
||||
code := make([]byte, analysisCodeSize)
|
||||
b.SetBytes(analysisCodeSize)
|
||||
for i := range code {
|
||||
code[i] = byte(op)
|
||||
}
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
clear(bits)
|
||||
eofCodeBitmapInternal(code, bits)
|
||||
}
|
||||
}
|
||||
for op = PUSH1; op <= PUSH32; op++ {
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
op = JUMPDEST
|
||||
bench.Run(op.String(), bencher)
|
||||
op = STOP
|
||||
bench.Run(op.String(), bencher)
|
||||
op = RJUMPV
|
||||
bench.Run(op.String(), bencher)
|
||||
op = EOFCREATE
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
170
core/vm/eips.go
170
core/vm/eips.go
|
|
@ -533,3 +533,173 @@ func enable4762(jt *JumpTable) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enableEOF applies the EOF changes.
|
||||
// OBS! For EOF, there are two changes:
|
||||
// 1. Two separate jumptables are required. One, EOF-jumptable, is used by
|
||||
// eof contracts. This one contains things like RJUMP.
|
||||
// 2. The regular non-eof jumptable also needs to be modified, specifically to
|
||||
// modify how EXTCODECOPY works under the hood.
|
||||
//
|
||||
// This method _only_ deals with case 1.
|
||||
func enableEOF(jt *JumpTable) {
|
||||
// Deprecate opcodes
|
||||
undefined := &operation{
|
||||
execute: opUndefined,
|
||||
constantGas: 0,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
undefined: true,
|
||||
}
|
||||
jt[CALL] = undefined
|
||||
jt[CALLCODE] = undefined
|
||||
jt[DELEGATECALL] = undefined
|
||||
jt[STATICCALL] = undefined
|
||||
jt[SELFDESTRUCT] = undefined
|
||||
jt[JUMP] = undefined
|
||||
jt[JUMPI] = undefined
|
||||
jt[PC] = undefined
|
||||
jt[CREATE] = undefined
|
||||
jt[CREATE2] = undefined
|
||||
jt[CODESIZE] = undefined
|
||||
jt[CODECOPY] = undefined
|
||||
jt[EXTCODESIZE] = undefined
|
||||
jt[EXTCODECOPY] = undefined
|
||||
jt[EXTCODEHASH] = undefined
|
||||
jt[GAS] = undefined
|
||||
// Allow 0xFE to terminate sections
|
||||
jt[INVALID] = &operation{
|
||||
execute: opUndefined,
|
||||
constantGas: 0,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
|
||||
// New opcodes
|
||||
jt[RJUMP] = &operation{
|
||||
execute: opRjump,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
jt[RJUMPI] = &operation{
|
||||
execute: opRjumpi,
|
||||
constantGas: GasFastishStep,
|
||||
minStack: minStack(1, 0),
|
||||
maxStack: maxStack(1, 0),
|
||||
}
|
||||
jt[RJUMPV] = &operation{
|
||||
execute: opRjumpv,
|
||||
constantGas: GasFastishStep,
|
||||
minStack: minStack(1, 0),
|
||||
maxStack: maxStack(1, 0),
|
||||
}
|
||||
jt[CALLF] = &operation{
|
||||
execute: opCallf,
|
||||
constantGas: GasFastStep,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
jt[RETF] = &operation{
|
||||
execute: opRetf,
|
||||
constantGas: GasFastestStep,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
jt[JUMPF] = &operation{
|
||||
execute: opJumpf,
|
||||
constantGas: GasFastStep,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
jt[EOFCREATE] = &operation{
|
||||
execute: opEOFCreate,
|
||||
constantGas: params.Create2Gas,
|
||||
dynamicGas: gasEOFCreate,
|
||||
minStack: minStack(4, 1),
|
||||
maxStack: maxStack(4, 1),
|
||||
memorySize: memoryEOFCreate,
|
||||
}
|
||||
jt[RETURNCONTRACT] = &operation{
|
||||
execute: opReturnContract,
|
||||
// returncontract has zero constant gas cost
|
||||
dynamicGas: pureMemoryGascost,
|
||||
minStack: minStack(2, 0),
|
||||
maxStack: maxStack(2, 0),
|
||||
memorySize: memoryReturnContract,
|
||||
}
|
||||
jt[DATALOAD] = &operation{
|
||||
execute: opDataLoad,
|
||||
constantGas: GasFastishStep,
|
||||
minStack: minStack(1, 1),
|
||||
maxStack: maxStack(1, 1),
|
||||
}
|
||||
jt[DATALOADN] = &operation{
|
||||
execute: opDataLoadN,
|
||||
constantGas: GasFastestStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
jt[DATASIZE] = &operation{
|
||||
execute: opDataSize,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
jt[DATACOPY] = &operation{
|
||||
execute: opDataCopy,
|
||||
constantGas: GasFastestStep,
|
||||
dynamicGas: memoryCopierGas(2),
|
||||
minStack: minStack(3, 0),
|
||||
maxStack: maxStack(3, 0),
|
||||
memorySize: memoryDataCopy,
|
||||
}
|
||||
jt[DUPN] = &operation{
|
||||
execute: opDupN,
|
||||
constantGas: GasFastestStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
jt[SWAPN] = &operation{
|
||||
execute: opSwapN,
|
||||
constantGas: GasFastestStep,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
jt[EXCHANGE] = &operation{
|
||||
execute: opExchange,
|
||||
constantGas: GasFastestStep,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
}
|
||||
jt[RETURNDATALOAD] = &operation{
|
||||
execute: opReturnDataLoad,
|
||||
constantGas: GasFastestStep,
|
||||
minStack: minStack(1, 1),
|
||||
maxStack: maxStack(1, 1),
|
||||
}
|
||||
jt[EXTCALL] = &operation{
|
||||
execute: opExtCall,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
dynamicGas: makeCallVariantGasCallEIP2929(gasExtCall, 0),
|
||||
minStack: minStack(4, 1),
|
||||
maxStack: maxStack(4, 1),
|
||||
memorySize: memoryExtCall,
|
||||
}
|
||||
jt[EXTDELEGATECALL] = &operation{
|
||||
execute: opExtDelegateCall,
|
||||
dynamicGas: makeCallVariantGasCallEIP2929(gasExtDelegateCall, 0),
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
minStack: minStack(3, 1),
|
||||
maxStack: maxStack(3, 1),
|
||||
memorySize: memoryExtCall,
|
||||
}
|
||||
jt[EXTSTATICCALL] = &operation{
|
||||
execute: opExtStaticCall,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
dynamicGas: makeCallVariantGasCallEIP2929(gasExtStaticCall, 0),
|
||||
minStack: minStack(3, 1),
|
||||
maxStack: maxStack(3, 1),
|
||||
memorySize: memoryExtCall,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
501
core/vm/eof.go
Normal file
501
core/vm/eof.go
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
const (
|
||||
offsetVersion = 2
|
||||
offsetTypesKind = 3
|
||||
offsetCodeKind = 6
|
||||
|
||||
kindTypes = 1
|
||||
kindCode = 2
|
||||
kindContainer = 3
|
||||
kindData = 4
|
||||
|
||||
eofFormatByte = 0xef
|
||||
eof1Version = 1
|
||||
|
||||
maxInputItems = 127
|
||||
maxOutputItems = 128
|
||||
maxStackHeight = 1023
|
||||
maxContainerSections = 256
|
||||
)
|
||||
|
||||
var eofMagic = []byte{0xef, 0x00}
|
||||
|
||||
// HasEOFByte returns true if code starts with 0xEF byte
|
||||
func HasEOFByte(code []byte) bool {
|
||||
return len(code) != 0 && code[0] == eofFormatByte
|
||||
}
|
||||
|
||||
// hasEOFMagic returns true if code starts with magic defined by EIP-3540
|
||||
func hasEOFMagic(code []byte) bool {
|
||||
return len(eofMagic) <= len(code) && bytes.Equal(eofMagic, code[0:len(eofMagic)])
|
||||
}
|
||||
|
||||
// isEOFVersion1 returns true if the code's version byte equals eof1Version. It
|
||||
// does not verify the EOF magic is valid.
|
||||
func isEOFVersion1(code []byte) bool {
|
||||
return 2 < len(code) && code[2] == byte(eof1Version)
|
||||
}
|
||||
|
||||
// Container is an EOF container object.
|
||||
type Container struct {
|
||||
types []*functionMetadata
|
||||
codeSections [][]byte
|
||||
subContainers []*Container
|
||||
subContainerCodes [][]byte
|
||||
data []byte
|
||||
dataSize int // might be more than len(data)
|
||||
}
|
||||
|
||||
// functionMetadata is an EOF function signature.
|
||||
type functionMetadata struct {
|
||||
inputs uint8
|
||||
outputs uint8
|
||||
maxStackHeight uint16
|
||||
}
|
||||
|
||||
// stackDelta returns the #outputs - #inputs
|
||||
func (meta *functionMetadata) stackDelta() int {
|
||||
return int(meta.outputs) - int(meta.inputs)
|
||||
}
|
||||
|
||||
// checkInputs checks the current minimum stack (stackMin) against the required inputs
|
||||
// of the metadata, and returns an error if the stack is too shallow.
|
||||
func (meta *functionMetadata) checkInputs(stackMin int) error {
|
||||
if int(meta.inputs) > stackMin {
|
||||
return ErrStackUnderflow{stackLen: stackMin, required: int(meta.inputs)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkStackMax checks the if current maximum stack combined with the
|
||||
// functin max stack will result in a stack overflow, and if so returns an error.
|
||||
func (meta *functionMetadata) checkStackMax(stackMax int) error {
|
||||
newMaxStack := stackMax + int(meta.maxStackHeight) - int(meta.inputs)
|
||||
if newMaxStack > int(params.StackLimit) {
|
||||
return ErrStackOverflow{stackLen: newMaxStack, limit: int(params.StackLimit)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary encodes an EOF container into binary format.
|
||||
func (c *Container) MarshalBinary() []byte {
|
||||
// Build EOF prefix.
|
||||
b := make([]byte, 2)
|
||||
copy(b, eofMagic)
|
||||
b = append(b, eof1Version)
|
||||
|
||||
// Write section headers.
|
||||
b = append(b, kindTypes)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4))
|
||||
b = append(b, kindCode)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSections)))
|
||||
for _, codeSection := range c.codeSections {
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(codeSection)))
|
||||
}
|
||||
var encodedContainer [][]byte
|
||||
if len(c.subContainers) != 0 {
|
||||
b = append(b, kindContainer)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.subContainers)))
|
||||
for _, section := range c.subContainers {
|
||||
encoded := section.MarshalBinary()
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(encoded)))
|
||||
encodedContainer = append(encodedContainer, encoded)
|
||||
}
|
||||
}
|
||||
b = append(b, kindData)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(c.dataSize))
|
||||
b = append(b, 0) // terminator
|
||||
|
||||
// Write section contents.
|
||||
for _, ty := range c.types {
|
||||
b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...)
|
||||
}
|
||||
for _, code := range c.codeSections {
|
||||
b = append(b, code...)
|
||||
}
|
||||
for _, section := range encodedContainer {
|
||||
b = append(b, section...)
|
||||
}
|
||||
b = append(b, c.data...)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes an EOF container.
|
||||
func (c *Container) UnmarshalBinary(b []byte, isInitcode bool) error {
|
||||
return c.unmarshalContainer(b, isInitcode, true)
|
||||
}
|
||||
|
||||
// UnmarshalSubContainer decodes an EOF container that is inside another container.
|
||||
func (c *Container) UnmarshalSubContainer(b []byte, isInitcode bool) error {
|
||||
return c.unmarshalContainer(b, isInitcode, false)
|
||||
}
|
||||
|
||||
func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) error {
|
||||
if !hasEOFMagic(b) {
|
||||
return fmt.Errorf("%w: want %x", errInvalidMagic, eofMagic)
|
||||
}
|
||||
if len(b) < 14 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if len(b) > params.MaxInitCodeSize {
|
||||
return ErrMaxInitCodeSizeExceeded
|
||||
}
|
||||
if !isEOFVersion1(b) {
|
||||
return fmt.Errorf("%w: have %d, want %d", errInvalidVersion, b[2], eof1Version)
|
||||
}
|
||||
|
||||
var (
|
||||
kind, typesSize, dataSize int
|
||||
codeSizes []int
|
||||
err error
|
||||
)
|
||||
|
||||
// Parse type section header.
|
||||
kind, typesSize, err = parseSection(b, offsetTypesKind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kind != kindTypes {
|
||||
return fmt.Errorf("%w: found section kind %x instead", errMissingTypeHeader, kind)
|
||||
}
|
||||
if typesSize < 4 || typesSize%4 != 0 {
|
||||
return fmt.Errorf("%w: type section size must be divisible by 4, have %d", errInvalidTypeSize, typesSize)
|
||||
}
|
||||
if typesSize/4 > 1024 {
|
||||
return fmt.Errorf("%w: type section must not exceed 4*1024, have %d", errInvalidTypeSize, typesSize)
|
||||
}
|
||||
|
||||
// Parse code section header.
|
||||
kind, codeSizes, err = parseSectionList(b, offsetCodeKind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kind != kindCode {
|
||||
return fmt.Errorf("%w: found section kind %x instead", errMissingCodeHeader, kind)
|
||||
}
|
||||
if len(codeSizes) != typesSize/4 {
|
||||
return fmt.Errorf("%w: mismatch of code sections found and type signatures, types %d, code %d", errInvalidCodeSize, typesSize/4, len(codeSizes))
|
||||
}
|
||||
|
||||
// Parse (optional) container section header.
|
||||
var containerSizes []int
|
||||
offset := offsetCodeKind + 2 + 2*len(codeSizes) + 1
|
||||
if offset < len(b) && b[offset] == kindContainer {
|
||||
kind, containerSizes, err = parseSectionList(b, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kind != kindContainer {
|
||||
panic("somethings wrong")
|
||||
}
|
||||
if len(containerSizes) == 0 {
|
||||
return fmt.Errorf("%w: total container count must not be zero", errInvalidContainerSectionSize)
|
||||
}
|
||||
offset = offset + 2 + 2*len(containerSizes) + 1
|
||||
}
|
||||
|
||||
// Parse data section header.
|
||||
kind, dataSize, err = parseSection(b, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kind != kindData {
|
||||
return fmt.Errorf("%w: found section %x instead", errMissingDataHeader, kind)
|
||||
}
|
||||
c.dataSize = dataSize
|
||||
|
||||
// Check for terminator.
|
||||
offsetTerminator := offset + 3
|
||||
if len(b) < offsetTerminator {
|
||||
return fmt.Errorf("%w: invalid offset terminator", io.ErrUnexpectedEOF)
|
||||
}
|
||||
if b[offsetTerminator] != 0 {
|
||||
return fmt.Errorf("%w: have %x", errMissingTerminator, b[offsetTerminator])
|
||||
}
|
||||
|
||||
// Verify overall container size.
|
||||
expectedSize := offsetTerminator + typesSize + sum(codeSizes) + dataSize + 1
|
||||
if len(containerSizes) != 0 {
|
||||
expectedSize += sum(containerSizes)
|
||||
}
|
||||
if len(b) < expectedSize-dataSize {
|
||||
return fmt.Errorf("%w: have %d, want %d", errInvalidContainerSize, len(b), expectedSize)
|
||||
}
|
||||
// Only check that the expected size is not exceed on non-initcode
|
||||
if (!topLevel || !isInitcode) && len(b) > expectedSize {
|
||||
return fmt.Errorf("%w: have %d, want %d", errInvalidContainerSize, len(b), expectedSize)
|
||||
}
|
||||
|
||||
// Parse types section.
|
||||
idx := offsetTerminator + 1
|
||||
var types = make([]*functionMetadata, 0, typesSize/4)
|
||||
for i := 0; i < typesSize/4; i++ {
|
||||
sig := &functionMetadata{
|
||||
inputs: b[idx+i*4],
|
||||
outputs: b[idx+i*4+1],
|
||||
maxStackHeight: binary.BigEndian.Uint16(b[idx+i*4+2:]),
|
||||
}
|
||||
if sig.inputs > maxInputItems {
|
||||
return fmt.Errorf("%w for section %d: have %d", errTooManyInputs, i, sig.inputs)
|
||||
}
|
||||
if sig.outputs > maxOutputItems {
|
||||
return fmt.Errorf("%w for section %d: have %d", errTooManyOutputs, i, sig.outputs)
|
||||
}
|
||||
if sig.maxStackHeight > maxStackHeight {
|
||||
return fmt.Errorf("%w for section %d: have %d", errTooLargeMaxStackHeight, i, sig.maxStackHeight)
|
||||
}
|
||||
types = append(types, sig)
|
||||
}
|
||||
if types[0].inputs != 0 || types[0].outputs != 0x80 {
|
||||
return fmt.Errorf("%w: have %d, %d", errInvalidSection0Type, types[0].inputs, types[0].outputs)
|
||||
}
|
||||
c.types = types
|
||||
|
||||
// Parse code sections.
|
||||
idx += typesSize
|
||||
codeSections := make([][]byte, len(codeSizes))
|
||||
for i, size := range codeSizes {
|
||||
if size == 0 {
|
||||
return fmt.Errorf("%w for section %d: size must not be 0", errInvalidCodeSize, i)
|
||||
}
|
||||
codeSections[i] = b[idx : idx+size]
|
||||
idx += size
|
||||
}
|
||||
c.codeSections = codeSections
|
||||
// Parse the optional container sizes.
|
||||
if len(containerSizes) != 0 {
|
||||
if len(containerSizes) > maxContainerSections {
|
||||
return fmt.Errorf("%w number of container section exceed: %v: have %v", errInvalidContainerSectionSize, maxContainerSections, len(containerSizes))
|
||||
}
|
||||
subContainerCodes := make([][]byte, 0, len(containerSizes))
|
||||
subContainers := make([]*Container, 0, len(containerSizes))
|
||||
for i, size := range containerSizes {
|
||||
if size == 0 || idx+size > len(b) {
|
||||
return fmt.Errorf("%w for section %d: size must not be 0", errInvalidContainerSectionSize, i)
|
||||
}
|
||||
subC := new(Container)
|
||||
end := min(idx+size, len(b))
|
||||
if err := subC.unmarshalContainer(b[idx:end], isInitcode, false); err != nil {
|
||||
if topLevel {
|
||||
return fmt.Errorf("%w in sub container %d", err, i)
|
||||
}
|
||||
return err
|
||||
}
|
||||
subContainers = append(subContainers, subC)
|
||||
subContainerCodes = append(subContainerCodes, b[idx:end])
|
||||
|
||||
idx += size
|
||||
}
|
||||
c.subContainers = subContainers
|
||||
c.subContainerCodes = subContainerCodes
|
||||
}
|
||||
|
||||
//Parse data section.
|
||||
end := len(b)
|
||||
if !isInitcode {
|
||||
end = min(idx+dataSize, len(b))
|
||||
}
|
||||
if topLevel && len(b) != idx+dataSize {
|
||||
return errTruncatedTopLevelContainer
|
||||
}
|
||||
c.data = b[idx:end]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateCode validates each code section of the container against the EOF v1
|
||||
// rule set.
|
||||
func (c *Container) ValidateCode(jt *JumpTable, isInitCode bool) error {
|
||||
refBy := notRefByEither
|
||||
if isInitCode {
|
||||
refBy = refByEOFCreate
|
||||
}
|
||||
return c.validateSubContainer(jt, refBy)
|
||||
}
|
||||
|
||||
func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error {
|
||||
visited := make(map[int]struct{})
|
||||
subContainerVisited := make(map[int]int)
|
||||
toVisit := []int{0}
|
||||
for len(toVisit) > 0 {
|
||||
// TODO check if this can be used as a DOS
|
||||
// Theres and edge case here where we mark something as visited that we visit before,
|
||||
// This should not trigger a re-visit
|
||||
// e.g. 0 -> 1, 2, 3
|
||||
// 1 -> 2, 3
|
||||
// should not mean 2 and 3 should be visited twice
|
||||
var (
|
||||
index = toVisit[0]
|
||||
code = c.codeSections[index]
|
||||
)
|
||||
if _, ok := visited[index]; !ok {
|
||||
res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
visited[index] = struct{}{}
|
||||
// Mark all sections that can be visited from here.
|
||||
for idx := range res.visitedCode {
|
||||
if _, ok := visited[idx]; !ok {
|
||||
toVisit = append(toVisit, idx)
|
||||
}
|
||||
}
|
||||
// Mark all subcontainer that can be visited from here.
|
||||
for idx, reference := range res.visitedSubContainers {
|
||||
// Make sure subcontainers are only ever referenced by either EOFCreate or ReturnContract
|
||||
if ref, ok := subContainerVisited[idx]; ok && ref != reference {
|
||||
return errors.New("section referenced by both EOFCreate and ReturnContract")
|
||||
}
|
||||
subContainerVisited[idx] = reference
|
||||
}
|
||||
if refBy == refByReturnContract && res.isInitCode {
|
||||
return errIncompatibleContainerKind
|
||||
}
|
||||
if refBy == refByEOFCreate && res.isRuntime {
|
||||
return errIncompatibleContainerKind
|
||||
}
|
||||
}
|
||||
toVisit = toVisit[1:]
|
||||
}
|
||||
// Make sure every code section is visited at least once.
|
||||
if len(visited) != len(c.codeSections) {
|
||||
return errUnreachableCode
|
||||
}
|
||||
for idx, container := range c.subContainers {
|
||||
reference, ok := subContainerVisited[idx]
|
||||
if !ok {
|
||||
return errOrphanedSubcontainer
|
||||
}
|
||||
if err := container.validateSubContainer(jt, reference); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseSection decodes a (kind, size) pair from an EOF header.
|
||||
func parseSection(b []byte, idx int) (kind, size int, err error) {
|
||||
if idx+3 >= len(b) {
|
||||
return 0, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
kind = int(b[idx])
|
||||
size = int(binary.BigEndian.Uint16(b[idx+1:]))
|
||||
return kind, size, nil
|
||||
}
|
||||
|
||||
// parseSectionList decodes a (kind, len, []codeSize) section list from an EOF
|
||||
// header.
|
||||
func parseSectionList(b []byte, idx int) (kind int, list []int, err error) {
|
||||
if idx >= len(b) {
|
||||
return 0, nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
kind = int(b[idx])
|
||||
list, err = parseList(b, idx+1)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return kind, list, nil
|
||||
}
|
||||
|
||||
// parseList decodes a list of uint16..
|
||||
func parseList(b []byte, idx int) ([]int, error) {
|
||||
if len(b) < idx+2 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
count := binary.BigEndian.Uint16(b[idx:])
|
||||
if len(b) <= idx+2+int(count)*2 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
list := make([]int, count)
|
||||
for i := 0; i < int(count); i++ {
|
||||
list[i] = int(binary.BigEndian.Uint16(b[idx+2+2*i:]))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// parseUint16 parses a 16 bit unsigned integer.
|
||||
func parseUint16(b []byte) (int, error) {
|
||||
if len(b) < 2 {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return int(binary.BigEndian.Uint16(b)), nil
|
||||
}
|
||||
|
||||
// parseInt16 parses a 16 bit signed integer.
|
||||
func parseInt16(b []byte) int {
|
||||
return int(int16(b[1]) | int16(b[0])<<8)
|
||||
}
|
||||
|
||||
// sum computes the sum of a slice.
|
||||
func sum(list []int) (s int) {
|
||||
for _, n := range list {
|
||||
s += n
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Container) String() string {
|
||||
var output = []string{
|
||||
"Header",
|
||||
fmt.Sprintf(" - EOFMagic: %02x", eofMagic),
|
||||
fmt.Sprintf(" - EOFVersion: %02x", eof1Version),
|
||||
fmt.Sprintf(" - KindType: %02x", kindTypes),
|
||||
fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4),
|
||||
fmt.Sprintf(" - KindCode: %02x", kindCode),
|
||||
fmt.Sprintf(" - KindData: %02x", kindData),
|
||||
fmt.Sprintf(" - DataSize: %04x", len(c.data)),
|
||||
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSections)),
|
||||
}
|
||||
for i, code := range c.codeSections {
|
||||
output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(code)))
|
||||
}
|
||||
|
||||
output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.subContainers)))
|
||||
if len(c.subContainers) > 0 {
|
||||
for i, section := range c.subContainers {
|
||||
output = append(output, fmt.Sprintf(" - subcontainer %d length: %04x\n", i, len(section.MarshalBinary())))
|
||||
}
|
||||
}
|
||||
output = append(output, "Body")
|
||||
for i, typ := range c.types {
|
||||
output = append(output, fmt.Sprintf(" - Type %v: %x", i,
|
||||
[]byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)}))
|
||||
}
|
||||
for i, code := range c.codeSections {
|
||||
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, code))
|
||||
}
|
||||
for i, section := range c.subContainers {
|
||||
output = append(output, fmt.Sprintf(" - Subcontainer %d: %x", i, section.MarshalBinary()))
|
||||
}
|
||||
output = append(output, fmt.Sprintf(" - Data: %#x", c.data))
|
||||
return strings.Join(output, "\n")
|
||||
}
|
||||
235
core/vm/eof_control_flow.go
Normal file
235
core/vm/eof_control_flow.go
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func validateControlFlow(code []byte, section int, metadata []*functionMetadata, jt *JumpTable) (int, error) {
|
||||
var (
|
||||
maxStackHeight = int(metadata[section].inputs)
|
||||
visitCount = 0
|
||||
next = make([]int, 0, 1)
|
||||
)
|
||||
var (
|
||||
stackBoundsMax = make([]uint16, len(code))
|
||||
stackBoundsMin = make([]uint16, len(code))
|
||||
)
|
||||
setBounds := func(pos, min, maxi int) {
|
||||
// The stackboundMax slice is a bit peculiar. We use `0` to denote
|
||||
// not set. Therefore, we use `1` to represent the value `0`, and so on.
|
||||
// So if the caller wants to store `1` as max bound, we internally store it as
|
||||
// `2`.
|
||||
if stackBoundsMax[pos] == 0 { // Not yet set
|
||||
visitCount++
|
||||
}
|
||||
if maxi < 65535 {
|
||||
stackBoundsMax[pos] = uint16(maxi + 1)
|
||||
}
|
||||
stackBoundsMin[pos] = uint16(min)
|
||||
maxStackHeight = max(maxStackHeight, maxi)
|
||||
}
|
||||
getStackMaxMin := func(pos int) (ok bool, min, max int) {
|
||||
maxi := stackBoundsMax[pos]
|
||||
if maxi == 0 { // Not yet set
|
||||
return false, 0, 0
|
||||
}
|
||||
return true, int(stackBoundsMin[pos]), int(maxi - 1)
|
||||
}
|
||||
// set the initial stack bounds
|
||||
setBounds(0, int(metadata[section].inputs), int(metadata[section].inputs))
|
||||
|
||||
qualifiedExit := false
|
||||
for pos := 0; pos < len(code); pos++ {
|
||||
op := OpCode(code[pos])
|
||||
ok, currentStackMin, currentStackMax := getStackMaxMin(pos)
|
||||
if !ok {
|
||||
return 0, errUnreachableCode
|
||||
}
|
||||
|
||||
switch op {
|
||||
case CALLF:
|
||||
arg, _ := parseUint16(code[pos+1:])
|
||||
newSection := metadata[arg]
|
||||
if err := newSection.checkInputs(currentStackMin); err != nil {
|
||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
||||
}
|
||||
if err := newSection.checkStackMax(currentStackMax); err != nil {
|
||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
||||
}
|
||||
delta := newSection.stackDelta()
|
||||
currentStackMax += delta
|
||||
currentStackMin += delta
|
||||
case RETF:
|
||||
/* From the spec:
|
||||
> for RETF the following must hold: stack_height_max == stack_height_min == types[current_code_index].outputs,
|
||||
|
||||
In other words: RETF must unambiguously return all items remaining on the stack.
|
||||
*/
|
||||
if currentStackMax != currentStackMin {
|
||||
return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", errInvalidOutputs, currentStackMax, currentStackMin, pos)
|
||||
}
|
||||
numOutputs := int(metadata[section].outputs)
|
||||
if numOutputs >= maxOutputItems {
|
||||
return 0, fmt.Errorf("%w: at pos %d", errInvalidNonReturningFlag, pos)
|
||||
}
|
||||
if numOutputs != currentStackMin {
|
||||
return 0, fmt.Errorf("%w: have %d, want %d, at pos %d", errInvalidOutputs, numOutputs, currentStackMin, pos)
|
||||
}
|
||||
qualifiedExit = true
|
||||
case JUMPF:
|
||||
arg, _ := parseUint16(code[pos+1:])
|
||||
newSection := metadata[arg]
|
||||
|
||||
if err := newSection.checkStackMax(currentStackMax); err != nil {
|
||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
||||
}
|
||||
|
||||
if newSection.outputs == 0x80 {
|
||||
if err := newSection.checkInputs(currentStackMin); err != nil {
|
||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
||||
}
|
||||
} else {
|
||||
if currentStackMax != currentStackMin {
|
||||
return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", errInvalidOutputs, currentStackMax, currentStackMin, pos)
|
||||
}
|
||||
wantStack := int(metadata[section].outputs) - newSection.stackDelta()
|
||||
if currentStackMax != wantStack {
|
||||
return 0, fmt.Errorf("%w: at pos %d", errInvalidOutputs, pos)
|
||||
}
|
||||
}
|
||||
qualifiedExit = qualifiedExit || newSection.outputs < maxOutputItems
|
||||
case DUPN:
|
||||
arg := int(code[pos+1]) + 1
|
||||
if want, have := arg, currentStackMin; want > have {
|
||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
||||
}
|
||||
case SWAPN:
|
||||
arg := int(code[pos+1]) + 1
|
||||
if want, have := arg+1, currentStackMin; want > have {
|
||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
||||
}
|
||||
case EXCHANGE:
|
||||
arg := int(code[pos+1])
|
||||
n := arg>>4 + 1
|
||||
m := arg&0x0f + 1
|
||||
if want, have := n+m+1, currentStackMin; want > have {
|
||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
||||
}
|
||||
default:
|
||||
if want, have := jt[op].minStack, currentStackMin; want > have {
|
||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
||||
}
|
||||
}
|
||||
if !terminals[op] && op != CALLF {
|
||||
change := int(params.StackLimit) - jt[op].maxStack
|
||||
currentStackMax += change
|
||||
currentStackMin += change
|
||||
}
|
||||
next = next[:0]
|
||||
switch op {
|
||||
case RJUMP:
|
||||
nextPos := pos + 2 + parseInt16(code[pos+1:])
|
||||
next = append(next, nextPos)
|
||||
// We set the stack bounds of the destination
|
||||
// and skip the argument, only for RJUMP, all other opcodes are handled later
|
||||
if nextPos+1 < pos {
|
||||
ok, nextMin, nextMax := getStackMaxMin(nextPos + 1)
|
||||
if !ok {
|
||||
return 0, errInvalidBackwardJump
|
||||
}
|
||||
if nextMax != currentStackMax || nextMin != currentStackMin {
|
||||
return 0, errInvalidMaxStackHeight
|
||||
}
|
||||
} else {
|
||||
ok, nextMin, nextMax := getStackMaxMin(nextPos + 1)
|
||||
if !ok {
|
||||
setBounds(nextPos+1, currentStackMin, currentStackMax)
|
||||
} else {
|
||||
setBounds(nextPos+1, min(nextMin, currentStackMin), max(nextMax, currentStackMax))
|
||||
}
|
||||
}
|
||||
case RJUMPI:
|
||||
arg := parseInt16(code[pos+1:])
|
||||
next = append(next, pos+2)
|
||||
next = append(next, pos+2+arg)
|
||||
case RJUMPV:
|
||||
count := int(code[pos+1]) + 1
|
||||
next = append(next, pos+1+2*count)
|
||||
for i := 0; i < count; i++ {
|
||||
arg := parseInt16(code[pos+2+2*i:])
|
||||
next = append(next, pos+1+2*count+arg)
|
||||
}
|
||||
default:
|
||||
if imm := int(immediates[op]); imm != 0 {
|
||||
next = append(next, pos+imm)
|
||||
} else {
|
||||
// Simple op, no operand.
|
||||
next = append(next, pos)
|
||||
}
|
||||
}
|
||||
|
||||
if op != RJUMP && !terminals[op] {
|
||||
for _, instr := range next {
|
||||
nextPC := instr + 1
|
||||
if nextPC >= len(code) {
|
||||
return 0, fmt.Errorf("%w: end with %s, pos %d", errInvalidCodeTermination, op, pos)
|
||||
}
|
||||
if nextPC > pos {
|
||||
// target reached via forward jump or seq flow
|
||||
ok, nextMin, nextMax := getStackMaxMin(nextPC)
|
||||
if !ok {
|
||||
setBounds(nextPC, currentStackMin, currentStackMax)
|
||||
} else {
|
||||
setBounds(nextPC, min(nextMin, currentStackMin), max(nextMax, currentStackMax))
|
||||
}
|
||||
} else {
|
||||
// target reached via backwards jump
|
||||
ok, nextMin, nextMax := getStackMaxMin(nextPC)
|
||||
if !ok {
|
||||
return 0, errInvalidBackwardJump
|
||||
}
|
||||
if currentStackMax != nextMax {
|
||||
return 0, fmt.Errorf("%w want %d as current max got %d at pos %d,", errInvalidBackwardJump, currentStackMax, nextMax, pos)
|
||||
}
|
||||
if currentStackMin != nextMin {
|
||||
return 0, fmt.Errorf("%w want %d as current min got %d at pos %d,", errInvalidBackwardJump, currentStackMin, nextMin, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if op == RJUMP {
|
||||
pos += 2 // skip the immediate
|
||||
} else {
|
||||
pos = next[0]
|
||||
}
|
||||
}
|
||||
if qualifiedExit != (metadata[section].outputs < maxOutputItems) {
|
||||
return 0, fmt.Errorf("%w no RETF or qualified JUMPF", errInvalidNonReturningFlag)
|
||||
}
|
||||
if maxStackHeight >= int(params.StackLimit) {
|
||||
return 0, ErrStackOverflow{maxStackHeight, int(params.StackLimit)}
|
||||
}
|
||||
if maxStackHeight != int(metadata[section].maxStackHeight) {
|
||||
return 0, fmt.Errorf("%w in code section %d: have %d, want %d", errInvalidMaxStackHeight, section, maxStackHeight, metadata[section].maxStackHeight)
|
||||
}
|
||||
return visitCount, nil
|
||||
}
|
||||
70
core/vm/eof_immediates.go
Normal file
70
core/vm/eof_immediates.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
// immediate denotes how many immediate bytes an operation uses. This information
|
||||
// is not required during runtime, only during EOF-validation, so is not
|
||||
// places into the op-struct in the instruction table.
|
||||
// Note: the immediates is fork-agnostic, and assumes that validity of opcodes at
|
||||
// the given time is performed elsewhere.
|
||||
var immediates [256]uint8
|
||||
|
||||
// terminals denotes whether instructions can be the final opcode in a code section.
|
||||
// Note: the terminals is fork-agnostic, and assumes that validity of opcodes at
|
||||
// the given time is performed elsewhere.
|
||||
var terminals [256]bool
|
||||
|
||||
func init() {
|
||||
// The legacy pushes
|
||||
for i := uint8(1); i < 33; i++ {
|
||||
immediates[int(PUSH0)+int(i)] = i
|
||||
}
|
||||
// And new eof opcodes.
|
||||
immediates[DATALOADN] = 2
|
||||
immediates[RJUMP] = 2
|
||||
immediates[RJUMPI] = 2
|
||||
immediates[RJUMPV] = 3
|
||||
immediates[CALLF] = 2
|
||||
immediates[JUMPF] = 2
|
||||
immediates[DUPN] = 1
|
||||
immediates[SWAPN] = 1
|
||||
immediates[EXCHANGE] = 1
|
||||
immediates[EOFCREATE] = 1
|
||||
immediates[RETURNCONTRACT] = 1
|
||||
|
||||
// Define the terminals.
|
||||
terminals[STOP] = true
|
||||
terminals[RETF] = true
|
||||
terminals[JUMPF] = true
|
||||
terminals[RETURNCONTRACT] = true
|
||||
terminals[RETURN] = true
|
||||
terminals[REVERT] = true
|
||||
terminals[INVALID] = true
|
||||
}
|
||||
|
||||
// Immediates returns the number bytes of immediates (argument not from
|
||||
// stack but from code) a given opcode has.
|
||||
// OBS:
|
||||
// - This function assumes EOF instruction-set. It cannot be upon in
|
||||
// a. pre-EOF code
|
||||
// b. post-EOF but legacy code
|
||||
// - RJUMPV is unique as it has a variable sized operand. The total size is
|
||||
// determined by the count byte which immediately follows RJUMPV. This method
|
||||
// will return '3' for RJUMPV, which is the minimum.
|
||||
func Immediates(op OpCode) int {
|
||||
return int(immediates[op])
|
||||
}
|
||||
112
core/vm/eof_instructions.go
Normal file
112
core/vm/eof_instructions.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
// opRjump implements the RJUMP opcode.
|
||||
func opRjump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opRjumpi implements the RJUMPI opcode
|
||||
func opRjumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opRjumpv implements the RJUMPV opcode
|
||||
func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opCallf implements the CALLF opcode
|
||||
func opCallf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opRetf implements the RETF opcode
|
||||
func opRetf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opJumpf implements the JUMPF opcode
|
||||
func opJumpf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opEOFCreate implements the EOFCREATE opcode
|
||||
func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opReturnContract implements the RETURNCONTRACT opcode
|
||||
func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opDataLoad implements the DATALOAD opcode
|
||||
func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opDataLoadN implements the DATALOADN opcode
|
||||
func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opDataSize implements the DATASIZE opcode
|
||||
func opDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opDataCopy implements the DATACOPY opcode
|
||||
func opDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opDupN implements the DUPN opcode
|
||||
func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opSwapN implements the SWAPN opcode
|
||||
func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opExchange implements the EXCHANGE opcode
|
||||
func opExchange(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opReturnDataLoad implements the RETURNDATALOAD opcode
|
||||
func opReturnDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opExtCall implements the EOFCREATE opcode
|
||||
func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opExtDelegateCall implements the EXTDELEGATECALL opcode
|
||||
func opExtDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// opExtStaticCall implements the EXTSTATICCALL opcode
|
||||
func opExtStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
119
core/vm/eof_test.go
Normal file
119
core/vm/eof_test.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestEOFMarshaling(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
want Container
|
||||
err error
|
||||
}{
|
||||
{
|
||||
want: Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
||||
data: []byte{0x01, 0x02, 0x03},
|
||||
dataSize: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
want: Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
||||
data: []byte{0x01, 0x02, 0x03},
|
||||
dataSize: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
want: Container{
|
||||
types: []*functionMetadata{
|
||||
{inputs: 0, outputs: 0x80, maxStackHeight: 1},
|
||||
{inputs: 2, outputs: 3, maxStackHeight: 4},
|
||||
{inputs: 1, outputs: 1, maxStackHeight: 1},
|
||||
},
|
||||
codeSections: [][]byte{
|
||||
common.Hex2Bytes("604200"),
|
||||
common.Hex2Bytes("6042604200"),
|
||||
common.Hex2Bytes("00"),
|
||||
},
|
||||
data: []byte{},
|
||||
},
|
||||
},
|
||||
} {
|
||||
var (
|
||||
b = test.want.MarshalBinary()
|
||||
got Container
|
||||
)
|
||||
t.Logf("b: %#x", b)
|
||||
if err := got.UnmarshalBinary(b, true); err != nil && err != test.err {
|
||||
t.Fatalf("test %d: got error \"%v\", want \"%v\"", i, err, test.err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Fatalf("test %d: got %+v, want %+v", i, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEOFSubcontainer(t *testing.T) {
|
||||
var subcontainer = new(Container)
|
||||
if err := subcontainer.UnmarshalBinary(common.Hex2Bytes("ef000101000402000100010400000000800000fe"), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container := Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
||||
subContainers: []*Container{subcontainer},
|
||||
data: []byte{0x01, 0x02, 0x03},
|
||||
dataSize: 3,
|
||||
}
|
||||
var (
|
||||
b = container.MarshalBinary()
|
||||
got Container
|
||||
)
|
||||
if err := got.UnmarshalBinary(b, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Print(got)
|
||||
if res := got.MarshalBinary(); !reflect.DeepEqual(res, b) {
|
||||
t.Fatalf("invalid marshalling, want %v got %v", b, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshaling(t *testing.T) {
|
||||
tests := []string{
|
||||
"EF000101000402000100040400000000800000E0000000",
|
||||
"ef0001010004020001000d04000000008000025fe100055f5fe000035f600100",
|
||||
}
|
||||
for i, test := range tests {
|
||||
s, err := hex.DecodeString(test)
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: error decoding: %v", i, err)
|
||||
}
|
||||
var got Container
|
||||
if err := got.UnmarshalBinary(s, true); err != nil {
|
||||
t.Fatalf("test %d: got error %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
255
core/vm/eof_validation.go
Normal file
255
core/vm/eof_validation.go
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Below are all possible errors that can occur during validation of
|
||||
// EOF containers.
|
||||
var (
|
||||
errInvalidMagic = errors.New("invalid magic")
|
||||
errUndefinedInstruction = errors.New("undefined instruction")
|
||||
errTruncatedImmediate = errors.New("truncated immediate")
|
||||
errInvalidSectionArgument = errors.New("invalid section argument")
|
||||
errInvalidCallArgument = errors.New("callf into non-returning section")
|
||||
errInvalidDataloadNArgument = errors.New("invalid dataloadN argument")
|
||||
errInvalidJumpDest = errors.New("invalid jump destination")
|
||||
errInvalidBackwardJump = errors.New("invalid backward jump")
|
||||
errInvalidOutputs = errors.New("invalid number of outputs")
|
||||
errInvalidMaxStackHeight = errors.New("invalid max stack height")
|
||||
errInvalidCodeTermination = errors.New("invalid code termination")
|
||||
errEOFCreateWithTruncatedSection = errors.New("eofcreate with truncated section")
|
||||
errOrphanedSubcontainer = errors.New("subcontainer not referenced at all")
|
||||
errIncompatibleContainerKind = errors.New("incompatible container kind")
|
||||
errStopAndReturnContract = errors.New("Stop/Return and Returncontract in the same code section")
|
||||
errStopInInitCode = errors.New("initcode contains a RETURN or STOP opcode")
|
||||
errTruncatedTopLevelContainer = errors.New("truncated top level container")
|
||||
errUnreachableCode = errors.New("unreachable code")
|
||||
errInvalidNonReturningFlag = errors.New("invalid non-returning flag, bad RETF")
|
||||
errInvalidVersion = errors.New("invalid version")
|
||||
errMissingTypeHeader = errors.New("missing type header")
|
||||
errInvalidTypeSize = errors.New("invalid type section size")
|
||||
errMissingCodeHeader = errors.New("missing code header")
|
||||
errInvalidCodeSize = errors.New("invalid code size")
|
||||
errInvalidContainerSectionSize = errors.New("invalid container section size")
|
||||
errMissingDataHeader = errors.New("missing data header")
|
||||
errMissingTerminator = errors.New("missing header terminator")
|
||||
errTooManyInputs = errors.New("invalid type content, too many inputs")
|
||||
errTooManyOutputs = errors.New("invalid type content, too many outputs")
|
||||
errInvalidSection0Type = errors.New("invalid section 0 type, input and output should be zero and non-returning (0x80)")
|
||||
errTooLargeMaxStackHeight = errors.New("invalid type content, max stack height exceeds limit")
|
||||
errInvalidContainerSize = errors.New("invalid container size")
|
||||
)
|
||||
|
||||
const (
|
||||
notRefByEither = iota
|
||||
refByReturnContract
|
||||
refByEOFCreate
|
||||
)
|
||||
|
||||
type validationResult struct {
|
||||
visitedCode map[int]struct{}
|
||||
visitedSubContainers map[int]int
|
||||
isInitCode bool
|
||||
isRuntime bool
|
||||
}
|
||||
|
||||
// validateCode validates the code parameter against the EOF v1 validity requirements.
|
||||
func validateCode(code []byte, section int, container *Container, jt *JumpTable, isInitCode bool) (*validationResult, error) {
|
||||
var (
|
||||
i = 0
|
||||
// Tracks the number of actual instructions in the code (e.g.
|
||||
// non-immediate values). This is used at the end to determine
|
||||
// if each instruction is reachable.
|
||||
count = 0
|
||||
op OpCode
|
||||
analysis bitvec
|
||||
visitedCode map[int]struct{}
|
||||
visitedSubcontainers map[int]int
|
||||
hasReturnContract bool
|
||||
hasStop bool
|
||||
)
|
||||
// This loop visits every single instruction and verifies:
|
||||
// * if the instruction is valid for the given jump table.
|
||||
// * if the instruction has an immediate value, it is not truncated.
|
||||
// * if performing a relative jump, all jump destinations are valid.
|
||||
// * if changing code sections, the new code section index is valid and
|
||||
// will not cause a stack overflow.
|
||||
for i < len(code) {
|
||||
count++
|
||||
op = OpCode(code[i])
|
||||
if jt[op].undefined {
|
||||
return nil, fmt.Errorf("%w: op %s, pos %d", errUndefinedInstruction, op, i)
|
||||
}
|
||||
size := int(immediates[op])
|
||||
if size != 0 && len(code) <= i+size {
|
||||
return nil, fmt.Errorf("%w: op %s, pos %d", errTruncatedImmediate, op, i)
|
||||
}
|
||||
switch op {
|
||||
case RJUMP, RJUMPI:
|
||||
if err := checkDest(code, &analysis, i+1, i+3, len(code)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case RJUMPV:
|
||||
max_size := int(code[i+1])
|
||||
length := max_size + 1
|
||||
if len(code) <= i+length {
|
||||
return nil, fmt.Errorf("%w: jump table truncated, op %s, pos %d", errTruncatedImmediate, op, i)
|
||||
}
|
||||
offset := i + 2
|
||||
for j := 0; j < length; j++ {
|
||||
if err := checkDest(code, &analysis, offset+j*2, offset+(length*2), len(code)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
i += 2 * max_size
|
||||
case CALLF:
|
||||
arg, _ := parseUint16(code[i+1:])
|
||||
if arg >= len(container.types) {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.types), i)
|
||||
}
|
||||
if container.types[arg].outputs == 0x80 {
|
||||
return nil, fmt.Errorf("%w: section %v", errInvalidCallArgument, arg)
|
||||
}
|
||||
if visitedCode == nil {
|
||||
visitedCode = make(map[int]struct{})
|
||||
}
|
||||
visitedCode[arg] = struct{}{}
|
||||
case JUMPF:
|
||||
arg, _ := parseUint16(code[i+1:])
|
||||
if arg >= len(container.types) {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.types), i)
|
||||
}
|
||||
if container.types[arg].outputs != 0x80 && container.types[arg].outputs > container.types[section].outputs {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidOutputs, arg, len(container.types), i)
|
||||
}
|
||||
if visitedCode == nil {
|
||||
visitedCode = make(map[int]struct{})
|
||||
}
|
||||
visitedCode[arg] = struct{}{}
|
||||
case DATALOADN:
|
||||
arg, _ := parseUint16(code[i+1:])
|
||||
// TODO why are we checking this? We should just pad
|
||||
if arg+32 > len(container.data) {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, len(container.data), i)
|
||||
}
|
||||
case RETURNCONTRACT:
|
||||
if !isInitCode {
|
||||
return nil, errIncompatibleContainerKind
|
||||
}
|
||||
arg := int(code[i+1])
|
||||
if arg >= len(container.subContainers) {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i)
|
||||
}
|
||||
if visitedSubcontainers == nil {
|
||||
visitedSubcontainers = make(map[int]int)
|
||||
}
|
||||
// We need to store per subcontainer how it was referenced
|
||||
if v, ok := visitedSubcontainers[arg]; ok && v != refByReturnContract {
|
||||
return nil, fmt.Errorf("section already referenced, arg :%d", arg)
|
||||
}
|
||||
if hasStop {
|
||||
return nil, errStopAndReturnContract
|
||||
}
|
||||
hasReturnContract = true
|
||||
visitedSubcontainers[arg] = refByReturnContract
|
||||
case EOFCREATE:
|
||||
arg := int(code[i+1])
|
||||
if arg >= len(container.subContainers) {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i)
|
||||
}
|
||||
if ct := container.subContainers[arg]; len(ct.data) != ct.dataSize {
|
||||
return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, len(ct.data), ct.dataSize, i)
|
||||
}
|
||||
if visitedSubcontainers == nil {
|
||||
visitedSubcontainers = make(map[int]int)
|
||||
}
|
||||
// We need to store per subcontainer how it was referenced
|
||||
if v, ok := visitedSubcontainers[arg]; ok && v != refByEOFCreate {
|
||||
return nil, fmt.Errorf("section already referenced, arg :%d", arg)
|
||||
}
|
||||
visitedSubcontainers[arg] = refByEOFCreate
|
||||
case STOP, RETURN:
|
||||
if isInitCode {
|
||||
return nil, errStopInInitCode
|
||||
}
|
||||
if hasReturnContract {
|
||||
return nil, errStopAndReturnContract
|
||||
}
|
||||
hasStop = true
|
||||
}
|
||||
i += size + 1
|
||||
}
|
||||
// Code sections may not "fall through" and require proper termination.
|
||||
// Therefore, the last instruction must be considered terminal or RJUMP.
|
||||
if !terminals[op] && op != RJUMP {
|
||||
return nil, fmt.Errorf("%w: end with %s, pos %d", errInvalidCodeTermination, op, i)
|
||||
}
|
||||
if paths, err := validateControlFlow(code, section, container.types, jt); err != nil {
|
||||
return nil, err
|
||||
} else if paths != count {
|
||||
// TODO(matt): return actual position of unreachable code
|
||||
return nil, errUnreachableCode
|
||||
}
|
||||
return &validationResult{
|
||||
visitedCode: visitedCode,
|
||||
visitedSubContainers: visitedSubcontainers,
|
||||
isInitCode: hasReturnContract,
|
||||
isRuntime: hasStop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkDest parses a relative offset at code[0:2] and checks if it is a valid jump destination.
|
||||
func checkDest(code []byte, analysis *bitvec, imm, from, length int) error {
|
||||
if len(code) < imm+2 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if analysis != nil && *analysis == nil {
|
||||
*analysis = eofCodeBitmap(code)
|
||||
}
|
||||
offset := parseInt16(code[imm:])
|
||||
dest := from + offset
|
||||
if dest < 0 || dest >= length {
|
||||
return fmt.Errorf("%w: out-of-bounds offset: offset %d, dest %d, pos %d", errInvalidJumpDest, offset, dest, imm)
|
||||
}
|
||||
if !analysis.codeSegment(uint64(dest)) {
|
||||
return fmt.Errorf("%w: offset into immediate: offset %d, dest %d, pos %d", errInvalidJumpDest, offset, dest, imm)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//// disasm is a helper utility to show a sequence of comma-separated operations,
|
||||
//// with immediates shown inline,
|
||||
//// e.g: PUSH1(0x00),EOFCREATE(0x00),
|
||||
//func disasm(code []byte) string {
|
||||
// var ops []string
|
||||
// for i := 0; i < len(code); i++ {
|
||||
// var op string
|
||||
// if args := immediates[code[i]]; args > 0 {
|
||||
// op = fmt.Sprintf("%v(%#x)", OpCode(code[i]).String(), code[i+1:i+1+int(args)])
|
||||
// i += int(args)
|
||||
// } else {
|
||||
// op = OpCode(code[i]).String()
|
||||
// }
|
||||
// ops = append(ops, op)
|
||||
// }
|
||||
// return strings.Join(ops, ",")
|
||||
//}
|
||||
517
core/vm/eof_validation_test.go
Normal file
517
core/vm/eof_validation_test.go
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestValidateCode(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
code []byte
|
||||
section int
|
||||
metadata []*functionMetadata
|
||||
err error
|
||||
}{
|
||||
{
|
||||
code: []byte{
|
||||
byte(CALLER),
|
||||
byte(POP),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(CALLF), 0x00, 0x00,
|
||||
byte(RETF),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 0}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(ADDRESS),
|
||||
byte(CALLF), 0x00, 0x00,
|
||||
byte(POP),
|
||||
byte(RETF),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 1}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(CALLER),
|
||||
byte(POP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
err: errInvalidCodeTermination,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(RJUMP),
|
||||
byte(0x00),
|
||||
byte(0x01),
|
||||
byte(CALLER),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 0}},
|
||||
err: errUnreachableCode,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH1),
|
||||
byte(0x42),
|
||||
byte(ADD),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
err: ErrStackUnderflow{stackLen: 1, required: 2},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH1),
|
||||
byte(0x42),
|
||||
byte(POP),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 2}},
|
||||
err: errInvalidMaxStackHeight,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPI),
|
||||
byte(0x00),
|
||||
byte(0x01),
|
||||
byte(PUSH1),
|
||||
byte(0x42), // jumps to here
|
||||
byte(POP),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
err: errInvalidJumpDest,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPV),
|
||||
byte(0x01),
|
||||
byte(0x00),
|
||||
byte(0x01),
|
||||
byte(0x00),
|
||||
byte(0x02),
|
||||
byte(PUSH1),
|
||||
byte(0x42), // jumps to here
|
||||
byte(POP), // and here
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
err: errInvalidJumpDest,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPV),
|
||||
byte(0x00),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
err: errTruncatedImmediate,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(RJUMP), 0x00, 0x03,
|
||||
byte(JUMPDEST), // this code is unreachable to forward jumps alone
|
||||
byte(JUMPDEST),
|
||||
byte(RETURN),
|
||||
byte(PUSH1), 20,
|
||||
byte(PUSH1), 39,
|
||||
byte(PUSH1), 0x00,
|
||||
byte(DATACOPY),
|
||||
byte(PUSH1), 20,
|
||||
byte(PUSH1), 0x00,
|
||||
byte(RJUMP), 0xff, 0xef,
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}},
|
||||
err: errUnreachableCode,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH1), 1,
|
||||
byte(RJUMPI), 0x00, 0x03,
|
||||
byte(JUMPDEST),
|
||||
byte(JUMPDEST),
|
||||
byte(STOP),
|
||||
byte(PUSH1), 20,
|
||||
byte(PUSH1), 39,
|
||||
byte(PUSH1), 0x00,
|
||||
byte(DATACOPY),
|
||||
byte(PUSH1), 20,
|
||||
byte(PUSH1), 0x00,
|
||||
byte(RETURN),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(PUSH1), 1,
|
||||
byte(RJUMPV), 0x01, 0x00, 0x03, 0xff, 0xf8,
|
||||
byte(JUMPDEST),
|
||||
byte(JUMPDEST),
|
||||
byte(STOP),
|
||||
byte(PUSH1), 20,
|
||||
byte(PUSH1), 39,
|
||||
byte(PUSH1), 0x00,
|
||||
byte(DATACOPY),
|
||||
byte(PUSH1), 20,
|
||||
byte(PUSH1), 0x00,
|
||||
byte(RETURN),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(STOP),
|
||||
byte(STOP),
|
||||
byte(INVALID),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 0}},
|
||||
err: errUnreachableCode,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(RETF),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 1, maxStackHeight: 0}},
|
||||
err: errInvalidOutputs,
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(RETF),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 3, outputs: 3, maxStackHeight: 3}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(CALLF), 0x00, 0x01,
|
||||
byte(POP),
|
||||
byte(STOP),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}, {inputs: 0, outputs: 1, maxStackHeight: 0}},
|
||||
},
|
||||
{
|
||||
code: []byte{
|
||||
byte(ORIGIN),
|
||||
byte(ORIGIN),
|
||||
byte(CALLF), 0x00, 0x01,
|
||||
byte(POP),
|
||||
byte(RETF),
|
||||
},
|
||||
section: 0,
|
||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 2}, {inputs: 2, outputs: 1, maxStackHeight: 2}},
|
||||
},
|
||||
} {
|
||||
container := &Container{
|
||||
types: test.metadata,
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
_, err := validateCode(test.code, test.section, container, &pragueEOFInstructionSet, false)
|
||||
if !errors.Is(err, test.err) {
|
||||
t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRJUMPI tries to benchmark the RJUMPI opcode validation
|
||||
// For this we do a bunch of RJUMPIs that jump backwards (in a potential infinite loop).
|
||||
func BenchmarkRJUMPI(b *testing.B) {
|
||||
snippet := []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPI), 0xFF, 0xFC,
|
||||
}
|
||||
code := []byte{}
|
||||
for i := 0; i < params.MaxCodeSize/len(snippet)-1; i++ {
|
||||
code = append(code, snippet...)
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
container := &Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := validateCode(code, 0, container, &pragueEOFInstructionSet, false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRJUMPV tries to benchmark the validation of the RJUMPV opcode
|
||||
// for this we set up as many RJUMPV opcodes with a full jumptable (containing 0s) as possible.
|
||||
func BenchmarkRJUMPV(b *testing.B) {
|
||||
snippet := []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPV),
|
||||
0xff, // count
|
||||
0x00, 0x00,
|
||||
}
|
||||
for i := 0; i < 255; i++ {
|
||||
snippet = append(snippet, []byte{0x00, 0x00}...)
|
||||
}
|
||||
code := []byte{}
|
||||
for i := 0; i < 24576/len(snippet)-1; i++ {
|
||||
code = append(code, snippet...)
|
||||
}
|
||||
code = append(code, byte(PUSH0))
|
||||
code = append(code, byte(STOP))
|
||||
container := &Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := validateCode(code, 0, container, &pragueEOFInstructionSet, false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEOFValidation tries to benchmark the code validation for the CALLF/RETF operation.
|
||||
// For this we set up code that calls into 1024 code sections which can either
|
||||
// - just contain a RETF opcode
|
||||
// - or code to again call into 1024 code sections.
|
||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
||||
func BenchmarkEOFValidation(b *testing.B) {
|
||||
var container Container
|
||||
var code []byte
|
||||
maxSections := 1024
|
||||
for i := 0; i < maxSections; i++ {
|
||||
code = append(code, byte(CALLF))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
||||
}
|
||||
// First container
|
||||
container.codeSections = append(container.codeSections, append(code, byte(STOP)))
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
||||
|
||||
inner := []byte{
|
||||
byte(RETF),
|
||||
}
|
||||
|
||||
for i := 0; i < 1023; i++ {
|
||||
container.codeSections = append(container.codeSections, inner)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
}
|
||||
|
||||
for i := 0; i < 12; i++ {
|
||||
container.codeSections[i+1] = append(code, byte(RETF))
|
||||
}
|
||||
|
||||
bin := container.MarshalBinary()
|
||||
if len(bin) > 48*1024 {
|
||||
b.Fatal("Exceeds 48Kb")
|
||||
}
|
||||
|
||||
var container2 Container
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := container2.UnmarshalBinary(bin, true); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := container2.ValidateCode(&pragueEOFInstructionSet, false); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEOFValidation tries to benchmark the code validation for the CALLF/RETF operation.
|
||||
// For this we set up code that calls into 1024 code sections which
|
||||
// - contain calls to some other code sections.
|
||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
||||
func BenchmarkEOFValidation2(b *testing.B) {
|
||||
var container Container
|
||||
var code []byte
|
||||
maxSections := 1024
|
||||
for i := 0; i < maxSections; i++ {
|
||||
code = append(code, byte(CALLF))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
// First container
|
||||
container.codeSections = append(container.codeSections, code)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
||||
|
||||
inner := []byte{
|
||||
byte(CALLF), 0x03, 0xE8,
|
||||
byte(CALLF), 0x03, 0xE9,
|
||||
byte(CALLF), 0x03, 0xF0,
|
||||
byte(CALLF), 0x03, 0xF1,
|
||||
byte(CALLF), 0x03, 0xF2,
|
||||
byte(CALLF), 0x03, 0xF3,
|
||||
byte(CALLF), 0x03, 0xF4,
|
||||
byte(CALLF), 0x03, 0xF5,
|
||||
byte(CALLF), 0x03, 0xF6,
|
||||
byte(CALLF), 0x03, 0xF7,
|
||||
byte(CALLF), 0x03, 0xF8,
|
||||
byte(CALLF), 0x03, 0xF,
|
||||
byte(RETF),
|
||||
}
|
||||
|
||||
for i := 0; i < 1023; i++ {
|
||||
container.codeSections = append(container.codeSections, inner)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
}
|
||||
|
||||
bin := container.MarshalBinary()
|
||||
if len(bin) > 48*1024 {
|
||||
b.Fatal("Exceeds 48Kb")
|
||||
}
|
||||
|
||||
var container2 Container
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := container2.UnmarshalBinary(bin, true); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := container2.ValidateCode(&pragueEOFInstructionSet, false); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEOFValidation3 tries to benchmark the code validation for the CALLF/RETF and RJUMPI/V operations.
|
||||
// For this we set up code that calls into 1024 code sections which either
|
||||
// - contain an RJUMP opcode
|
||||
// - contain calls to other code sections
|
||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
||||
func BenchmarkEOFValidation3(b *testing.B) {
|
||||
var container Container
|
||||
var code []byte
|
||||
snippet := []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPV),
|
||||
0xff, // count
|
||||
0x00, 0x00,
|
||||
}
|
||||
for i := 0; i < 255; i++ {
|
||||
snippet = append(snippet, []byte{0x00, 0x00}...)
|
||||
}
|
||||
code = append(code, snippet...)
|
||||
// First container, calls into all other containers
|
||||
maxSections := 1024
|
||||
for i := 0; i < maxSections; i++ {
|
||||
code = append(code, byte(CALLF))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
container.codeSections = append(container.codeSections, code)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1})
|
||||
|
||||
// Other containers
|
||||
for i := 0; i < 1023; i++ {
|
||||
container.codeSections = append(container.codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)})
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
}
|
||||
// Other containers
|
||||
for i := 0; i < 68; i++ {
|
||||
container.codeSections[i+1] = append(snippet, byte(RETF))
|
||||
container.types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1}
|
||||
}
|
||||
bin := container.MarshalBinary()
|
||||
if len(bin) > 48*1024 {
|
||||
b.Fatal("Exceeds 48Kb")
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportMetric(float64(len(bin)), "bytes")
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k := 0; k < 40; k++ {
|
||||
var container2 Container
|
||||
if err := container2.UnmarshalBinary(bin, true); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := container2.ValidateCode(&pragueEOFInstructionSet, false); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRJUMPI_2(b *testing.B) {
|
||||
code := []byte{
|
||||
byte(PUSH0),
|
||||
byte(RJUMPI), 0xFF, 0xFC,
|
||||
}
|
||||
for i := 0; i < params.MaxCodeSize/4-1; i++ {
|
||||
code = append(code, byte(PUSH0))
|
||||
x := -4 * i
|
||||
code = append(code, byte(RJUMPI))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(x))
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
container := &Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := validateCode(code, 0, container, &pragueEOFInstructionSet, false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshalBinary(f *testing.F) {
|
||||
f.Fuzz(func(_ *testing.T, input []byte) {
|
||||
var container Container
|
||||
container.UnmarshalBinary(input, true)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzValidate(f *testing.F) {
|
||||
f.Fuzz(func(_ *testing.T, code []byte, maxStack uint16) {
|
||||
var container Container
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: maxStack})
|
||||
validateCode(code, 0, &container, &pragueEOFInstructionSet, true)
|
||||
})
|
||||
}
|
||||
|
|
@ -51,10 +51,14 @@ type ErrStackUnderflow struct {
|
|||
required int
|
||||
}
|
||||
|
||||
func (e *ErrStackUnderflow) Error() string {
|
||||
func (e ErrStackUnderflow) Error() string {
|
||||
return fmt.Sprintf("stack underflow (%d <=> %d)", e.stackLen, e.required)
|
||||
}
|
||||
|
||||
func (e ErrStackUnderflow) Unwrap() error {
|
||||
return fmt.Errorf("stack underflow")
|
||||
}
|
||||
|
||||
// ErrStackOverflow wraps an evm error when the items on the stack exceeds
|
||||
// the maximum allowance.
|
||||
type ErrStackOverflow struct {
|
||||
|
|
@ -62,10 +66,14 @@ type ErrStackOverflow struct {
|
|||
limit int
|
||||
}
|
||||
|
||||
func (e *ErrStackOverflow) Error() string {
|
||||
func (e ErrStackOverflow) Error() string {
|
||||
return fmt.Sprintf("stack limit reached %d (%d)", e.stackLen, e.limit)
|
||||
}
|
||||
|
||||
func (e ErrStackOverflow) Unwrap() error {
|
||||
return fmt.Errorf("stack overflow")
|
||||
}
|
||||
|
||||
// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
|
||||
type ErrInvalidOpCode struct {
|
||||
opcode OpCode
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
const (
|
||||
GasQuickStep uint64 = 2
|
||||
GasFastestStep uint64 = 3
|
||||
GasFastishStep uint64 = 4
|
||||
GasFastStep uint64 = 5
|
||||
GasMidStep uint64 = 8
|
||||
GasSlowStep uint64 = 10
|
||||
|
|
|
|||
|
|
@ -502,3 +502,20 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExtCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func gasExtDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func gasExtStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// gasEOFCreate returns the gas-cost for EOF-Create. Hashing charge needs to be
|
||||
// deducted in the opcode itself, since it depends on the immediate
|
||||
func gasEOFCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ type operation struct {
|
|||
|
||||
// memorySize returns the memory size required for the operation
|
||||
memorySize memorySizeFunc
|
||||
|
||||
// undefined denotes if the instruction is not officially defined in the jump table
|
||||
undefined bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -58,6 +61,7 @@ var (
|
|||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||
cancunInstructionSet = newCancunInstructionSet()
|
||||
verkleInstructionSet = newVerkleInstructionSet()
|
||||
pragueEOFInstructionSet = newPragueEOFInstructionSet()
|
||||
)
|
||||
|
||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||
|
|
@ -87,6 +91,16 @@ func newVerkleInstructionSet() JumpTable {
|
|||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
func NewPragueEOFInstructionSetForTesting() JumpTable {
|
||||
return newPragueEOFInstructionSet()
|
||||
}
|
||||
|
||||
func newPragueEOFInstructionSet() JumpTable {
|
||||
instructionSet := newCancunInstructionSet()
|
||||
enableEOF(&instructionSet)
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
func newCancunInstructionSet() JumpTable {
|
||||
instructionSet := newShanghaiInstructionSet()
|
||||
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
||||
|
|
@ -1059,12 +1073,17 @@ func newFrontierInstructionSet() JumpTable {
|
|||
minStack: minStack(1, 0),
|
||||
maxStack: maxStack(1, 0),
|
||||
},
|
||||
INVALID: {
|
||||
execute: opUndefined,
|
||||
minStack: minStack(0, 0),
|
||||
maxStack: maxStack(0, 0),
|
||||
},
|
||||
}
|
||||
|
||||
// Fill all unassigned slots with opUndefined.
|
||||
for i, entry := range tbl {
|
||||
if entry == nil {
|
||||
tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)}
|
||||
tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0), undefined: true}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ func memoryCall(stack *Stack) (uint64, bool) {
|
|||
}
|
||||
return y, false
|
||||
}
|
||||
|
||||
func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
||||
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||
if overflow {
|
||||
|
|
@ -119,3 +120,19 @@ func memoryRevert(stack *Stack) (uint64, bool) {
|
|||
func memoryLog(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryExtCall(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryDataCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryEOFCreate(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(2), stack.Back(3))
|
||||
}
|
||||
|
||||
func memoryReturnContract(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import (
|
|||
type OpCode byte
|
||||
|
||||
// IsPush specifies if an opcode is a PUSH opcode.
|
||||
// @deprecated: this method is often used in order to know if there are immediates.
|
||||
// Please use `vm.Immediates` instead.
|
||||
func (op OpCode) IsPush() bool {
|
||||
return PUSH0 <= op && op <= PUSH32
|
||||
}
|
||||
|
|
@ -209,6 +211,29 @@ const (
|
|||
LOG4
|
||||
)
|
||||
|
||||
// 0xd0 range - eof operations.
|
||||
const (
|
||||
DATALOAD OpCode = 0xd0
|
||||
DATALOADN OpCode = 0xd1
|
||||
DATASIZE OpCode = 0xd2
|
||||
DATACOPY OpCode = 0xd3
|
||||
)
|
||||
|
||||
// 0xe0 range - eof operations.
|
||||
const (
|
||||
RJUMP OpCode = 0xe0
|
||||
RJUMPI OpCode = 0xe1
|
||||
RJUMPV OpCode = 0xe2
|
||||
CALLF OpCode = 0xe3
|
||||
RETF OpCode = 0xe4
|
||||
JUMPF OpCode = 0xe5
|
||||
DUPN OpCode = 0xe6
|
||||
SWAPN OpCode = 0xe7
|
||||
EXCHANGE OpCode = 0xe8
|
||||
EOFCREATE OpCode = 0xec
|
||||
RETURNCONTRACT OpCode = 0xee
|
||||
)
|
||||
|
||||
// 0xf0 range - closures.
|
||||
const (
|
||||
CREATE OpCode = 0xf0
|
||||
|
|
@ -218,10 +243,15 @@ const (
|
|||
DELEGATECALL OpCode = 0xf4
|
||||
CREATE2 OpCode = 0xf5
|
||||
|
||||
STATICCALL OpCode = 0xfa
|
||||
REVERT OpCode = 0xfd
|
||||
INVALID OpCode = 0xfe
|
||||
SELFDESTRUCT OpCode = 0xff
|
||||
RETURNDATALOAD OpCode = 0xf7
|
||||
EXTCALL OpCode = 0xf8
|
||||
EXTDELEGATECALL OpCode = 0xf9
|
||||
|
||||
STATICCALL OpCode = 0xfa
|
||||
EXTSTATICCALL OpCode = 0xfb
|
||||
REVERT OpCode = 0xfd
|
||||
INVALID OpCode = 0xfe
|
||||
SELFDESTRUCT OpCode = 0xff
|
||||
)
|
||||
|
||||
var opCodeToString = [256]string{
|
||||
|
|
@ -384,6 +414,25 @@ var opCodeToString = [256]string{
|
|||
LOG3: "LOG3",
|
||||
LOG4: "LOG4",
|
||||
|
||||
// 0xd range - eof ops.
|
||||
DATALOAD: "DATALOAD",
|
||||
DATALOADN: "DATALOADN",
|
||||
DATASIZE: "DATASIZE",
|
||||
DATACOPY: "DATACOPY",
|
||||
|
||||
// 0xe0 range.
|
||||
RJUMP: "RJUMP",
|
||||
RJUMPI: "RJUMPI",
|
||||
RJUMPV: "RJUMPV",
|
||||
CALLF: "CALLF",
|
||||
RETF: "RETF",
|
||||
JUMPF: "JUMPF",
|
||||
DUPN: "DUPN",
|
||||
SWAPN: "SWAPN",
|
||||
EXCHANGE: "EXCHANGE",
|
||||
EOFCREATE: "EOFCREATE",
|
||||
RETURNCONTRACT: "RETURNCONTRACT",
|
||||
|
||||
// 0xf0 range - closures.
|
||||
CREATE: "CREATE",
|
||||
CALL: "CALL",
|
||||
|
|
@ -391,10 +440,16 @@ var opCodeToString = [256]string{
|
|||
CALLCODE: "CALLCODE",
|
||||
DELEGATECALL: "DELEGATECALL",
|
||||
CREATE2: "CREATE2",
|
||||
STATICCALL: "STATICCALL",
|
||||
REVERT: "REVERT",
|
||||
INVALID: "INVALID",
|
||||
SELFDESTRUCT: "SELFDESTRUCT",
|
||||
|
||||
RETURNDATALOAD: "RETURNDATALOAD",
|
||||
EXTCALL: "EXTCALL",
|
||||
EXTDELEGATECALL: "EXTDELEGATECALL",
|
||||
|
||||
STATICCALL: "STATICCALL",
|
||||
EXTSTATICCALL: "EXTSTATICCALL",
|
||||
REVERT: "REVERT",
|
||||
INVALID: "INVALID",
|
||||
SELFDESTRUCT: "SELFDESTRUCT",
|
||||
}
|
||||
|
||||
func (op OpCode) String() string {
|
||||
|
|
@ -405,155 +460,174 @@ func (op OpCode) String() string {
|
|||
}
|
||||
|
||||
var stringToOp = map[string]OpCode{
|
||||
"STOP": STOP,
|
||||
"ADD": ADD,
|
||||
"MUL": MUL,
|
||||
"SUB": SUB,
|
||||
"DIV": DIV,
|
||||
"SDIV": SDIV,
|
||||
"MOD": MOD,
|
||||
"SMOD": SMOD,
|
||||
"EXP": EXP,
|
||||
"NOT": NOT,
|
||||
"LT": LT,
|
||||
"GT": GT,
|
||||
"SLT": SLT,
|
||||
"SGT": SGT,
|
||||
"EQ": EQ,
|
||||
"ISZERO": ISZERO,
|
||||
"SIGNEXTEND": SIGNEXTEND,
|
||||
"AND": AND,
|
||||
"OR": OR,
|
||||
"XOR": XOR,
|
||||
"BYTE": BYTE,
|
||||
"SHL": SHL,
|
||||
"SHR": SHR,
|
||||
"SAR": SAR,
|
||||
"ADDMOD": ADDMOD,
|
||||
"MULMOD": MULMOD,
|
||||
"KECCAK256": KECCAK256,
|
||||
"ADDRESS": ADDRESS,
|
||||
"BALANCE": BALANCE,
|
||||
"ORIGIN": ORIGIN,
|
||||
"CALLER": CALLER,
|
||||
"CALLVALUE": CALLVALUE,
|
||||
"CALLDATALOAD": CALLDATALOAD,
|
||||
"CALLDATASIZE": CALLDATASIZE,
|
||||
"CALLDATACOPY": CALLDATACOPY,
|
||||
"CHAINID": CHAINID,
|
||||
"BASEFEE": BASEFEE,
|
||||
"BLOBHASH": BLOBHASH,
|
||||
"BLOBBASEFEE": BLOBBASEFEE,
|
||||
"DELEGATECALL": DELEGATECALL,
|
||||
"STATICCALL": STATICCALL,
|
||||
"CODESIZE": CODESIZE,
|
||||
"CODECOPY": CODECOPY,
|
||||
"GASPRICE": GASPRICE,
|
||||
"EXTCODESIZE": EXTCODESIZE,
|
||||
"EXTCODECOPY": EXTCODECOPY,
|
||||
"RETURNDATASIZE": RETURNDATASIZE,
|
||||
"RETURNDATACOPY": RETURNDATACOPY,
|
||||
"EXTCODEHASH": EXTCODEHASH,
|
||||
"BLOCKHASH": BLOCKHASH,
|
||||
"COINBASE": COINBASE,
|
||||
"TIMESTAMP": TIMESTAMP,
|
||||
"NUMBER": NUMBER,
|
||||
"DIFFICULTY": DIFFICULTY,
|
||||
"GASLIMIT": GASLIMIT,
|
||||
"SELFBALANCE": SELFBALANCE,
|
||||
"POP": POP,
|
||||
"MLOAD": MLOAD,
|
||||
"MSTORE": MSTORE,
|
||||
"MSTORE8": MSTORE8,
|
||||
"SLOAD": SLOAD,
|
||||
"SSTORE": SSTORE,
|
||||
"JUMP": JUMP,
|
||||
"JUMPI": JUMPI,
|
||||
"PC": PC,
|
||||
"MSIZE": MSIZE,
|
||||
"GAS": GAS,
|
||||
"JUMPDEST": JUMPDEST,
|
||||
"TLOAD": TLOAD,
|
||||
"TSTORE": TSTORE,
|
||||
"MCOPY": MCOPY,
|
||||
"PUSH0": PUSH0,
|
||||
"PUSH1": PUSH1,
|
||||
"PUSH2": PUSH2,
|
||||
"PUSH3": PUSH3,
|
||||
"PUSH4": PUSH4,
|
||||
"PUSH5": PUSH5,
|
||||
"PUSH6": PUSH6,
|
||||
"PUSH7": PUSH7,
|
||||
"PUSH8": PUSH8,
|
||||
"PUSH9": PUSH9,
|
||||
"PUSH10": PUSH10,
|
||||
"PUSH11": PUSH11,
|
||||
"PUSH12": PUSH12,
|
||||
"PUSH13": PUSH13,
|
||||
"PUSH14": PUSH14,
|
||||
"PUSH15": PUSH15,
|
||||
"PUSH16": PUSH16,
|
||||
"PUSH17": PUSH17,
|
||||
"PUSH18": PUSH18,
|
||||
"PUSH19": PUSH19,
|
||||
"PUSH20": PUSH20,
|
||||
"PUSH21": PUSH21,
|
||||
"PUSH22": PUSH22,
|
||||
"PUSH23": PUSH23,
|
||||
"PUSH24": PUSH24,
|
||||
"PUSH25": PUSH25,
|
||||
"PUSH26": PUSH26,
|
||||
"PUSH27": PUSH27,
|
||||
"PUSH28": PUSH28,
|
||||
"PUSH29": PUSH29,
|
||||
"PUSH30": PUSH30,
|
||||
"PUSH31": PUSH31,
|
||||
"PUSH32": PUSH32,
|
||||
"DUP1": DUP1,
|
||||
"DUP2": DUP2,
|
||||
"DUP3": DUP3,
|
||||
"DUP4": DUP4,
|
||||
"DUP5": DUP5,
|
||||
"DUP6": DUP6,
|
||||
"DUP7": DUP7,
|
||||
"DUP8": DUP8,
|
||||
"DUP9": DUP9,
|
||||
"DUP10": DUP10,
|
||||
"DUP11": DUP11,
|
||||
"DUP12": DUP12,
|
||||
"DUP13": DUP13,
|
||||
"DUP14": DUP14,
|
||||
"DUP15": DUP15,
|
||||
"DUP16": DUP16,
|
||||
"SWAP1": SWAP1,
|
||||
"SWAP2": SWAP2,
|
||||
"SWAP3": SWAP3,
|
||||
"SWAP4": SWAP4,
|
||||
"SWAP5": SWAP5,
|
||||
"SWAP6": SWAP6,
|
||||
"SWAP7": SWAP7,
|
||||
"SWAP8": SWAP8,
|
||||
"SWAP9": SWAP9,
|
||||
"SWAP10": SWAP10,
|
||||
"SWAP11": SWAP11,
|
||||
"SWAP12": SWAP12,
|
||||
"SWAP13": SWAP13,
|
||||
"SWAP14": SWAP14,
|
||||
"SWAP15": SWAP15,
|
||||
"SWAP16": SWAP16,
|
||||
"LOG0": LOG0,
|
||||
"LOG1": LOG1,
|
||||
"LOG2": LOG2,
|
||||
"LOG3": LOG3,
|
||||
"LOG4": LOG4,
|
||||
"CREATE": CREATE,
|
||||
"CREATE2": CREATE2,
|
||||
"CALL": CALL,
|
||||
"RETURN": RETURN,
|
||||
"CALLCODE": CALLCODE,
|
||||
"REVERT": REVERT,
|
||||
"INVALID": INVALID,
|
||||
"SELFDESTRUCT": SELFDESTRUCT,
|
||||
"STOP": STOP,
|
||||
"ADD": ADD,
|
||||
"MUL": MUL,
|
||||
"SUB": SUB,
|
||||
"DIV": DIV,
|
||||
"SDIV": SDIV,
|
||||
"MOD": MOD,
|
||||
"SMOD": SMOD,
|
||||
"EXP": EXP,
|
||||
"NOT": NOT,
|
||||
"LT": LT,
|
||||
"GT": GT,
|
||||
"SLT": SLT,
|
||||
"SGT": SGT,
|
||||
"EQ": EQ,
|
||||
"ISZERO": ISZERO,
|
||||
"SIGNEXTEND": SIGNEXTEND,
|
||||
"AND": AND,
|
||||
"OR": OR,
|
||||
"XOR": XOR,
|
||||
"BYTE": BYTE,
|
||||
"SHL": SHL,
|
||||
"SHR": SHR,
|
||||
"SAR": SAR,
|
||||
"ADDMOD": ADDMOD,
|
||||
"MULMOD": MULMOD,
|
||||
"KECCAK256": KECCAK256,
|
||||
"ADDRESS": ADDRESS,
|
||||
"BALANCE": BALANCE,
|
||||
"ORIGIN": ORIGIN,
|
||||
"CALLER": CALLER,
|
||||
"CALLVALUE": CALLVALUE,
|
||||
"CALLDATALOAD": CALLDATALOAD,
|
||||
"CALLDATASIZE": CALLDATASIZE,
|
||||
"CALLDATACOPY": CALLDATACOPY,
|
||||
"CHAINID": CHAINID,
|
||||
"BASEFEE": BASEFEE,
|
||||
"BLOBHASH": BLOBHASH,
|
||||
"BLOBBASEFEE": BLOBBASEFEE,
|
||||
"DELEGATECALL": DELEGATECALL,
|
||||
"STATICCALL": STATICCALL,
|
||||
"CODESIZE": CODESIZE,
|
||||
"CODECOPY": CODECOPY,
|
||||
"GASPRICE": GASPRICE,
|
||||
"EXTCODESIZE": EXTCODESIZE,
|
||||
"EXTCODECOPY": EXTCODECOPY,
|
||||
"RETURNDATASIZE": RETURNDATASIZE,
|
||||
"RETURNDATACOPY": RETURNDATACOPY,
|
||||
"EXTCODEHASH": EXTCODEHASH,
|
||||
"BLOCKHASH": BLOCKHASH,
|
||||
"COINBASE": COINBASE,
|
||||
"TIMESTAMP": TIMESTAMP,
|
||||
"NUMBER": NUMBER,
|
||||
"DIFFICULTY": DIFFICULTY,
|
||||
"GASLIMIT": GASLIMIT,
|
||||
"SELFBALANCE": SELFBALANCE,
|
||||
"POP": POP,
|
||||
"MLOAD": MLOAD,
|
||||
"MSTORE": MSTORE,
|
||||
"MSTORE8": MSTORE8,
|
||||
"SLOAD": SLOAD,
|
||||
"SSTORE": SSTORE,
|
||||
"JUMP": JUMP,
|
||||
"JUMPI": JUMPI,
|
||||
"PC": PC,
|
||||
"MSIZE": MSIZE,
|
||||
"GAS": GAS,
|
||||
"JUMPDEST": JUMPDEST,
|
||||
"TLOAD": TLOAD,
|
||||
"TSTORE": TSTORE,
|
||||
"MCOPY": MCOPY,
|
||||
"PUSH0": PUSH0,
|
||||
"PUSH1": PUSH1,
|
||||
"PUSH2": PUSH2,
|
||||
"PUSH3": PUSH3,
|
||||
"PUSH4": PUSH4,
|
||||
"PUSH5": PUSH5,
|
||||
"PUSH6": PUSH6,
|
||||
"PUSH7": PUSH7,
|
||||
"PUSH8": PUSH8,
|
||||
"PUSH9": PUSH9,
|
||||
"PUSH10": PUSH10,
|
||||
"PUSH11": PUSH11,
|
||||
"PUSH12": PUSH12,
|
||||
"PUSH13": PUSH13,
|
||||
"PUSH14": PUSH14,
|
||||
"PUSH15": PUSH15,
|
||||
"PUSH16": PUSH16,
|
||||
"PUSH17": PUSH17,
|
||||
"PUSH18": PUSH18,
|
||||
"PUSH19": PUSH19,
|
||||
"PUSH20": PUSH20,
|
||||
"PUSH21": PUSH21,
|
||||
"PUSH22": PUSH22,
|
||||
"PUSH23": PUSH23,
|
||||
"PUSH24": PUSH24,
|
||||
"PUSH25": PUSH25,
|
||||
"PUSH26": PUSH26,
|
||||
"PUSH27": PUSH27,
|
||||
"PUSH28": PUSH28,
|
||||
"PUSH29": PUSH29,
|
||||
"PUSH30": PUSH30,
|
||||
"PUSH31": PUSH31,
|
||||
"PUSH32": PUSH32,
|
||||
"DUP1": DUP1,
|
||||
"DUP2": DUP2,
|
||||
"DUP3": DUP3,
|
||||
"DUP4": DUP4,
|
||||
"DUP5": DUP5,
|
||||
"DUP6": DUP6,
|
||||
"DUP7": DUP7,
|
||||
"DUP8": DUP8,
|
||||
"DUP9": DUP9,
|
||||
"DUP10": DUP10,
|
||||
"DUP11": DUP11,
|
||||
"DUP12": DUP12,
|
||||
"DUP13": DUP13,
|
||||
"DUP14": DUP14,
|
||||
"DUP15": DUP15,
|
||||
"DUP16": DUP16,
|
||||
"SWAP1": SWAP1,
|
||||
"SWAP2": SWAP2,
|
||||
"SWAP3": SWAP3,
|
||||
"SWAP4": SWAP4,
|
||||
"SWAP5": SWAP5,
|
||||
"SWAP6": SWAP6,
|
||||
"SWAP7": SWAP7,
|
||||
"SWAP8": SWAP8,
|
||||
"SWAP9": SWAP9,
|
||||
"SWAP10": SWAP10,
|
||||
"SWAP11": SWAP11,
|
||||
"SWAP12": SWAP12,
|
||||
"SWAP13": SWAP13,
|
||||
"SWAP14": SWAP14,
|
||||
"SWAP15": SWAP15,
|
||||
"SWAP16": SWAP16,
|
||||
"LOG0": LOG0,
|
||||
"LOG1": LOG1,
|
||||
"LOG2": LOG2,
|
||||
"LOG3": LOG3,
|
||||
"LOG4": LOG4,
|
||||
"DATALOAD": DATALOAD,
|
||||
"DATALOADN": DATALOADN,
|
||||
"DATASIZE": DATASIZE,
|
||||
"DATACOPY": DATACOPY,
|
||||
"RJUMP": RJUMP,
|
||||
"RJUMPI": RJUMPI,
|
||||
"RJUMPV": RJUMPV,
|
||||
"CALLF": CALLF,
|
||||
"RETF": RETF,
|
||||
"JUMPF": JUMPF,
|
||||
"DUPN": DUPN,
|
||||
"SWAPN": SWAPN,
|
||||
"EXCHANGE": EXCHANGE,
|
||||
"EOFCREATE": EOFCREATE,
|
||||
"RETURNCONTRACT": RETURNCONTRACT,
|
||||
"CREATE": CREATE,
|
||||
"CREATE2": CREATE2,
|
||||
"RETURNDATALOAD": RETURNDATALOAD,
|
||||
"EXTCALL": EXTCALL,
|
||||
"EXTDELEGATECALL": EXTDELEGATECALL,
|
||||
"EXTSTATICCALL": EXTSTATICCALL,
|
||||
"CALL": CALL,
|
||||
"RETURN": RETURN,
|
||||
"CALLCODE": CALLCODE,
|
||||
"REVERT": REVERT,
|
||||
"INVALID": INVALID,
|
||||
"SELFDESTRUCT": SELFDESTRUCT,
|
||||
}
|
||||
|
||||
// StringToOp finds the opcode whose name is stored in `str`.
|
||||
|
|
|
|||
|
|
@ -152,9 +152,9 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(1).Bytes20())
|
||||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||
|
|
@ -192,10 +192,10 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
|||
}
|
||||
|
||||
var (
|
||||
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall)
|
||||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
||||
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1)
|
||||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1)
|
||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1)
|
||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1)
|
||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||||
|
|
|
|||
|
|
@ -306,7 +306,8 @@ func (c *SimulatedBeacon) Fork(parentHash common.Hash) error {
|
|||
if parent == nil {
|
||||
return errors.New("parent not found")
|
||||
}
|
||||
return c.eth.BlockChain().SetHead(parent.NumberU64())
|
||||
_, err := c.eth.BlockChain().SetCanonical(parent)
|
||||
return err
|
||||
}
|
||||
|
||||
// AdjustTime creates a new block with an adjusted timestamp.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x577f42ab21ccfd946511c57869ace0bdf7c217c36f02b7cd3459df0ed1cffc1a",
|
||||
"timestamp": "1709626771",
|
||||
"totalDifficulty": "1",
|
||||
"withdrawals": [],
|
||||
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"alloc": {
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@
|
|||
"nonce": "0x93363bbd2c95f410",
|
||||
"number": "2294701",
|
||||
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||
"timestamp": "1513676127",
|
||||
"totalDifficulty": "7160808139332585"
|
||||
"timestamp": "1513676127"
|
||||
},
|
||||
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -104,8 +104,7 @@
|
|||
"nonce": "0x38dee147326a8d40",
|
||||
"number": "25000",
|
||||
"stateRoot": "0xc5d6bbcd46236fcdcc80b332ffaaa5476b980b01608f9708408cfef01b58bd5b",
|
||||
"timestamp": "1479891517",
|
||||
"totalDifficulty": "1895410389427"
|
||||
"timestamp": "1479891517"
|
||||
},
|
||||
"input": "0xf88b8206628504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb80000000000000000000000000000000000000000000000280faf689c35ac00002aa0a7ee5b7877811bf671d121b40569462e722657044808dc1d6c4f1e4233ec145ba0417e7543d52b65738d9df419cbe40a708424f4d54b0fc145c0a64545a2bb1065",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@
|
|||
"nonce": "0x684129f283aaef18",
|
||||
"number": "11494",
|
||||
"stateRoot": "0x7057f31fe3dab1d620771adad35224aae43eb70e94861208bc84c557ff5b9d10",
|
||||
"timestamp": "1479735912",
|
||||
"totalDifficulty": "90744064339"
|
||||
"timestamp": "1479735912"
|
||||
},
|
||||
"input": "0xf889448504a817c800832dc6c094269296dddce321a6bcbaa2f0181127593d732cba80a47065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e29a080ed81e4c5e9971a730efab4885566e2c868cd80bd4166d0ed8c287fdf181650a069d7c49215e3d4416ad239cd09dbb71b9f04c16b33b385d14f40b618a7a65115",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@
|
|||
"nonce": "0x28c446f1cb9748c1",
|
||||
"number": "2290743",
|
||||
"stateRoot": "0x4898aceede76739daef76448a367d10015a2c022c9e7909b99a10fbf6fb16708",
|
||||
"timestamp": "1513616414",
|
||||
"totalDifficulty": "7146523769022564"
|
||||
"timestamp": "1513616414"
|
||||
},
|
||||
"input": "0xf8aa0e8509502f9000830493e0941d3ddf7caf024f253487e18bc4a15b1a360c170a80b8443b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e829a0524564944fa419f5c189b5074044f89210c6d6b2d77ee8f7f12a927d59b636dfa0015b28986807a424b18b186ee6642d76739df36cad802d20e8c00e79a61d7281",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "24973",
|
||||
"stateRoot": "0x532a5c3f75453a696428db078e32ae283c85cb97e4d8560dbdf022adac6df369",
|
||||
"timestamp": "1479891145",
|
||||
"totalDifficulty": "1892250259406",
|
||||
"alloc": {
|
||||
"0x6c06b16512b332e6cd8293a2974872674716ce18": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
"number": "0",
|
||||
"stateRoot": "0xa775801d572e9b79585eb131d18d79f8a0f71895455ab9a5b656911428e11708",
|
||||
"timestamp": "0",
|
||||
"totalDifficulty": "1",
|
||||
"alloc": {
|
||||
"0x3623191d4ccfbbdf09e8ebf6382a1f8257417bc1": {
|
||||
"balance": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7"
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x810f923ff4b450a1",
|
||||
"number": "2295103",
|
||||
"stateRoot": "0xff403612573d76dfdaf4fea2429b77dbe9764021ae0e38dc8ac79a3cf551179e",
|
||||
"timestamp": "1513681246",
|
||||
"totalDifficulty": "7162347056825919"
|
||||
"timestamp": "1513681246"
|
||||
},
|
||||
"input": "0xf86d808504e3b292008307dfa69433056b5dcac09a9b4becad0e1dcf92c19bd0af76880e92596fd62900008029a0e5f27bb66431f7081bb7f1f242003056d7f3f35414c352cd3d1848b52716dac2a07d0be78980edb0bd2a0678fc53aa90ea9558ce346b0d947967216918ac74ccea",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@
|
|||
"nonce": "0xd1bdb150f6fd170e",
|
||||
"number": "2294630",
|
||||
"stateRoot": "0x1ab1a534e84cc787cda1db21e0d5920ab06017948075b759166cfea7274657a1",
|
||||
"timestamp": "1513675347",
|
||||
"totalDifficulty": "7160543502214733"
|
||||
"timestamp": "1513675347"
|
||||
},
|
||||
"input": "0xf8ab820109855d21dba00082ca1d9443064693d3d38ad6a7cb579e0d6d9718c8aa6b6280b844a9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f90001ba0ce3ad83f5530136467b7c2bb225f406bd170f4ad59c254e5103c34eeabb5bd69a0455154527224a42ab405cacf0fe92918a75641ce4152f8db292019a5527aa956",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@
|
|||
"nonce": "0xbc5d43adc2c30c7d",
|
||||
"number": "2294500",
|
||||
"stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
|
||||
"timestamp": "1513673552",
|
||||
"totalDifficulty": "7160066586979149"
|
||||
"timestamp": "1513673552"
|
||||
},
|
||||
"input": "0xf9018b0a8505d21dba00832dc6c094abbcd5b340c80b5f1c0545c04c987b87310296ae80b9012473b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988000000000000000000000000000000000000000000000000000000000000000000000000000000001ba0fd659d76a4edbd2a823e324c93f78ad6803b30ff4a9c8bce71ba82798975c70ca06571eecc0b765688ec6c78942c5ee8b585e00988c0141b518287e9be919bc48a",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf888068449504f80832dc6c094f58833cf0c791881b494eb79d461e08a1f043f5280a45c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf12da0264664db3e71fae1dbdaf2f53954be149ad3b7ba8a5054b4d89c70febfacc8b1a0212e8398757963f419681839ae8c5a54b411e252473c82d93dda68405ca63294",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"tracerConfig": {
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@
|
|||
"nonce": "0x70849d5838dee2e9",
|
||||
"number": "25008",
|
||||
"stateRoot": "0x1e01d2161794768c5b917069e73d86e8dca80cd7f3168c0597de420ab93a3b7b",
|
||||
"timestamp": "1479891641",
|
||||
"totalDifficulty": "1896347038589"
|
||||
"timestamp": "1479891641"
|
||||
},
|
||||
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1719576",
|
||||
"stateRoot": "0xab5eec2177a92d633e282936af66c46e24cfa8f2fdc2b8155f33885f483d06f3",
|
||||
"timestamp": "1466150166",
|
||||
"totalDifficulty": "28295412423546970038",
|
||||
"alloc": {
|
||||
"0xf8bda96b67036ee48107f2a0695ea673479dda56": {
|
||||
"balance": "0x1529e844f9ecdeec",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555278",
|
||||
"stateRoot": "0x5f56d8323ee384b0c8d1de49d63e150e17283eea813483698362bc0ec9e0242a",
|
||||
"timestamp": "1590795319",
|
||||
"totalDifficulty": "2242614315030",
|
||||
"alloc": {
|
||||
"0x0000000000000000000000000000000000000004": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555277",
|
||||
"stateRoot": "0x6290d79215a2eebc25d5e456b35876c6d78ffc1ea47bdd70e375ebb3cf325620",
|
||||
"timestamp": "1590795308",
|
||||
"totalDifficulty": "2242609643446",
|
||||
"alloc": {
|
||||
"0x0000000000000000000000000000000000000001": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555274",
|
||||
"stateRoot": "0x795751f3f96a5de1fd3944ddd78cbfe4ef10491e1086be47609869a30929d0e5",
|
||||
"timestamp": "1590795228",
|
||||
"totalDifficulty": "2242595605834",
|
||||
"alloc": {
|
||||
"0x0000000000000000000000000000000000000009": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@
|
|||
"nonce": "0x93363bbd2c95f410",
|
||||
"number": "2294701",
|
||||
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||
"timestamp": "1513676127",
|
||||
"totalDifficulty": "7160808139332585"
|
||||
"timestamp": "1513676127"
|
||||
},
|
||||
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -104,8 +104,7 @@
|
|||
"nonce": "0x38dee147326a8d40",
|
||||
"number": "25000",
|
||||
"stateRoot": "0xc5d6bbcd46236fcdcc80b332ffaaa5476b980b01608f9708408cfef01b58bd5b",
|
||||
"timestamp": "1479891517",
|
||||
"totalDifficulty": "1895410389427"
|
||||
"timestamp": "1479891517"
|
||||
},
|
||||
"input": "0xf88b8206628504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb80000000000000000000000000000000000000000000000280faf689c35ac00002aa0a7ee5b7877811bf671d121b40569462e722657044808dc1d6c4f1e4233ec145ba0417e7543d52b65738d9df419cbe40a708424f4d54b0fc145c0a64545a2bb1065",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@
|
|||
"nonce": "0x684129f283aaef18",
|
||||
"number": "11494",
|
||||
"stateRoot": "0x7057f31fe3dab1d620771adad35224aae43eb70e94861208bc84c557ff5b9d10",
|
||||
"timestamp": "1479735912",
|
||||
"totalDifficulty": "90744064339"
|
||||
"timestamp": "1479735912"
|
||||
},
|
||||
"input": "0xf889448504a817c800832dc6c094269296dddce321a6bcbaa2f0181127593d732cba80a47065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e29a080ed81e4c5e9971a730efab4885566e2c868cd80bd4166d0ed8c287fdf181650a069d7c49215e3d4416ad239cd09dbb71b9f04c16b33b385d14f40b618a7a65115",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
"stateRoot": "0xfc01993ad96a8fb8790a093cea4f505f8db1b0e1143c5f57bb1d173db0baa9e3",
|
||||
"miner": "0x877bd459c9b7d8576b44e59e09d076c25946f443",
|
||||
"difficulty": "1926740",
|
||||
"totalDifficulty": "482216286599",
|
||||
"extraData": "0xd883010906846765746888676f312e31332e35856c696e7578",
|
||||
"gasLimit": "19388354",
|
||||
"timestamp": "1577558314",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555274",
|
||||
"stateRoot": "0x795751f3f96a5de1fd3944ddd78cbfe4ef10491e1086be47609869a30929d0e5",
|
||||
"timestamp": "1590795228",
|
||||
"totalDifficulty": "2242595605834",
|
||||
"alloc": {
|
||||
"0x0000000000000000000000000000000000000001": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
"stateRoot": "0x68418fb5cf4afa9b807dc079e8cdde0e148ac2c8afb378e675465b5bed1fbd02",
|
||||
"miner": "0x877bd459c9b7d8576b44e59e09d076c25946f443",
|
||||
"difficulty": "1813945",
|
||||
"totalDifficulty": "469107641961",
|
||||
"extraData": "0xd883010906846765746888676f312e31332e35856c696e7578",
|
||||
"gasLimit": "6321166",
|
||||
"timestamp": "1577471202",
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@
|
|||
"nonce": "0x28c446f1cb9748c1",
|
||||
"number": "2290743",
|
||||
"stateRoot": "0x4898aceede76739daef76448a367d10015a2c022c9e7909b99a10fbf6fb16708",
|
||||
"timestamp": "1513616414",
|
||||
"totalDifficulty": "7146523769022564"
|
||||
"timestamp": "1513616414"
|
||||
},
|
||||
"input": "0xf8aa0e8509502f9000830493e0941d3ddf7caf024f253487e18bc4a15b1a360c170a80b8443b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e829a0524564944fa419f5c189b5074044f89210c6d6b2d77ee8f7f12a927d59b636dfa0015b28986807a424b18b186ee6642d76739df36cad802d20e8c00e79a61d7281",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "24973",
|
||||
"stateRoot": "0x532a5c3f75453a696428db078e32ae283c85cb97e4d8560dbdf022adac6df369",
|
||||
"timestamp": "1479891145",
|
||||
"totalDifficulty": "1892250259406",
|
||||
"alloc": {
|
||||
"0x6c06b16512b332e6cd8293a2974872674716ce18": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
"stateRoot": "0x68418fb5cf4afa9b807dc079e8cdde0e148ac2c8afb378e675465b5bed1fbd02",
|
||||
"miner": "0x877bd459c9b7d8576b44e59e09d076c25946f443",
|
||||
"difficulty": "1813945",
|
||||
"totalDifficulty": "469107641961",
|
||||
"extraData": "0xd883010906846765746888676f312e31332e35856c696e7578",
|
||||
"gasLimit": "6321166",
|
||||
"timestamp": "1577471202",
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x810f923ff4b450a1",
|
||||
"number": "2295103",
|
||||
"stateRoot": "0xff403612573d76dfdaf4fea2429b77dbe9764021ae0e38dc8ac79a3cf551179e",
|
||||
"timestamp": "1513681246",
|
||||
"totalDifficulty": "7162347056825919"
|
||||
"timestamp": "1513681246"
|
||||
},
|
||||
"input": "0xf86d808504e3b292008307dfa69433056b5dcac09a9b4becad0e1dcf92c19bd0af76880e92596fd62900008029a0e5f27bb66431f7081bb7f1f242003056d7f3f35414c352cd3d1848b52716dac2a07d0be78980edb0bd2a0678fc53aa90ea9558ce346b0d947967216918ac74ccea",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "555461",
|
||||
"stateRoot": "0xba8272acd0dfeb5f04376328e8bfc5b276b177697000c204a060f6f7b629ae32",
|
||||
"timestamp": "1577423350",
|
||||
"totalDifficulty": "462222992438",
|
||||
"alloc": {
|
||||
"0xcf5b3467dfa45cdc8e5358a7a1ba4deb02e5faed": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555160",
|
||||
"stateRoot": "0x95067c12148e2362fcd4a89df286ff0b1739ef097a40ca42ae7f698af9a9d913",
|
||||
"timestamp": "1590793999",
|
||||
"totalDifficulty": "2242063623471",
|
||||
"alloc": {
|
||||
"0x8785e369f0ef0a4e5c5a5f929680427dc75273a5": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555169",
|
||||
"stateRoot": "0xc4b9703de3e59ff795baae2c3afa010cf039c37244a7a6af7f3f491a10601348",
|
||||
"timestamp": "1590794111",
|
||||
"totalDifficulty": "2242105342155",
|
||||
"alloc": {
|
||||
"0x5ac5599fc9df172c89ee7ec55ad9104ccbfed40d": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "839246",
|
||||
"stateRoot": "0x4bb3b02ec70b837651233957fb61a6ea3fc6a4244c1f55df7a713c154829ec0a",
|
||||
"timestamp": "1581179375",
|
||||
"totalDifficulty": "1023985623933",
|
||||
"alloc": {
|
||||
"0x76554b33410b6d90b7dc889bfed0451ad195f27e": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
"stateRoot": "0x7f54db248a004ca182fe87fdfa6efda97163908b4f0cc84b36a6d60699d5d1be",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"difficulty": "1",
|
||||
"totalDifficulty": "24766",
|
||||
"extraData": "0xf09f928e20407072796c616273206e6f64652d3020f09f928e000000000000001d32ac3baf238e163e18ed6d77b67b0b54b08ad9781dc4ffd93c5ede1ca12c5f21b36ac39c7ebb88dff65da91f5b9461f19873a02602230b931ba388a809119f00",
|
||||
"gasLimit": "8000000",
|
||||
"timestamp": "1549153003",
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@
|
|||
"nonce": "0xd1bdb150f6fd170e",
|
||||
"number": "2294630",
|
||||
"stateRoot": "0x1ab1a534e84cc787cda1db21e0d5920ab06017948075b759166cfea7274657a1",
|
||||
"timestamp": "1513675347",
|
||||
"totalDifficulty": "7160543502214733"
|
||||
"timestamp": "1513675347"
|
||||
},
|
||||
"input": "0xf8ab820109855d21dba00082ca1d9443064693d3d38ad6a7cb579e0d6d9718c8aa6b6280b844a9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f90001ba0ce3ad83f5530136467b7c2bb225f406bd170f4ad59c254e5103c34eeabb5bd69a0455154527224a42ab405cacf0fe92918a75641ce4152f8db292019a5527aa956",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@
|
|||
"nonce": "0xd1bdb150f6fd170e",
|
||||
"number": "2294630",
|
||||
"stateRoot": "0x1ab1a534e84cc787cda1db21e0d5920ab06017948075b759166cfea7274657a1",
|
||||
"timestamp": "1513675347",
|
||||
"totalDifficulty": "7160543502214733"
|
||||
"timestamp": "1513675347"
|
||||
},
|
||||
"tracerConfig": {
|
||||
"convertParityErrors": true
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "553415",
|
||||
"stateRoot": "0x8f89e79109c19fa00e72b400502448540dc4773ad92dddd341dbba20c710a3b5",
|
||||
"timestamp": "1577396195",
|
||||
"totalDifficulty": "458361299240",
|
||||
"alloc": {
|
||||
"0x531f76bad925f6a925474996c7d738c1008045f6": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@
|
|||
"nonce": "0xbc5d43adc2c30c7d",
|
||||
"number": "2294500",
|
||||
"stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
|
||||
"timestamp": "1513673552",
|
||||
"totalDifficulty": "7160066586979149"
|
||||
"timestamp": "1513673552"
|
||||
},
|
||||
"input": "0xf9018b0a8505d21dba00832dc6c094abbcd5b340c80b5f1c0545c04c987b87310296ae80b9012473b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988000000000000000000000000000000000000000000000000000000000000000000000000000000001ba0fd659d76a4edbd2a823e324c93f78ad6803b30ff4a9c8bce71ba82798975c70ca06571eecc0b765688ec6c78942c5ee8b585e00988c0141b518287e9be919bc48a",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf888068449504f80832dc6c094f58833cf0c791881b494eb79d461e08a1f043f5280a45c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf12da0264664db3e71fae1dbdaf2f53954be149ad3b7ba8a5054b4d89c70febfacc8b1a0212e8398757963f419681839ae8c5a54b411e252473c82d93dda68405ca63294",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555145",
|
||||
"stateRoot": "0x565873b05f71b98595133e37a52d79c3476ce820c05ebedaddd35541b0e894a3",
|
||||
"timestamp": "1590793819",
|
||||
"totalDifficulty": "2241994078605",
|
||||
"alloc": {
|
||||
"0x119f569a45e9d0089d51d7f9529f5ea9bf5785e2": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"tracerConfig": {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "1555284",
|
||||
"stateRoot": "0x9fe125b361b72d5479b24ad9be9964b74228c73a2dfb0065060a79b4a6dfaa1e",
|
||||
"timestamp": "1590795374",
|
||||
"totalDifficulty": "2242642335405",
|
||||
"alloc": {
|
||||
"0xe85df1413eebe1b191c26260e19783a274a6b041": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "567687",
|
||||
"stateRoot": "0x7b4b193fe73ef87101c7c325954681861cc240c299d03459784b2b11c9c522ae",
|
||||
"timestamp": "1577578008",
|
||||
"totalDifficulty": "485254950048",
|
||||
"alloc": {
|
||||
"0x8521f13dd5e4bc3dab3cf0f01a195a5af899e851": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
"stateRoot": "0x40b5f53d610108947688a04fb68838ff9c0aa0dd6e54156b682537192171ff5c",
|
||||
"miner": "0x774c398d763161f55b66a646f17edda4addad2ca",
|
||||
"difficulty": "1928226",
|
||||
"totalDifficulty": "457857582215",
|
||||
"extraData": "0xd983010907846765746888676f312e31332e358664617277696e",
|
||||
"gasLimit": "7999473",
|
||||
"timestamp": "1577392669",
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@
|
|||
"nonce": "0x70849d5838dee2e9",
|
||||
"number": "25008",
|
||||
"stateRoot": "0x1e01d2161794768c5b917069e73d86e8dca80cd7f3168c0597de420ab93a3b7b",
|
||||
"timestamp": "1479891641",
|
||||
"totalDifficulty": "1896347038589"
|
||||
"timestamp": "1479891641"
|
||||
},
|
||||
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
||||
"result": [
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@
|
|||
"nonce": "0x93363bbd2c95f410",
|
||||
"number": "2294701",
|
||||
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||
"timestamp": "1513676127",
|
||||
"totalDifficulty": "7160808139332585"
|
||||
"timestamp": "1513676127"
|
||||
},
|
||||
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -104,8 +104,7 @@
|
|||
"nonce": "0x38dee147326a8d40",
|
||||
"number": "25000",
|
||||
"stateRoot": "0xc5d6bbcd46236fcdcc80b332ffaaa5476b980b01608f9708408cfef01b58bd5b",
|
||||
"timestamp": "1479891517",
|
||||
"totalDifficulty": "1895410389427"
|
||||
"timestamp": "1479891517"
|
||||
},
|
||||
"input": "0xf88b8206628504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb80000000000000000000000000000000000000000000000280faf689c35ac00002aa0a7ee5b7877811bf671d121b40569462e722657044808dc1d6c4f1e4233ec145ba0417e7543d52b65738d9df419cbe40a708424f4d54b0fc145c0a64545a2bb1065",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@
|
|||
"nonce": "0x684129f283aaef18",
|
||||
"number": "11494",
|
||||
"stateRoot": "0x7057f31fe3dab1d620771adad35224aae43eb70e94861208bc84c557ff5b9d10",
|
||||
"timestamp": "1479735912",
|
||||
"totalDifficulty": "90744064339"
|
||||
"timestamp": "1479735912"
|
||||
},
|
||||
"input": "0xf889448504a817c800832dc6c094269296dddce321a6bcbaa2f0181127593d732cba80a47065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e29a080ed81e4c5e9971a730efab4885566e2c868cd80bd4166d0ed8c287fdf181650a069d7c49215e3d4416ad239cd09dbb71b9f04c16b33b385d14f40b618a7a65115",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@
|
|||
"nonce": "0x28c446f1cb9748c1",
|
||||
"number": "2290743",
|
||||
"stateRoot": "0x4898aceede76739daef76448a367d10015a2c022c9e7909b99a10fbf6fb16708",
|
||||
"timestamp": "1513616414",
|
||||
"totalDifficulty": "7146523769022564"
|
||||
"timestamp": "1513616414"
|
||||
},
|
||||
"input": "0xf8aa0e8509502f9000830493e0941d3ddf7caf024f253487e18bc4a15b1a360c170a80b8443b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e829a0524564944fa419f5c189b5074044f89210c6d6b2d77ee8f7f12a927d59b636dfa0015b28986807a424b18b186ee6642d76739df36cad802d20e8c00e79a61d7281",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "24973",
|
||||
"stateRoot": "0x532a5c3f75453a696428db078e32ae283c85cb97e4d8560dbdf022adac6df369",
|
||||
"timestamp": "1479891145",
|
||||
"totalDifficulty": "1892250259406",
|
||||
"alloc": {
|
||||
"0x6c06b16512b332e6cd8293a2974872674716ce18": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x810f923ff4b450a1",
|
||||
"number": "2295103",
|
||||
"stateRoot": "0xff403612573d76dfdaf4fea2429b77dbe9764021ae0e38dc8ac79a3cf551179e",
|
||||
"timestamp": "1513681246",
|
||||
"totalDifficulty": "7162347056825919"
|
||||
"timestamp": "1513681246"
|
||||
},
|
||||
"input": "0xf86d808504e3b292008307dfa69433056b5dcac09a9b4becad0e1dcf92c19bd0af76880e92596fd62900008029a0e5f27bb66431f7081bb7f1f242003056d7f3f35414c352cd3d1848b52716dac2a07d0be78980edb0bd2a0678fc53aa90ea9558ce346b0d947967216918ac74ccea",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@
|
|||
"nonce": "0xd1bdb150f6fd170e",
|
||||
"number": "2294630",
|
||||
"stateRoot": "0x1ab1a534e84cc787cda1db21e0d5920ab06017948075b759166cfea7274657a1",
|
||||
"timestamp": "1513675347",
|
||||
"totalDifficulty": "7160543502214733"
|
||||
"timestamp": "1513675347"
|
||||
},
|
||||
"input": "0xf8ab820109855d21dba00082ca1d9443064693d3d38ad6a7cb579e0d6d9718c8aa6b6280b844a9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f90001ba0ce3ad83f5530136467b7c2bb225f406bd170f4ad59c254e5103c34eeabb5bd69a0455154527224a42ab405cacf0fe92918a75641ce4152f8db292019a5527aa956",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@
|
|||
"nonce": "0xbc5d43adc2c30c7d",
|
||||
"number": "2294500",
|
||||
"stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
|
||||
"timestamp": "1513673552",
|
||||
"totalDifficulty": "7160066586979149"
|
||||
"timestamp": "1513673552"
|
||||
},
|
||||
"input": "0xf9018b0a8505d21dba00832dc6c094abbcd5b340c80b5f1c0545c04c987b87310296ae80b9012473b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988000000000000000000000000000000000000000000000000000000000000000000000000000000001ba0fd659d76a4edbd2a823e324c93f78ad6803b30ff4a9c8bce71ba82798975c70ca06571eecc0b765688ec6c78942c5ee8b585e00988c0141b518287e9be919bc48a",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf888068449504f80832dc6c094f58833cf0c791881b494eb79d461e08a1f043f5280a45c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf12da0264664db3e71fae1dbdaf2f53954be149ad3b7ba8a5054b4d89c70febfacc8b1a0212e8398757963f419681839ae8c5a54b411e252473c82d93dda68405ca63294",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@
|
|||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
"timestamp": "1513601261"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@
|
|||
"nonce": "0x70849d5838dee2e9",
|
||||
"number": "25008",
|
||||
"stateRoot": "0x1e01d2161794768c5b917069e73d86e8dca80cd7f3168c0597de420ab93a3b7b",
|
||||
"timestamp": "1479891641",
|
||||
"totalDifficulty": "1896347038589"
|
||||
"timestamp": "1479891641"
|
||||
},
|
||||
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
||||
"result": {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"number": "995200",
|
||||
"stateRoot": "0x3579328470dd2aef5b9da69f5480cbe0d375e653b530ab3c1aee0da5e1ff4c94",
|
||||
"timestamp": "1455322761",
|
||||
"totalDifficulty": "7077231809278509672",
|
||||
"alloc": {
|
||||
"0x200edd17f30485a8735878661960cd7a9a95733f": {
|
||||
"balance": "0x0",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue