Compare commits
279 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7231b3efb8 | ||
|
|
da1b6f3906 | ||
|
|
f423290ac8 | ||
|
|
312e02bca9 | ||
|
|
0183256e7f | ||
|
|
84d8eb2ca8 | ||
|
|
554b1b9d5f | ||
|
|
b97f57882c | ||
|
|
60d3cc8b77 | ||
|
|
c36f8fefc3 | ||
|
|
433f0919cc | ||
|
|
b8dc1e2705 | ||
|
|
eaa24a8a15 | ||
|
|
c641cff51a | ||
|
|
464885faaa | ||
|
|
bb74230f2a | ||
|
|
f915f6873f | ||
|
|
08e782c61f | ||
|
|
011fe3eb5e | ||
|
|
79b727bc8a | ||
|
|
778ff94794 | ||
|
|
e4f570fcc6 | ||
|
|
f9d683b07f | ||
|
|
633e7ef478 | ||
|
|
3d11a22c99 | ||
|
|
6289137827 | ||
|
|
da3da7c0e7 | ||
|
|
cf8a6d6173 | ||
|
|
088bc34194 | ||
|
|
53b1420ede | ||
|
|
8b6e018401 | ||
|
|
64da037e99 | ||
|
|
8a430fbd1c | ||
|
|
bcbd700367 | ||
|
|
84bccd0900 | ||
|
|
a6a0609b05 | ||
|
|
1bea4b0dfa | ||
|
|
ee120ef865 | ||
|
|
28d30b51f8 | ||
|
|
2fe0c65f4b | ||
|
|
ec2b43c2c3 | ||
|
|
48496e0675 | ||
|
|
edb1937cf7 | ||
|
|
4e599ee469 | ||
|
|
57ff2dee06 | ||
|
|
307156cc46 | ||
|
|
0dbb3b1601 | ||
|
|
5a0e1d88f4 | ||
|
|
12f971fb2d | ||
|
|
01fdca53e1 | ||
|
|
5240725041 | ||
|
|
b522f5e091 | ||
|
|
a47b8cf6f5 | ||
|
|
07a5bc1b0b | ||
|
|
f2491c5ed7 | ||
|
|
06082fe267 | ||
|
|
eae3b1946a | ||
|
|
3a6fe69f23 | ||
|
|
42bc1944a1 | ||
|
|
a541fbea18 | ||
|
|
3531ca2246 | ||
|
|
92c5d104d0 | ||
|
|
783e97ef1f | ||
|
|
ab2caaee11 | ||
|
|
443afc975c | ||
|
|
ac7baeab57 | ||
|
|
12674d493e | ||
|
|
51ececb64e | ||
|
|
57a3fab8a7 | ||
|
|
ca9bce9a45 | ||
|
|
b61ef24cce | ||
|
|
d8211c7ec7 | ||
|
|
b1a5e4afdd | ||
|
|
5b246af54e | ||
|
|
86f3625455 | ||
|
|
9bf495bfc9 | ||
|
|
e28f713ada | ||
|
|
62e3b83af6 | ||
|
|
1b34283810 | ||
|
|
401354976b | ||
|
|
7ada89d4e6 | ||
|
|
84ff152de5 | ||
|
|
b8d7c662cd | ||
|
|
babe9b993e | ||
|
|
578bc8164d | ||
|
|
9ada4a2e2c | ||
|
|
9e17648d8c | ||
|
|
90987db733 | ||
|
|
5c1fc3bf54 | ||
|
|
51ed39c093 | ||
|
|
6ef3a16869 | ||
|
|
794c6133ef | ||
|
|
9a0df80bbc | ||
|
|
ca5bc676d1 | ||
|
|
7957530225 | ||
|
|
de2c44ab5c | ||
|
|
4d88974864 | ||
|
|
067084feda | ||
|
|
d019e90162 | ||
|
|
31be5d41d9 | ||
|
|
f85cf722ff | ||
|
|
3258211f68 | ||
|
|
ffae2043f0 | ||
|
|
62ad17fb00 | ||
|
|
108eec3fee | ||
|
|
d584e39862 | ||
|
|
7c4cad064c | ||
|
|
154b525ce8 | ||
|
|
32c576bd3c | ||
|
|
8a134014b4 | ||
|
|
887902ea4d | ||
|
|
d16214228f | ||
|
|
3784e15106 | ||
|
|
84c51bc5ec | ||
|
|
efee85378e | ||
|
|
45f34430fd | ||
|
|
83ad92c421 | ||
|
|
fe2f153b55 | ||
|
|
a5a5237178 | ||
|
|
a789dcc978 | ||
|
|
b69f5ca7d4 | ||
|
|
0db0b27754 | ||
|
|
d705f5a554 | ||
|
|
5cee33eb72 | ||
|
|
85126c4eb9 | ||
|
|
a0a4a153e9 | ||
|
|
0b40977480 | ||
|
|
5c66bab3b8 | ||
|
|
8e0771c218 | ||
|
|
8dbf261fd9 | ||
|
|
79bb9300c1 | ||
|
|
ea4bc2dbff | ||
|
|
26675454bf | ||
|
|
1d99573192 | ||
|
|
f38abc55f1 | ||
|
|
dfeb2f7e80 | ||
|
|
bb1f7ebf20 | ||
|
|
d02c605367 | ||
|
|
c368f728c1 | ||
|
|
5566e5d152 | ||
|
|
57feabea66 | ||
|
|
16ecdd5839 | ||
|
|
85b9bdd641 | ||
|
|
6902485767 | ||
|
|
fb4007bb22 | ||
|
|
0a68558e7e | ||
|
|
fd604becbb | ||
|
|
5f98020a21 | ||
|
|
a580f7d6c5 | ||
|
|
12f0ff40b1 | ||
|
|
971df49fe2 | ||
|
|
f4ad493870 | ||
|
|
2a451f9eb3 | ||
|
|
278ec7176a | ||
|
|
deff5056fb | ||
|
|
c27bd3481e | ||
|
|
0fbc94eabc | ||
|
|
9097d0a325 | ||
|
|
5d0ab07343 | ||
|
|
9d6480c3cd | ||
|
|
a879c42bd3 | ||
|
|
39fe7eca6b | ||
|
|
66948316f7 | ||
|
|
57d9e0ac75 | ||
|
|
e4b687cf46 | ||
|
|
6d175460df | ||
|
|
520f25688a | ||
|
|
3b38a83274 | ||
|
|
97bd6cd216 | ||
|
|
d60cfd2604 | ||
|
|
9e59474e46 | ||
|
|
8a24b56331 | ||
|
|
0658712f65 | ||
|
|
d3e3a460ec | ||
|
|
28ba686cbf | ||
|
|
f311488d2c | ||
|
|
c38fab912b | ||
|
|
4cd6a1458e | ||
|
|
82c5085399 | ||
|
|
95bbd46eab | ||
|
|
85afdeef37 | ||
|
|
860184d542 | ||
|
|
3526f69047 | ||
|
|
295bc35ecf | ||
|
|
8f11d279d2 | ||
|
|
b157bae2c9 | ||
|
|
64a5e125c5 | ||
|
|
fb8ea5993f | ||
|
|
5c13012b56 | ||
|
|
523866c2cc | ||
|
|
56e9001a1a | ||
|
|
0730acc5a0 | ||
|
|
2faf796d2a | ||
|
|
3aea432b35 | ||
|
|
b20bc5c0ca | ||
|
|
5c89ec9b98 | ||
|
|
bbfa6488ac | ||
|
|
a1f16bc74c | ||
|
|
576681f29b | ||
|
|
370680a7a9 | ||
|
|
97aacd9b35 | ||
|
|
f05419f0fb | ||
|
|
a5e3aa693c | ||
|
|
89fde59a80 | ||
|
|
f0b1bddac4 | ||
|
|
33ca98ece9 | ||
|
|
1fac96c1f9 | ||
|
|
b9e6e43722 | ||
|
|
c49e065fea | ||
|
|
846badc480 | ||
|
|
8fe47b0a0d | ||
|
|
58b0420a8a | ||
|
|
afd4227df8 | ||
|
|
9624f92ede | ||
|
|
dea71556cc | ||
|
|
ff4ff30a68 | ||
|
|
00b922fc5d | ||
|
|
7522642393 | ||
|
|
b9d4412715 | ||
|
|
5441a8fa47 | ||
|
|
e13d14e6a3 | ||
|
|
d21a069619 | ||
|
|
13bc9c0c6e | ||
|
|
5afc82de6e | ||
|
|
bd566977e8 | ||
|
|
99169016d2 | ||
|
|
78c34fdc3c | ||
|
|
d081c935d7 | ||
|
|
bb0191f22b | ||
|
|
c619562313 | ||
|
|
6b6d3190cf | ||
|
|
3b05318525 | ||
|
|
a182c76815 | ||
|
|
6ed812db13 | ||
|
|
3212fb6838 | ||
|
|
f5f906dd0d | ||
|
|
bbbeb7d8ba | ||
|
|
c131e812ae | ||
|
|
686b2884ee | ||
|
|
e7c8693635 | ||
|
|
ec88bd0cd0 | ||
|
|
acdf9238fb | ||
|
|
d3f018fde8 | ||
|
|
4fcc93d922 | ||
|
|
61f4b5aa89 | ||
|
|
35dbf7a8a3 | ||
|
|
1b5582acf7 | ||
|
|
dde6f1e92d | ||
|
|
2d4eff21ca | ||
|
|
bca8c03e57 | ||
|
|
c07918e7d8 | ||
|
|
0e6961366a | ||
|
|
948a600ed5 | ||
|
|
9e23610b0f | ||
|
|
29905d86ae | ||
|
|
10eb654f27 | ||
|
|
4dde0665c8 | ||
|
|
a750bf8686 | ||
|
|
bef78efb49 | ||
|
|
ddf10250c7 | ||
|
|
fcd7bdc2b7 | ||
|
|
1e44c3585f | ||
|
|
5228b2a353 | ||
|
|
e0123026b6 | ||
|
|
653a30f4ca | ||
|
|
0f2347d070 | ||
|
|
da000c8314 | ||
|
|
f915a4bf20 | ||
|
|
732a6a3666 | ||
|
|
7b6c8363da | ||
|
|
4695117f2e | ||
|
|
e9f99d1c91 | ||
|
|
ef946a6c87 | ||
|
|
58aeab77d2 | ||
|
|
97ce6dfa6d | ||
|
|
bbb2b30506 | ||
|
|
15fe3050a1 | ||
|
|
c63c2d855e | ||
|
|
87a11a87c2 |
1
.gitmodules
vendored
1
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
|||||||
[submodule "tests"]
|
[submodule "tests"]
|
||||||
path = tests/testdata
|
path = tests/testdata
|
||||||
url = https://github.com/ethereum/tests
|
url = https://github.com/ethereum/tests
|
||||||
|
shallow = true
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# This file configures github.com/golangci/golangci-lint.
|
# This file configures github.com/golangci/golangci-lint.
|
||||||
|
|
||||||
run:
|
run:
|
||||||
timeout: 3m
|
timeout: 5m
|
||||||
tests: true
|
tests: true
|
||||||
# default is true. Enables skipping of directories:
|
# default is true. Enables skipping of directories:
|
||||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||||
|
|||||||
59
.travis.yml
59
.travis.yml
@@ -5,7 +5,7 @@ jobs:
|
|||||||
allow_failures:
|
allow_failures:
|
||||||
- stage: build
|
- stage: build
|
||||||
os: osx
|
os: osx
|
||||||
go: 1.15.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
- azure-ios
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- stage: lint
|
- stage: lint
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- lint
|
- lint
|
||||||
git:
|
git:
|
||||||
@@ -24,27 +24,48 @@ jobs:
|
|||||||
script:
|
script:
|
||||||
- go run build/ci.go lint
|
- go run build/ci.go lint
|
||||||
|
|
||||||
# This builder does the Docker Hub build and upload for amd64
|
# These builders create the Docker sub-images for multi-arch push and each
|
||||||
|
# will attempt to push the multi-arch image if they are the last builder
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
|
arch: amd64
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- docker
|
- docker
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
before_install:
|
||||||
|
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go docker -upload karalabe/geth-docker-test
|
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
||||||
|
|
||||||
|
- stage: build
|
||||||
|
if: type = push
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
dist: bionic
|
||||||
|
go: 1.17.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
|
||||||
|
|
||||||
# This builder does the Ubuntu PPA upload
|
# This builder does the Ubuntu PPA upload
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- ubuntu-ppa
|
- ubuntu-ppa
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
@@ -69,7 +90,7 @@ jobs:
|
|||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- azure-linux
|
- azure-linux
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
@@ -106,7 +127,7 @@ jobs:
|
|||||||
dist: bionic
|
dist: bionic
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- azure-linux-mips
|
- azure-linux-mips
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
@@ -171,7 +192,7 @@ jobs:
|
|||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: osx
|
os: osx
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
- azure-ios
|
||||||
@@ -203,7 +224,7 @@ jobs:
|
|||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
@@ -214,7 +235,7 @@ jobs:
|
|||||||
os: linux
|
os: linux
|
||||||
arch: arm64
|
arch: arm64
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
@@ -223,7 +244,7 @@ jobs:
|
|||||||
- stage: build
|
- stage: build
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.15.x
|
go: 1.16.x
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
@@ -234,7 +255,7 @@ jobs:
|
|||||||
if: type = cron
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.16.x
|
go: 1.17.x
|
||||||
env:
|
env:
|
||||||
- azure-purge
|
- azure-purge
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
@@ -242,3 +263,15 @@ jobs:
|
|||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go purge -store gethstore/builds -days 14
|
- go run build/ci.go purge -store gethstore/builds -days 14
|
||||||
|
|
||||||
|
# This builder executes race tests
|
||||||
|
- stage: build
|
||||||
|
if: type = cron
|
||||||
|
os: linux
|
||||||
|
dist: bionic
|
||||||
|
go: 1.17.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
script:
|
||||||
|
- go run build/ci.go test -race -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
|
|||||||
20
Dockerfile
20
Dockerfile
@@ -1,10 +1,15 @@
|
|||||||
# Build Geth in a stock Go builder container
|
# Support setting various labels on the final image
|
||||||
FROM golang:1.16-alpine as builder
|
ARG COMMIT=""
|
||||||
|
ARG VERSION=""
|
||||||
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
# Build Geth in a stock Go builder container
|
||||||
|
FROM golang:1.17-alpine as builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||||
|
|
||||||
ADD . /go-ethereum
|
ADD . /go-ethereum
|
||||||
RUN cd /go-ethereum && make geth
|
RUN cd /go-ethereum && go run build/ci.go install ./cmd/geth
|
||||||
|
|
||||||
# Pull Geth into a second stage deploy alpine container
|
# Pull Geth into a second stage deploy alpine container
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
@@ -14,3 +19,10 @@ COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
|
|||||||
|
|
||||||
EXPOSE 8545 8546 30303 30303/udp
|
EXPOSE 8545 8546 30303 30303/udp
|
||||||
ENTRYPOINT ["geth"]
|
ENTRYPOINT ["geth"]
|
||||||
|
|
||||||
|
# Add some metadata labels to help programatic image consumption
|
||||||
|
ARG COMMIT=""
|
||||||
|
ARG VERSION=""
|
||||||
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
|
LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
# Build Geth in a stock Go builder container
|
# Support setting various labels on the final image
|
||||||
FROM golang:1.16-alpine as builder
|
ARG COMMIT=""
|
||||||
|
ARG VERSION=""
|
||||||
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
# Build Geth in a stock Go builder container
|
||||||
|
FROM golang:1.17-alpine as builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||||
|
|
||||||
ADD . /go-ethereum
|
ADD . /go-ethereum
|
||||||
RUN cd /go-ethereum && make all
|
RUN cd /go-ethereum && go run build/ci.go install
|
||||||
|
|
||||||
# Pull all binaries into a second stage deploy alpine container
|
# Pull all binaries into a second stage deploy alpine container
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
@@ -13,3 +18,10 @@ RUN apk add --no-cache ca-certificates
|
|||||||
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
||||||
|
|
||||||
EXPOSE 8545 8546 30303 30303/udp
|
EXPOSE 8545 8546 30303 30303/udp
|
||||||
|
|
||||||
|
# Add some metadata labels to help programatic image consumption
|
||||||
|
ARG COMMIT=""
|
||||||
|
ARG VERSION=""
|
||||||
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
|
LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ $ geth console
|
|||||||
```
|
```
|
||||||
|
|
||||||
This command will:
|
This command will:
|
||||||
* Start `geth` in fast sync mode (default, can be changed with the `--syncmode` flag),
|
* Start `geth` in snap sync mode (default, can be changed with the `--syncmode` flag),
|
||||||
causing it to download more data in exchange for avoiding processing the entire history
|
causing it to download more data in exchange for avoiding processing the entire history
|
||||||
of the Ethereum network, which is very CPU intensive.
|
of the Ethereum network, which is very CPU intensive.
|
||||||
* Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console),
|
* Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console),
|
||||||
(via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://web3js.readthedocs.io/en/)
|
(via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://web3js.readthedocs.io/)
|
||||||
(note: the `web3` version bundled within `geth` is very old, and not up to date with official docs),
|
(note: the `web3` version bundled within `geth` is very old, and not up to date with official docs),
|
||||||
as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server).
|
as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server).
|
||||||
This tool is optional and if you leave it out you can always attach to an already running
|
This tool is optional and if you leave it out you can always attach to an already running
|
||||||
@@ -231,7 +231,8 @@ aware of and agree upon. This consists of a small JSON file (e.g. call it `genes
|
|||||||
"constantinopleBlock": 0,
|
"constantinopleBlock": 0,
|
||||||
"petersburgBlock": 0,
|
"petersburgBlock": 0,
|
||||||
"istanbulBlock": 0,
|
"istanbulBlock": 0,
|
||||||
"berlinBlock": 0
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0
|
||||||
},
|
},
|
||||||
"alloc": {},
|
"alloc": {},
|
||||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
@@ -332,7 +333,7 @@ from anyone on the internet, and are grateful for even the smallest of fixes!
|
|||||||
|
|
||||||
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request
|
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request
|
||||||
for the maintainers to review and merge into the main code base. If you wish to submit
|
for the maintainers to review and merge into the main code base. If you wish to submit
|
||||||
more complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum)
|
more complex changes though, please check up with the core devs first on [our Discord Server](https://discord.gg/invite/nthXNEv)
|
||||||
to ensure those changes are in line with the general philosophy of the project and/or get
|
to ensure those changes are in line with the general philosophy of the project and/or get
|
||||||
some early feedback which can make both your efforts much lighter as well as our review
|
some early feedback which can make both your efforts much lighter as well as our review
|
||||||
and merge procedures quick and simple.
|
and merge procedures quick and simple.
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ Audit reports are published in the `docs` folder: https://github.com/ethereum/go
|
|||||||
| ------- | ------- | ----------- |
|
| ------- | ------- | ----------- |
|
||||||
| `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) |
|
| `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) |
|
||||||
| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) |
|
| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) |
|
||||||
|
| `Discv5` | 20191015 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2019-10-15_Discv5_audit_LeastAuthority.pdf) |
|
||||||
|
| `Discv5` | 20200124 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2020-01-24_DiscV5_audit_Cure53.pdf) |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type ABI struct {
|
|||||||
Constructor Method
|
Constructor Method
|
||||||
Methods map[string]Method
|
Methods map[string]Method
|
||||||
Events map[string]Event
|
Events map[string]Event
|
||||||
|
Errors map[string]Error
|
||||||
|
|
||||||
// Additional "special" functions introduced in solidity v0.6.0.
|
// Additional "special" functions introduced in solidity v0.6.0.
|
||||||
// It's separated from the original default fallback. Each contract
|
// It's separated from the original default fallback. Each contract
|
||||||
@@ -157,12 +158,13 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
abi.Methods = make(map[string]Method)
|
abi.Methods = make(map[string]Method)
|
||||||
abi.Events = make(map[string]Event)
|
abi.Events = make(map[string]Event)
|
||||||
|
abi.Errors = make(map[string]Error)
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||||
case "function":
|
case "function":
|
||||||
name := abi.overloadedMethodName(field.Name)
|
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
||||||
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
||||||
case "fallback":
|
case "fallback":
|
||||||
// New introduced function type in v0.6.0, check more detail
|
// New introduced function type in v0.6.0, check more detail
|
||||||
@@ -182,8 +184,10 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "event":
|
case "event":
|
||||||
name := abi.overloadedEventName(field.Name)
|
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
||||||
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||||
|
case "error":
|
||||||
|
abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||||
}
|
}
|
||||||
@@ -191,36 +195,6 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// overloadedMethodName returns the next available name for a given function.
|
|
||||||
// Needed since solidity allows for function overload.
|
|
||||||
//
|
|
||||||
// e.g. if the abi contains Methods send, send1
|
|
||||||
// overloadedMethodName would return send2 for input send.
|
|
||||||
func (abi *ABI) overloadedMethodName(rawName string) string {
|
|
||||||
name := rawName
|
|
||||||
_, ok := abi.Methods[name]
|
|
||||||
for idx := 0; ok; idx++ {
|
|
||||||
name = fmt.Sprintf("%s%d", rawName, idx)
|
|
||||||
_, ok = abi.Methods[name]
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
// overloadedEventName returns the next available name for a given event.
|
|
||||||
// Needed since solidity allows for event overload.
|
|
||||||
//
|
|
||||||
// e.g. if the abi contains events received, received1
|
|
||||||
// overloadedEventName would return received2 for input received.
|
|
||||||
func (abi *ABI) overloadedEventName(rawName string) string {
|
|
||||||
name := rawName
|
|
||||||
_, ok := abi.Events[name]
|
|
||||||
for idx := 0; ok; idx++ {
|
|
||||||
name = fmt.Sprintf("%s%d", rawName, idx)
|
|
||||||
_, ok = abi.Events[name]
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
// MethodById looks up a method by the 4-byte id,
|
// MethodById looks up a method by the 4-byte id,
|
||||||
// returns nil if none found.
|
// returns nil if none found.
|
||||||
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
@@ -277,3 +251,20 @@ func UnpackRevert(data []byte) (string, error) {
|
|||||||
}
|
}
|
||||||
return unpacked[0].(string), nil
|
return unpacked[0].(string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overloadedName returns the next available name for a given thing.
|
||||||
|
// Needed since solidity allows for overloading.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains Methods send, send1
|
||||||
|
// overloadedName would return send2 for input send.
|
||||||
|
//
|
||||||
|
// overloadedName works for methods, events and errors.
|
||||||
|
func overloadedName(rawName string, isAvail func(string) bool) string {
|
||||||
|
name := rawName
|
||||||
|
ok := isAvail(name)
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
ok = isAvail(name)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|||||||
@@ -295,6 +295,20 @@ func TestOverloadedMethodSignature(t *testing.T) {
|
|||||||
check("bar0", "bar(uint256,uint256)", false)
|
check("bar0", "bar(uint256,uint256)", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomErrors(t *testing.T) {
|
||||||
|
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
|
||||||
|
abi, err := JSON(strings.NewReader(json))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
check := func(name string, expect string) {
|
||||||
|
if abi.Errors[name].Sig != expect {
|
||||||
|
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check("MyError", "MyError(uint256)")
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultiPack(t *testing.T) {
|
func TestMultiPack(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
|
|||||||
dst := reflect.ValueOf(v).Elem()
|
dst := reflect.ValueOf(v).Elem()
|
||||||
src := reflect.ValueOf(marshalledValues)
|
src := reflect.ValueOf(marshalledValues)
|
||||||
|
|
||||||
if dst.Kind() == reflect.Struct && src.Kind() != reflect.Struct {
|
if dst.Kind() == reflect.Struct {
|
||||||
return set(dst.Field(0), src)
|
return set(dst.Field(0), src)
|
||||||
}
|
}
|
||||||
return set(dst, src)
|
return set(dst, src)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
@@ -74,6 +75,7 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account
|
|||||||
}
|
}
|
||||||
return tx.WithSignature(signer, signature)
|
return tx.WithSignature(signer, signature)
|
||||||
},
|
},
|
||||||
|
Context: context.Background(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +99,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
|||||||
}
|
}
|
||||||
return tx.WithSignature(signer, signature)
|
return tx.WithSignature(signer, signature)
|
||||||
},
|
},
|
||||||
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +136,7 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
|
|||||||
}
|
}
|
||||||
return tx.WithSignature(signer, signature)
|
return tx.WithSignature(signer, signature)
|
||||||
},
|
},
|
||||||
|
Context: context.Background(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +160,7 @@ func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*Tr
|
|||||||
}
|
}
|
||||||
return tx.WithSignature(signer, signature)
|
return tx.WithSignature(signer, signature)
|
||||||
},
|
},
|
||||||
|
Context: context.Background(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,5 +175,6 @@ func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account)
|
|||||||
}
|
}
|
||||||
return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
|
return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
|
||||||
},
|
},
|
||||||
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -488,8 +488,19 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
} else {
|
} else {
|
||||||
hi = b.pendingBlock.GasLimit()
|
hi = b.pendingBlock.GasLimit()
|
||||||
}
|
}
|
||||||
|
// Normalize the max fee per gas the call is willing to spend.
|
||||||
|
var feeCap *big.Int
|
||||||
|
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||||
|
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||||
|
} else if call.GasPrice != nil {
|
||||||
|
feeCap = call.GasPrice
|
||||||
|
} else if call.GasFeeCap != nil {
|
||||||
|
feeCap = call.GasFeeCap
|
||||||
|
} else {
|
||||||
|
feeCap = common.Big0
|
||||||
|
}
|
||||||
// Recap the highest gas allowance with account's balance.
|
// Recap the highest gas allowance with account's balance.
|
||||||
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
|
if feeCap.BitLen() != 0 {
|
||||||
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
||||||
available := new(big.Int).Set(balance)
|
available := new(big.Int).Set(balance)
|
||||||
if call.Value != nil {
|
if call.Value != nil {
|
||||||
@@ -498,14 +509,14 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
}
|
}
|
||||||
available.Sub(available, call.Value)
|
available.Sub(available, call.Value)
|
||||||
}
|
}
|
||||||
allowance := new(big.Int).Div(available, call.GasPrice)
|
allowance := new(big.Int).Div(available, feeCap)
|
||||||
if allowance.IsUint64() && hi > allowance.Uint64() {
|
if allowance.IsUint64() && hi > allowance.Uint64() {
|
||||||
transfer := call.Value
|
transfer := call.Value
|
||||||
if transfer == nil {
|
if transfer == nil {
|
||||||
transfer = new(big.Int)
|
transfer = new(big.Int)
|
||||||
}
|
}
|
||||||
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
||||||
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
|
"sent", transfer, "feecap", feeCap, "fundable", allowance)
|
||||||
hi = allowance.Uint64()
|
hi = allowance.Uint64()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -784,7 +795,7 @@ type callMsg struct {
|
|||||||
|
|
||||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
||||||
func (m callMsg) Nonce() uint64 { return 0 }
|
func (m callMsg) Nonce() uint64 { return 0 }
|
||||||
func (m callMsg) CheckNonce() bool { return false }
|
func (m callMsg) IsFake() bool { return true }
|
||||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
||||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||||
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
||||||
|
|||||||
@@ -580,6 +580,26 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
|||||||
Value: big.NewInt(100000000000),
|
Value: big.NewInt(100000000000),
|
||||||
Data: nil,
|
Data: nil,
|
||||||
}, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14)
|
}, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14)
|
||||||
|
|
||||||
|
{"EstimateEIP1559WithHighFees", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &addr,
|
||||||
|
Gas: 0,
|
||||||
|
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
|
||||||
|
GasTipCap: big.NewInt(1),
|
||||||
|
Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
|
||||||
|
Data: nil,
|
||||||
|
}, params.TxGas, nil},
|
||||||
|
|
||||||
|
{"EstimateEIP1559WithSuperHighFees", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &addr,
|
||||||
|
Gas: 0,
|
||||||
|
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
|
||||||
|
GasTipCap: big.NewInt(1),
|
||||||
|
Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether
|
||||||
|
Data: nil,
|
||||||
|
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
|
||||||
}
|
}
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
@@ -592,6 +612,9 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if c.expectError == nil && err != nil {
|
||||||
|
t.Fatalf("test %d: didn't expect error, got %v", i, err)
|
||||||
|
}
|
||||||
if got != c.expect {
|
if got != c.expect {
|
||||||
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
|
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
@@ -76,6 +78,29 @@ type WatchOpts struct {
|
|||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MetaData collects all metadata for a bound contract.
|
||||||
|
type MetaData struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
Sigs map[string]string
|
||||||
|
Bin string
|
||||||
|
ABI string
|
||||||
|
ab *abi.ABI
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MetaData) GetAbi() (*abi.ABI, error) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
if m.ab != nil {
|
||||||
|
return m.ab, nil
|
||||||
|
}
|
||||||
|
if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
m.ab = &parsed
|
||||||
|
}
|
||||||
|
return m.ab, nil
|
||||||
|
}
|
||||||
|
|
||||||
// BoundContract is the base wrapper object that reflects a contract on the
|
// BoundContract is the base wrapper object that reflects a contract on the
|
||||||
// Ethereum network. It contains a collection of methods that are used by the
|
// Ethereum network. It contains a collection of methods that are used by the
|
||||||
// higher level contract bindings to operate.
|
// higher level contract bindings to operate.
|
||||||
@@ -206,111 +231,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
|
|||||||
return c.transact(opts, &c.address, nil)
|
return c.transact(opts, &c.address, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// transact executes an actual transaction invocation, first deriving any missing
|
func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
|
||||||
// authorization fields, and then scheduling the transaction for execution.
|
// Normalize value
|
||||||
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Ensure a valid value field and resolve the account nonce
|
|
||||||
value := opts.Value
|
value := opts.Value
|
||||||
if value == nil {
|
if value == nil {
|
||||||
value = new(big.Int)
|
value = new(big.Int)
|
||||||
}
|
}
|
||||||
var nonce uint64
|
// Estimate TipCap
|
||||||
if opts.Nonce == nil {
|
gasTipCap := opts.GasTipCap
|
||||||
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
if gasTipCap == nil {
|
||||||
|
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
gasTipCap = tip
|
||||||
nonce = opts.Nonce.Uint64()
|
|
||||||
}
|
}
|
||||||
// Figure out reasonable gas price values
|
// Estimate FeeCap
|
||||||
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
|
gasFeeCap := opts.GasFeeCap
|
||||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
if gasFeeCap == nil {
|
||||||
|
gasFeeCap = new(big.Int).Add(
|
||||||
|
gasTipCap,
|
||||||
|
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
head, err := c.transactor.HeaderByNumber(opts.Context, nil)
|
if gasFeeCap.Cmp(gasTipCap) < 0 {
|
||||||
|
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
|
||||||
|
}
|
||||||
|
// Estimate GasLimit
|
||||||
|
gasLimit := opts.GasLimit
|
||||||
|
if opts.GasLimit == 0 {
|
||||||
|
var err error
|
||||||
|
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create the transaction
|
||||||
|
nonce, err := c.getNonce(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if head.BaseFee != nil && opts.GasPrice == nil {
|
baseTx := &types.DynamicFeeTx{
|
||||||
if opts.GasTipCap == nil {
|
To: contract,
|
||||||
tip, err := c.transactor.SuggestGasTipCap(opts.Context)
|
Nonce: nonce,
|
||||||
if err != nil {
|
GasFeeCap: gasFeeCap,
|
||||||
return nil, err
|
GasTipCap: gasTipCap,
|
||||||
}
|
Gas: gasLimit,
|
||||||
opts.GasTipCap = tip
|
Value: value,
|
||||||
}
|
Data: input,
|
||||||
if opts.GasFeeCap == nil {
|
|
||||||
gasFeeCap := new(big.Int).Add(
|
|
||||||
opts.GasTipCap,
|
|
||||||
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
|
|
||||||
)
|
|
||||||
opts.GasFeeCap = gasFeeCap
|
|
||||||
}
|
|
||||||
if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 {
|
|
||||||
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
|
||||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
|
||||||
}
|
|
||||||
if opts.GasPrice == nil {
|
|
||||||
price, err := c.transactor.SuggestGasTipCap(opts.Context)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if head.BaseFee != nil {
|
|
||||||
price.Add(price, head.BaseFee)
|
|
||||||
}
|
|
||||||
opts.GasPrice = price
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
gasLimit := opts.GasLimit
|
return types.NewTx(baseTx), nil
|
||||||
if gasLimit == 0 {
|
}
|
||||||
// Gas estimation cannot succeed without code for method invocations
|
|
||||||
if contract != nil {
|
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||||
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
||||||
return nil, err
|
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
||||||
} else if len(code) == 0 {
|
}
|
||||||
return nil, ErrNoCode
|
// Normalize value
|
||||||
}
|
value := opts.Value
|
||||||
}
|
if value == nil {
|
||||||
// If the contract surely has code (or code is not needed), estimate the transaction
|
value = new(big.Int)
|
||||||
msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input}
|
}
|
||||||
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
// Estimate GasPrice
|
||||||
|
gasPrice := opts.GasPrice
|
||||||
|
if gasPrice == nil {
|
||||||
|
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
|
return nil, err
|
||||||
|
}
|
||||||
|
gasPrice = price
|
||||||
|
}
|
||||||
|
// Estimate GasLimit
|
||||||
|
gasLimit := opts.GasLimit
|
||||||
|
if opts.GasLimit == 0 {
|
||||||
|
var err error
|
||||||
|
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create the transaction, sign it and schedule it for execution
|
// create the transaction
|
||||||
var rawTx *types.Transaction
|
nonce, err := c.getNonce(opts)
|
||||||
if opts.GasFeeCap == nil {
|
if err != nil {
|
||||||
baseTx := &types.LegacyTx{
|
return nil, err
|
||||||
Nonce: nonce,
|
}
|
||||||
GasPrice: opts.GasPrice,
|
baseTx := &types.LegacyTx{
|
||||||
Gas: gasLimit,
|
To: contract,
|
||||||
Value: value,
|
Nonce: nonce,
|
||||||
Data: input,
|
GasPrice: gasPrice,
|
||||||
|
Gas: gasLimit,
|
||||||
|
Value: value,
|
||||||
|
Data: input,
|
||||||
|
}
|
||||||
|
return types.NewTx(baseTx), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
|
||||||
|
if contract != nil {
|
||||||
|
// Gas estimation cannot succeed without code for method invocations.
|
||||||
|
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if len(code) == 0 {
|
||||||
|
return 0, ErrNoCode
|
||||||
}
|
}
|
||||||
if contract != nil {
|
}
|
||||||
baseTx.To = &c.address
|
msg := ethereum.CallMsg{
|
||||||
}
|
From: opts.From,
|
||||||
rawTx = types.NewTx(baseTx)
|
To: contract,
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
GasTipCap: gasTipCap,
|
||||||
|
GasFeeCap: gasFeeCap,
|
||||||
|
Value: value,
|
||||||
|
Data: input,
|
||||||
|
}
|
||||||
|
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
|
||||||
|
if opts.Nonce == nil {
|
||||||
|
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
||||||
} else {
|
} else {
|
||||||
baseTx := &types.DynamicFeeTx{
|
return opts.Nonce.Uint64(), nil
|
||||||
Nonce: nonce,
|
|
||||||
GasFeeCap: opts.GasFeeCap,
|
|
||||||
GasTipCap: opts.GasTipCap,
|
|
||||||
Gas: gasLimit,
|
|
||||||
Value: value,
|
|
||||||
Data: input,
|
|
||||||
}
|
|
||||||
if contract != nil {
|
|
||||||
baseTx.To = &c.address
|
|
||||||
}
|
|
||||||
rawTx = types.NewTx(baseTx)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transact executes an actual transaction invocation, first deriving any missing
|
||||||
|
// authorization fields, and then scheduling the transaction for execution.
|
||||||
|
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||||
|
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
|
||||||
|
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||||
|
}
|
||||||
|
// Create the transaction
|
||||||
|
var (
|
||||||
|
rawTx *types.Transaction
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if opts.GasPrice != nil {
|
||||||
|
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||||
|
} else {
|
||||||
|
// Only query for basefee if gasPrice not specified
|
||||||
|
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); err != nil {
|
||||||
|
return nil, errHead
|
||||||
|
} else if head.BaseFee != nil {
|
||||||
|
rawTx, err = c.createDynamicTx(opts, contract, input, head)
|
||||||
|
} else {
|
||||||
|
// Chain is not London ready -> use legacy transaction
|
||||||
|
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Sign the transaction and schedule it for execution
|
||||||
if opts.Signer == nil {
|
if opts.Signer == nil {
|
||||||
return nil, errors.New("no signer to authorize the transaction with")
|
return nil, errors.New("no signer to authorize the transaction with")
|
||||||
}
|
}
|
||||||
@@ -409,6 +481,9 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
|||||||
|
|
||||||
// UnpackLog unpacks a retrieved log into the provided output structure.
|
// UnpackLog unpacks a retrieved log into the provided output structure.
|
||||||
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
||||||
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
|
return fmt.Errorf("event signature mismatch")
|
||||||
|
}
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -425,6 +500,9 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
|
|||||||
|
|
||||||
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
||||||
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
|
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
|
||||||
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
|
return fmt.Errorf("event signature mismatch")
|
||||||
|
}
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -443,7 +521,7 @@ func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event strin
|
|||||||
// user specified it as such.
|
// user specified it as such.
|
||||||
func ensureContext(ctx context.Context) context.Context {
|
func ensureContext(ctx context.Context) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return context.TODO()
|
return context.Background()
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,49 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
|
||||||
|
|
||||||
|
type mockTransactor struct {
|
||||||
|
baseFee *big.Int
|
||||||
|
gasTipCap *big.Int
|
||||||
|
gasPrice *big.Int
|
||||||
|
suggestGasTipCapCalled bool
|
||||||
|
suggestGasPriceCalled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||||
|
return &types.Header{BaseFee: mt.baseFee}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||||
|
return []byte{1}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||||
|
mt.suggestGasPriceCalled = true
|
||||||
|
return mt.gasPrice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
|
mt.suggestGasTipCapCalled = true
|
||||||
|
return mt.gasTipCap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type mockCaller struct {
|
type mockCaller struct {
|
||||||
codeAtBlockNumber *big.Int
|
codeAtBlockNumber *big.Int
|
||||||
callContractBlockNumber *big.Int
|
callContractBlockNumber *big.Int
|
||||||
@@ -110,7 +151,7 @@ const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16
|
|||||||
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
||||||
hash := crypto.Keccak256Hash([]byte("testName"))
|
hash := crypto.Keccak256Hash([]byte("testName"))
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
common.HexToHash("0x0"),
|
crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")),
|
||||||
hash,
|
hash,
|
||||||
}
|
}
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
||||||
@@ -135,7 +176,7 @@ func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(sliceBytes)
|
hash := crypto.Keccak256Hash(sliceBytes)
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
common.HexToHash("0x0"),
|
crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
|
||||||
hash,
|
hash,
|
||||||
}
|
}
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
||||||
@@ -160,7 +201,7 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(arrBytes)
|
hash := crypto.Keccak256Hash(arrBytes)
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
common.HexToHash("0x0"),
|
crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
|
||||||
hash,
|
hash,
|
||||||
}
|
}
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
||||||
@@ -187,7 +228,7 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
|||||||
var functionTy [24]byte
|
var functionTy [24]byte
|
||||||
copy(functionTy[:], functionTyBytes[0:24])
|
copy(functionTy[:], functionTyBytes[0:24])
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
|
||||||
common.BytesToHash(functionTyBytes),
|
common.BytesToHash(functionTyBytes),
|
||||||
}
|
}
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
||||||
@@ -208,7 +249,7 @@ func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
|||||||
bytes := []byte{1, 2, 3, 4, 5}
|
bytes := []byte{1, 2, 3, 4, 5}
|
||||||
hash := crypto.Keccak256Hash(bytes)
|
hash := crypto.Keccak256Hash(bytes)
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")),
|
||||||
hash,
|
hash,
|
||||||
}
|
}
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
||||||
@@ -226,6 +267,51 @@ func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
|||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransactGasFee(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// GasTipCap and GasFeeCap
|
||||||
|
// When opts.GasTipCap and opts.GasFeeCap are nil
|
||||||
|
mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
|
||||||
|
bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
|
||||||
|
opts := &bind.TransactOpts{Signer: mockSign}
|
||||||
|
tx, err := bc.Transact(opts, "")
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(big.NewInt(5), tx.GasTipCap())
|
||||||
|
assert.Equal(big.NewInt(205), tx.GasFeeCap())
|
||||||
|
assert.Nil(opts.GasTipCap)
|
||||||
|
assert.Nil(opts.GasFeeCap)
|
||||||
|
assert.True(mt.suggestGasTipCapCalled)
|
||||||
|
|
||||||
|
// Second call to Transact should use latest suggested GasTipCap
|
||||||
|
mt.gasTipCap = big.NewInt(6)
|
||||||
|
mt.suggestGasTipCapCalled = false
|
||||||
|
tx, err = bc.Transact(opts, "")
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(big.NewInt(6), tx.GasTipCap())
|
||||||
|
assert.Equal(big.NewInt(206), tx.GasFeeCap())
|
||||||
|
assert.True(mt.suggestGasTipCapCalled)
|
||||||
|
|
||||||
|
// GasPrice
|
||||||
|
// When opts.GasPrice is nil
|
||||||
|
mt = &mockTransactor{gasPrice: big.NewInt(5)}
|
||||||
|
bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
|
||||||
|
opts = &bind.TransactOpts{Signer: mockSign}
|
||||||
|
tx, err = bc.Transact(opts, "")
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(big.NewInt(5), tx.GasPrice())
|
||||||
|
assert.Nil(opts.GasPrice)
|
||||||
|
assert.True(mt.suggestGasPriceCalled)
|
||||||
|
|
||||||
|
// Second call to Transact should use latest suggested GasPrice
|
||||||
|
mt.gasPrice = big.NewInt(6)
|
||||||
|
mt.suggestGasPriceCalled = false
|
||||||
|
tx, err = bc.Transact(opts, "")
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(big.NewInt(6), tx.GasPrice())
|
||||||
|
assert.True(mt.suggestGasPriceCalled)
|
||||||
|
}
|
||||||
|
|
||||||
func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
|
func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
|
||||||
received := make(map[string]interface{})
|
received := make(map[string]interface{})
|
||||||
if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
|
if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
|
||||||
|
|||||||
@@ -1785,6 +1785,132 @@ var bindTests = []struct {
|
|||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
// Test resolving single struct argument
|
||||||
|
{
|
||||||
|
`NewSingleStructArgument`,
|
||||||
|
`
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract NewSingleStructArgument {
|
||||||
|
struct MyStruct{
|
||||||
|
uint256 a;
|
||||||
|
uint256 b;
|
||||||
|
}
|
||||||
|
event StructEvent(MyStruct s);
|
||||||
|
function TestEvent() public {
|
||||||
|
emit StructEvent(MyStruct({a: 1, b: 2}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
[]string{"608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806324ec1d3f14602d575b600080fd5b60336035565b005b7fb4b2ff75e30cb4317eaae16dd8a187dd89978df17565104caa6c2797caae27d460405180604001604052806001815260200160028152506040516078919060ba565b60405180910390a1565b6040820160008201516096600085018260ad565b50602082015160a7602085018260ad565b50505050565b60b48160d3565b82525050565b600060408201905060cd60008301846082565b92915050565b600081905091905056fea26469706673582212208823628796125bf9941ce4eda18da1be3cf2931b231708ab848e1bd7151c0c9a64736f6c63430008070033"},
|
||||||
|
[]string{`[{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"}],"indexed":false,"internalType":"struct Test.MyStruct","name":"s","type":"tuple"}],"name":"StructEvent","type":"event"},{"inputs":[],"name":"TestEvent","outputs":[],"stateMutability":"nonpayable","type":"function"}]`},
|
||||||
|
`
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
var (
|
||||||
|
key, _ = crypto.GenerateKey()
|
||||||
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
|
)
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
_, _, d, err := DeployNewSingleStructArgument(user, sim)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to deploy contract %v", err)
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
_, err = d.TestEvent(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to call contract %v", err)
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
it, err := d.FilterStructEvent(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to filter contract event %v", err)
|
||||||
|
}
|
||||||
|
var count int
|
||||||
|
for it.Next() {
|
||||||
|
if it.Event.S.A.Cmp(big.NewInt(1)) != 0 {
|
||||||
|
t.Fatal("Unexpected contract event")
|
||||||
|
}
|
||||||
|
if it.Event.S.B.Cmp(big.NewInt(2)) != 0 {
|
||||||
|
t.Fatal("Unexpected contract event")
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
if count != 1 {
|
||||||
|
t.Fatal("Unexpected contract event number")
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
// Test errors introduced in v0.8.4
|
||||||
|
{
|
||||||
|
`NewErrors`,
|
||||||
|
`
|
||||||
|
pragma solidity >0.8.4;
|
||||||
|
|
||||||
|
contract NewErrors {
|
||||||
|
error MyError(uint256);
|
||||||
|
error MyError1(uint256);
|
||||||
|
error MyError2(uint256, uint256);
|
||||||
|
error MyError3(uint256 a, uint256 b, uint256 c);
|
||||||
|
function Error() public pure {
|
||||||
|
revert MyError3(1,2,3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
[]string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"},
|
||||||
|
[]string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
||||||
|
`
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
var (
|
||||||
|
key, _ = crypto.GenerateKey()
|
||||||
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
|
)
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
_, tx, contract, err := DeployNewErrors(user, sim)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
_, err = bind.WaitDeployed(nil, sim, tx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if err := contract.Error(new(bind.CallOpts)); err == nil {
|
||||||
|
t.Fatalf("expected contract to throw error")
|
||||||
|
}
|
||||||
|
// TODO (MariusVanDerWijden unpack error using abigen
|
||||||
|
// once that is implemented
|
||||||
|
`,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that packages generated by the binder can be successfully compiled and
|
// Tests that packages generated by the binder can be successfully compiled and
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ package {{.Package}}
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
"errors"
|
||||||
|
|
||||||
ethereum "github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
@@ -101,6 +102,7 @@ import (
|
|||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var (
|
var (
|
||||||
|
_ = errors.New
|
||||||
_ = big.NewInt
|
_ = big.NewInt
|
||||||
_ = strings.NewReader
|
_ = strings.NewReader
|
||||||
_ = ethereum.NotFound
|
_ = ethereum.NotFound
|
||||||
@@ -120,32 +122,48 @@ var (
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
|
||||||
const {{.Type}}ABI = "{{.InputABI}}"
|
var {{.Type}}MetaData = &bind.MetaData{
|
||||||
|
ABI: "{{.InputABI}}",
|
||||||
{{if $contract.FuncSigs}}
|
{{if $contract.FuncSigs -}}
|
||||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
Sigs: map[string]string{
|
||||||
var {{.Type}}FuncSigs = map[string]string{
|
|
||||||
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
},
|
||||||
|
{{end -}}
|
||||||
|
{{if .InputBin -}}
|
||||||
|
Bin: "0x{{.InputBin}}",
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||||
|
// Deprecated: Use {{.Type}}MetaData.ABI instead.
|
||||||
|
var {{.Type}}ABI = {{.Type}}MetaData.ABI
|
||||||
|
|
||||||
|
{{if $contract.FuncSigs}}
|
||||||
|
// Deprecated: Use {{.Type}}MetaData.Sigs instead.
|
||||||
|
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
||||||
|
var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .InputBin}}
|
{{if .InputBin}}
|
||||||
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
||||||
var {{.Type}}Bin = "0x{{.InputBin}}"
|
// Deprecated: Use {{.Type}}MetaData.Bin instead.
|
||||||
|
var {{.Type}}Bin = {{.Type}}MetaData.Bin
|
||||||
|
|
||||||
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
||||||
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
||||||
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
parsed, err := {{.Type}}MetaData.GetAbi()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
|
if parsed == nil {
|
||||||
|
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
|
||||||
|
}
|
||||||
{{range $pattern, $name := .Libraries}}
|
{{range $pattern, $name := .Libraries}}
|
||||||
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||||
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
||||||
{{end}}
|
{{end}}
|
||||||
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2021 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
@@ -17,66 +17,75 @@
|
|||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Error struct {
|
||||||
errBadBool = errors.New("abi: improperly encoded boolean value")
|
Name string
|
||||||
)
|
Inputs Arguments
|
||||||
|
str string
|
||||||
// formatSliceString formats the reflection kind with the given slice size
|
// Sig contains the string signature according to the ABI spec.
|
||||||
// and returns a formatted string representation.
|
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
if sliceSize == -1 {
|
Sig string
|
||||||
return fmt.Sprintf("[]%v", kind)
|
// ID returns the canonical representation of the event's signature used by the
|
||||||
}
|
// abi definition to identify event names and types.
|
||||||
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
ID common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// sliceTypeCheck checks that the given slice can by assigned to the reflection
|
func NewError(name string, inputs Arguments) Error {
|
||||||
// type in t.
|
// sanitize inputs to remove inputs without names
|
||||||
func sliceTypeCheck(t Type, val reflect.Value) error {
|
// and precompute string and sig representation.
|
||||||
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
names := make([]string, len(inputs))
|
||||||
return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
|
types := make([]string, len(inputs))
|
||||||
}
|
for i, input := range inputs {
|
||||||
|
if input.Name == "" {
|
||||||
if t.T == ArrayTy && val.Len() != t.Size {
|
inputs[i] = Argument{
|
||||||
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
Name: fmt.Sprintf("arg%d", i),
|
||||||
}
|
Indexed: input.Indexed,
|
||||||
|
Type: input.Type,
|
||||||
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
|
}
|
||||||
if val.Len() > 0 {
|
} else {
|
||||||
return sliceTypeCheck(*t.Elem, val.Index(0))
|
inputs[i] = input
|
||||||
}
|
}
|
||||||
|
// string representation
|
||||||
|
names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
|
||||||
|
if input.Indexed {
|
||||||
|
names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
|
||||||
|
}
|
||||||
|
// sig representation
|
||||||
|
types[i] = input.Type.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
|
str := fmt.Sprintf("error %v(%v)", name, strings.Join(names, ", "))
|
||||||
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
sig := fmt.Sprintf("%v(%v)", name, strings.Join(types, ","))
|
||||||
|
id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
|
||||||
|
|
||||||
|
return Error{
|
||||||
|
Name: name,
|
||||||
|
Inputs: inputs,
|
||||||
|
str: str,
|
||||||
|
Sig: sig,
|
||||||
|
ID: id,
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// typeCheck checks that the given reflection value can be assigned to the reflection
|
func (e *Error) String() string {
|
||||||
// type in t.
|
return e.str
|
||||||
func typeCheck(t Type, value reflect.Value) error {
|
|
||||||
if t.T == SliceTy || t.T == ArrayTy {
|
|
||||||
return sliceTypeCheck(t, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check base type validity. Element types will be checked later on.
|
|
||||||
if t.GetType().Kind() != value.Kind() {
|
|
||||||
return typeErr(t.GetType().Kind(), value.Kind())
|
|
||||||
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
|
||||||
return typeErr(t.GetType(), value.Type())
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// typeErr returns a formatted type casting error.
|
func (e *Error) Unpack(data []byte) (interface{}, error) {
|
||||||
func typeErr(expected, got interface{}) error {
|
if len(data) < 4 {
|
||||||
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
|
return "", errors.New("invalid data for unpacking")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(data[:4], e.ID[:4]) {
|
||||||
|
return "", errors.New("invalid data for unpacking")
|
||||||
|
}
|
||||||
|
return e.Inputs.Unpack(data[4:])
|
||||||
}
|
}
|
||||||
|
|||||||
82
accounts/abi/error_handling.go
Normal file
82
accounts/abi/error_handling.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2016 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 abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errBadBool = errors.New("abi: improperly encoded boolean value")
|
||||||
|
)
|
||||||
|
|
||||||
|
// formatSliceString formats the reflection kind with the given slice size
|
||||||
|
// and returns a formatted string representation.
|
||||||
|
func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
||||||
|
if sliceSize == -1 {
|
||||||
|
return fmt.Sprintf("[]%v", kind)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceTypeCheck checks that the given slice can by assigned to the reflection
|
||||||
|
// type in t.
|
||||||
|
func sliceTypeCheck(t Type, val reflect.Value) error {
|
||||||
|
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
||||||
|
return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.T == ArrayTy && val.Len() != t.Size {
|
||||||
|
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
|
||||||
|
if val.Len() > 0 {
|
||||||
|
return sliceTypeCheck(*t.Elem, val.Index(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
|
||||||
|
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeCheck checks that the given reflection value can be assigned to the reflection
|
||||||
|
// type in t.
|
||||||
|
func typeCheck(t Type, value reflect.Value) error {
|
||||||
|
if t.T == SliceTy || t.T == ArrayTy {
|
||||||
|
return sliceTypeCheck(t, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check base type validity. Element types will be checked later on.
|
||||||
|
if t.GetType().Kind() != value.Kind() {
|
||||||
|
return typeErr(t.GetType().Kind(), value.Kind())
|
||||||
|
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
||||||
|
return typeErr(t.GetType(), value.Type())
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeErr returns a formatted type casting error.
|
||||||
|
func typeErr(expected, got interface{}) error {
|
||||||
|
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
|
||||||
|
}
|
||||||
@@ -123,15 +123,8 @@ func set(dst, src reflect.Value) error {
|
|||||||
func setSlice(dst, src reflect.Value) error {
|
func setSlice(dst, src reflect.Value) error {
|
||||||
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
|
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
|
||||||
for i := 0; i < src.Len(); i++ {
|
for i := 0; i < src.Len(); i++ {
|
||||||
if src.Index(i).Kind() == reflect.Struct {
|
if err := set(slice.Index(i), src.Index(i)); err != nil {
|
||||||
if err := set(slice.Index(i), src.Index(i)); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// e.g. [][32]uint8 to []common.Hash
|
|
||||||
if err := set(slice.Index(i), src.Index(i)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dst.CanSet() {
|
if dst.CanSet() {
|
||||||
|
|||||||
@@ -762,20 +762,24 @@ func TestUnpackTuple(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
|
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
|
||||||
|
|
||||||
// If the result is single tuple, use struct as return value container directly.
|
// If the result is single tuple, use struct as return value container directly.
|
||||||
v := struct {
|
type v struct {
|
||||||
A *big.Int
|
A *big.Int
|
||||||
B *big.Int
|
B *big.Int
|
||||||
}{new(big.Int), new(big.Int)}
|
}
|
||||||
|
type r struct {
|
||||||
|
Result v
|
||||||
|
}
|
||||||
|
var ret0 = new(r)
|
||||||
|
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&v, "tuple", buff.Bytes())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else {
|
} else {
|
||||||
if v.A.Cmp(big.NewInt(1)) != 0 {
|
if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.A)
|
t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
|
||||||
}
|
}
|
||||||
if v.B.Cmp(big.NewInt(-1)) != 0 {
|
if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", -1, v.B)
|
t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
accounts/external/backend.go
vendored
21
accounts/external/backend.go
vendored
@@ -29,7 +29,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/signer/core"
|
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExternalBackend struct {
|
type ExternalBackend struct {
|
||||||
@@ -196,6 +196,10 @@ type signTransactionResult struct {
|
|||||||
Tx *types.Transaction `json:"tx"`
|
Tx *types.Transaction `json:"tx"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignTx sends the transaction to the external signer.
|
||||||
|
// If chainID is nil, or tx.ChainID is zero, the chain ID will be assigned
|
||||||
|
// by the external signer. For non-legacy transactions, the chain ID of the
|
||||||
|
// transaction overrides the chainID parameter.
|
||||||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
data := hexutil.Bytes(tx.Data())
|
data := hexutil.Bytes(tx.Data())
|
||||||
var to *common.MixedcaseAddress
|
var to *common.MixedcaseAddress
|
||||||
@@ -203,7 +207,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
|||||||
t := common.NewMixedcaseAddress(*tx.To())
|
t := common.NewMixedcaseAddress(*tx.To())
|
||||||
to = &t
|
to = &t
|
||||||
}
|
}
|
||||||
args := &core.SendTxArgs{
|
args := &apitypes.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: hexutil.Uint64(tx.Nonce()),
|
Nonce: hexutil.Uint64(tx.Nonce()),
|
||||||
Value: hexutil.Big(*tx.Value()),
|
Value: hexutil.Big(*tx.Value()),
|
||||||
@@ -211,21 +215,24 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
|||||||
To: to,
|
To: to,
|
||||||
From: common.NewMixedcaseAddress(account.Address),
|
From: common.NewMixedcaseAddress(account.Address),
|
||||||
}
|
}
|
||||||
if tx.GasFeeCap() != nil {
|
switch tx.Type() {
|
||||||
|
case types.LegacyTxType, types.AccessListTxType:
|
||||||
|
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
||||||
|
case types.DynamicFeeTxType:
|
||||||
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
||||||
} else {
|
default:
|
||||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
return nil, fmt.Errorf("unsupported tx type %d", tx.Type())
|
||||||
}
|
}
|
||||||
// We should request the default chain id that we're operating with
|
// We should request the default chain id that we're operating with
|
||||||
// (the chain we're executing on)
|
// (the chain we're executing on)
|
||||||
if chainID != nil {
|
if chainID != nil && chainID.Sign() != 0 {
|
||||||
args.ChainID = (*hexutil.Big)(chainID)
|
args.ChainID = (*hexutil.Big)(chainID)
|
||||||
}
|
}
|
||||||
if tx.Type() != types.LegacyTxType {
|
if tx.Type() != types.LegacyTxType {
|
||||||
// However, if the user asked for a particular chain id, then we should
|
// However, if the user asked for a particular chain id, then we should
|
||||||
// use that instead.
|
// use that instead.
|
||||||
if tx.ChainId() != nil {
|
if tx.ChainId().Sign() != 0 {
|
||||||
args.ChainID = (*hexutil.Big)(tx.ChainId())
|
args.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||||
}
|
}
|
||||||
accessList := tx.AccessList()
|
accessList := tx.AccessList()
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func TestWatchNoDir(t *testing.T) {
|
|||||||
|
|
||||||
// Create ks but not the directory that it watches.
|
// Create ks but not the directory that it watches.
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
list := ks.Accounts()
|
list := ks.Accounts()
|
||||||
@@ -322,7 +322,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||||||
|
|
||||||
// Create a temporary kesytore to test with
|
// Create a temporary kesytore to test with
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
list := ks.Accounts()
|
list := ks.Accounts()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
|
||||||
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
|
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
|
||||||
// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||||
|
|
||||||
// This is the fallback implementation of directory watching.
|
// This is the fallback implementation of directory watching.
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// managerSubBufferSize determines how many incoming wallet events
|
||||||
|
// the manager will buffer in its channel.
|
||||||
|
const managerSubBufferSize = 50
|
||||||
|
|
||||||
// Config contains the settings of the global account manager.
|
// Config contains the settings of the global account manager.
|
||||||
//
|
//
|
||||||
// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management
|
// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management
|
||||||
@@ -33,18 +37,27 @@ type Config struct {
|
|||||||
InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed
|
InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newBackendEvent lets the manager know it should
|
||||||
|
// track the given backend for wallet updates.
|
||||||
|
type newBackendEvent struct {
|
||||||
|
backend Backend
|
||||||
|
processed chan struct{} // Informs event emitter that backend has been integrated
|
||||||
|
}
|
||||||
|
|
||||||
// Manager is an overarching account manager that can communicate with various
|
// Manager is an overarching account manager that can communicate with various
|
||||||
// backends for signing transactions.
|
// backends for signing transactions.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
config *Config // Global account manager configurations
|
config *Config // Global account manager configurations
|
||||||
backends map[reflect.Type][]Backend // Index of backends currently registered
|
backends map[reflect.Type][]Backend // Index of backends currently registered
|
||||||
updaters []event.Subscription // Wallet update subscriptions for all backends
|
updaters []event.Subscription // Wallet update subscriptions for all backends
|
||||||
updates chan WalletEvent // Subscription sink for backend wallet changes
|
updates chan WalletEvent // Subscription sink for backend wallet changes
|
||||||
wallets []Wallet // Cache of all wallets from all registered backends
|
newBackends chan newBackendEvent // Incoming backends to be tracked by the manager
|
||||||
|
wallets []Wallet // Cache of all wallets from all registered backends
|
||||||
|
|
||||||
feed event.Feed // Wallet feed notifying of arrivals/departures
|
feed event.Feed // Wallet feed notifying of arrivals/departures
|
||||||
|
|
||||||
quit chan chan error
|
quit chan chan error
|
||||||
|
term chan struct{} // Channel is closed upon termination of the update loop
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +70,7 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
|||||||
wallets = merge(wallets, backend.Wallets()...)
|
wallets = merge(wallets, backend.Wallets()...)
|
||||||
}
|
}
|
||||||
// Subscribe to wallet notifications from all backends
|
// Subscribe to wallet notifications from all backends
|
||||||
updates := make(chan WalletEvent, 4*len(backends))
|
updates := make(chan WalletEvent, managerSubBufferSize)
|
||||||
|
|
||||||
subs := make([]event.Subscription, len(backends))
|
subs := make([]event.Subscription, len(backends))
|
||||||
for i, backend := range backends {
|
for i, backend := range backends {
|
||||||
@@ -65,12 +78,14 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
|||||||
}
|
}
|
||||||
// Assemble the account manager and return
|
// Assemble the account manager and return
|
||||||
am := &Manager{
|
am := &Manager{
|
||||||
config: config,
|
config: config,
|
||||||
backends: make(map[reflect.Type][]Backend),
|
backends: make(map[reflect.Type][]Backend),
|
||||||
updaters: subs,
|
updaters: subs,
|
||||||
updates: updates,
|
updates: updates,
|
||||||
wallets: wallets,
|
newBackends: make(chan newBackendEvent),
|
||||||
quit: make(chan chan error),
|
wallets: wallets,
|
||||||
|
quit: make(chan chan error),
|
||||||
|
term: make(chan struct{}),
|
||||||
}
|
}
|
||||||
for _, backend := range backends {
|
for _, backend := range backends {
|
||||||
kind := reflect.TypeOf(backend)
|
kind := reflect.TypeOf(backend)
|
||||||
@@ -93,6 +108,14 @@ func (am *Manager) Config() *Config {
|
|||||||
return am.config
|
return am.config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddBackend starts the tracking of an additional backend for wallet updates.
|
||||||
|
// cmd/geth assumes once this func returns the backends have been already integrated.
|
||||||
|
func (am *Manager) AddBackend(backend Backend) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
am.newBackends <- newBackendEvent{backend, done}
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
// update is the wallet event loop listening for notifications from the backends
|
// update is the wallet event loop listening for notifications from the backends
|
||||||
// and updating the cache of wallets.
|
// and updating the cache of wallets.
|
||||||
func (am *Manager) update() {
|
func (am *Manager) update() {
|
||||||
@@ -122,10 +145,22 @@ func (am *Manager) update() {
|
|||||||
|
|
||||||
// Notify any listeners of the event
|
// Notify any listeners of the event
|
||||||
am.feed.Send(event)
|
am.feed.Send(event)
|
||||||
|
case event := <-am.newBackends:
|
||||||
|
am.lock.Lock()
|
||||||
|
// Update caches
|
||||||
|
backend := event.backend
|
||||||
|
am.wallets = merge(am.wallets, backend.Wallets()...)
|
||||||
|
am.updaters = append(am.updaters, backend.Subscribe(am.updates))
|
||||||
|
kind := reflect.TypeOf(backend)
|
||||||
|
am.backends[kind] = append(am.backends[kind], backend)
|
||||||
|
am.lock.Unlock()
|
||||||
|
close(event.processed)
|
||||||
case errc := <-am.quit:
|
case errc := <-am.quit:
|
||||||
// Manager terminating, return
|
// Manager terminating, return
|
||||||
errc <- nil
|
errc <- nil
|
||||||
|
// Signals event emitters the loop is not receiving values
|
||||||
|
// to prevent them from getting stuck.
|
||||||
|
close(am.term)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,6 +168,9 @@ func (am *Manager) update() {
|
|||||||
|
|
||||||
// Backends retrieves the backend(s) with the given type from the account manager.
|
// Backends retrieves the backend(s) with the given type from the account manager.
|
||||||
func (am *Manager) Backends(kind reflect.Type) []Backend {
|
func (am *Manager) Backends(kind reflect.Type) []Backend {
|
||||||
|
am.lock.RLock()
|
||||||
|
defer am.lock.RUnlock()
|
||||||
|
|
||||||
return am.backends[kind]
|
return am.backends[kind]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
appveyor.yml
60
appveyor.yml
@@ -1,29 +1,57 @@
|
|||||||
os: Visual Studio 2019
|
|
||||||
clone_depth: 5
|
clone_depth: 5
|
||||||
version: "{branch}.{build}"
|
version: "{branch}.{build}"
|
||||||
|
|
||||||
|
image:
|
||||||
|
- Ubuntu
|
||||||
|
- Visual Studio 2019
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
# We use gcc from MSYS2 because it is the most recent compiler version available on
|
|
||||||
# AppVeyor. Note: gcc.exe only works properly if the corresponding bin/ directory is
|
|
||||||
# contained in PATH.
|
|
||||||
- GETH_ARCH: amd64
|
- GETH_ARCH: amd64
|
||||||
GETH_CC: C:\msys64\mingw64\bin\gcc.exe
|
GETH_MINGW: 'C:\msys64\mingw64'
|
||||||
PATH: C:\msys64\mingw64\bin;C:\Program Files (x86)\NSIS\;%PATH%
|
|
||||||
- GETH_ARCH: 386
|
- GETH_ARCH: 386
|
||||||
GETH_CC: C:\msys64\mingw32\bin\gcc.exe
|
GETH_MINGW: 'C:\msys64\mingw32'
|
||||||
PATH: C:\msys64\mingw32\bin;C:\Program Files (x86)\NSIS\;%PATH%
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- git submodule update --init --depth 1
|
- git submodule update --init --depth 1
|
||||||
- go version
|
- go version
|
||||||
- "%GETH_CC% --version"
|
|
||||||
|
|
||||||
build_script:
|
for:
|
||||||
- go run build\ci.go install -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
|
# Linux has its own script without -arch and -cc.
|
||||||
|
# The linux builder also runs lint.
|
||||||
|
- matrix:
|
||||||
|
only:
|
||||||
|
- image: Ubuntu
|
||||||
|
build_script:
|
||||||
|
- go run build/ci.go lint
|
||||||
|
- go run build/ci.go install -dlgo
|
||||||
|
test_script:
|
||||||
|
- go run build/ci.go test -dlgo -coverage
|
||||||
|
|
||||||
after_build:
|
# linux/386 is disabled.
|
||||||
- go run build\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
- matrix:
|
||||||
- go run build\ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
exclude:
|
||||||
|
- image: Ubuntu
|
||||||
|
GETH_ARCH: 386
|
||||||
|
|
||||||
test_script:
|
# Windows builds for amd64 + 386.
|
||||||
- go run build\ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -coverage
|
- matrix:
|
||||||
|
only:
|
||||||
|
- image: Visual Studio 2019
|
||||||
|
environment:
|
||||||
|
# We use gcc from MSYS2 because it is the most recent compiler version available on
|
||||||
|
# AppVeyor. Note: gcc.exe only works properly if the corresponding bin/ directory is
|
||||||
|
# contained in PATH.
|
||||||
|
GETH_CC: '%GETH_MINGW%\bin\gcc.exe'
|
||||||
|
PATH: '%GETH_MINGW%\bin;C:\Program Files (x86)\NSIS\;%PATH%'
|
||||||
|
build_script:
|
||||||
|
- 'echo %GETH_ARCH%'
|
||||||
|
- 'echo %GETH_CC%'
|
||||||
|
- '%GETH_CC% --version'
|
||||||
|
- go run build/ci.go install -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
|
||||||
|
after_build:
|
||||||
|
# Upload builds. Note that ci.go makes this a no-op PR builds.
|
||||||
|
- go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||||
|
- go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||||
|
test_script:
|
||||||
|
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -coverage
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
# This file contains sha256 checksums of optional build dependencies.
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
ae4f6b6e2a1677d31817984655a762074b5356da50fb58722b99104870d43503 go1.16.4.src.tar.gz
|
2255eb3e4e824dd7d5fcdc2e7f84534371c186312e546fb1086a34c17752f431 go1.17.2.src.tar.gz
|
||||||
18fe94775763db3878717393b6d41371b0b45206055e49b3838328120c977d13 go1.16.4.darwin-amd64.tar.gz
|
7914497a302a132a465d33f5ee044ce05568bacdb390ab805cb75a3435a23f94 go1.17.2.darwin-amd64.tar.gz
|
||||||
cb6b972cc42e669f3585c648198cd5b6f6d7a0811d413ad64b50c02ba06ccc3a go1.16.4.darwin-arm64.tar.gz
|
ce8771bd3edfb5b28104084b56bbb532eeb47fbb7769c3e664c6223712c30904 go1.17.2.darwin-arm64.tar.gz
|
||||||
cd1b146ef6e9006f27dd99e9687773e7fef30e8c985b7d41bff33e955a3bb53a go1.16.4.linux-386.tar.gz
|
8cea5b8d1f8e8cbb58069bfed58954c71c5b1aca2f3c857765dae83bf724d0d7 go1.17.2.freebsd-386.tar.gz
|
||||||
7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59 go1.16.4.linux-amd64.tar.gz
|
c96e57218fb03e74d683ad63b1684d44c89d5e5b994f36102b33dce21b58499a go1.17.2.freebsd-amd64.tar.gz
|
||||||
8b18eb05ddda2652d69ab1b1dd1f40dd731799f43c6a58b512ad01ae5b5bba21 go1.16.4.linux-arm64.tar.gz
|
8617f2e40d51076983502894181ae639d1d8101bfbc4d7463a2b442f239f5596 go1.17.2.linux-386.tar.gz
|
||||||
a53391a800ddec749ee90d38992babb27b95cfb864027350c737b9aa8e069494 go1.16.4.linux-armv6l.tar.gz
|
f242a9db6a0ad1846de7b6d94d507915d14062660616a61ef7c808a76e4f1676 go1.17.2.linux-amd64.tar.gz
|
||||||
e75c0b114a09eb5499874162b208931dc260de0fedaeedac8621bf263c974605 go1.16.4.windows-386.zip
|
a5a43c9cdabdb9f371d56951b14290eba8ce2f9b0db48fb5fc657943984fd4fc go1.17.2.linux-arm64.tar.gz
|
||||||
d40139b7ade8a3008e3240a6f86fe8f899a9c465c917e11dac8758af216f5eb0 go1.16.4.windows-amd64.zip
|
04d16105008230a9763005be05606f7eb1c683a3dbf0fbfed4034b23889cb7f2 go1.17.2.linux-armv6l.tar.gz
|
||||||
7cf2bc8a175d6d656861165bfc554f92dc78d2abf5afe5631db3579555d97409 go1.16.4.freebsd-386.tar.gz
|
12e2dc7e0ffeebe77083f267ef6705fec1621cdf2ed6489b3af04a13597ed68d go1.17.2.linux-ppc64le.tar.gz
|
||||||
ccdd2b76de1941b60734408fda0d750aaa69330d8a07430eed4c56bdb3502f6f go1.16.4.freebsd-amd64.tar.gz
|
c4b2349a8d11350ca038b8c57f3cc58dc0b31284bcbed4f7fca39aeed28b4a51 go1.17.2.linux-s390x.tar.gz
|
||||||
80cfac566e344096a8df8f37bbd21f89e76a6fbe601406565d71a87a665fc125 go1.16.4.linux-ppc64le.tar.gz
|
8a85257a351996fdf045fe95ed5fdd6917dd48636d562dd11dedf193005a53e0 go1.17.2.windows-386.zip
|
||||||
d6431881b3573dc29ecc24fbeab5e5ec25d8c9273aa543769c86a1a3bbac1ddf go1.16.4.linux-s390x.tar.gz
|
fa6da0b829a66f5fab7e4e312fd6aa1b2d8f045c7ecee83b3d00f6fe5306759a go1.17.2.windows-amd64.zip
|
||||||
|
00575c85dc7a129ba892685a456b27a3f3670f71c8bfde1c5ad151f771d55df7 go1.17.2.windows-arm64.zip
|
||||||
|
|
||||||
7e9a47ab540aa3e8472fbf8120d28bed3b9d9cf625b955818e8bc69628d7187c golangci-lint-1.39.0-darwin-amd64.tar.gz
|
d4bd25b9814eeaa2134197dd2c7671bb791eae786d42010d9d788af20dee4bfa golangci-lint-1.42.0-darwin-amd64.tar.gz
|
||||||
574daa2c9c299b01672a6daeb1873b5f12e413cdb6dc0e30f2ff163956778064 golangci-lint-1.39.0-darwin-arm64.tar.gz
|
e56859c04a2ad5390c6a497b1acb1cc9329ecb1010260c6faae9b5a4c35b35ea golangci-lint-1.42.0-darwin-arm64.tar.gz
|
||||||
6225f7014987324ab78e9b511f294e3f25be013728283c33918c67c8576d543e golangci-lint-1.39.0-freebsd-386.tar.gz
|
14d912a3fa856830339472fc4dc341933adf15f37bdb7130bbbfcf960ecf4809 golangci-lint-1.42.0-freebsd-386.tar.gz
|
||||||
6b3e76e1e5eaf0159411c8e2727f8d533989d3bb19f10e9caa6e0b9619ee267d golangci-lint-1.39.0-freebsd-amd64.tar.gz
|
337257fccc9baeb5ee1cd7e70c153e9d9f59d3afde46d631659500048afbdf80 golangci-lint-1.42.0-freebsd-amd64.tar.gz
|
||||||
a301cacfff87ed9b00313d95278533c25a4527a06b040a17d969b4b7e1b8a90d golangci-lint-1.39.0-freebsd-armv7.tar.gz
|
6debcc266b629359fdd8eef4f4abb05a621604079d27016265afb5b4593b0eff golangci-lint-1.42.0-freebsd-armv6.tar.gz
|
||||||
25bfd96a29c3112f508d5e4fc860dbad7afce657233c343acfa20715717d51e7 golangci-lint-1.39.0-freebsd-armv6.tar.gz
|
878f0e190169db2ce9dde8cefbd99adc4fe28b90b68686bbfcfcc2085e6d693e golangci-lint-1.42.0-freebsd-armv7.tar.gz
|
||||||
9687e4ff15545cfc722b0e46107a94195166a505023b48a316579af25ad09505 golangci-lint-1.39.0-linux-armv7.tar.gz
|
42c78e31faf62b225363eff1b1d2aa74f9dbcb75686c8914aa3e90d6af65cece golangci-lint-1.42.0-linux-386.tar.gz
|
||||||
a7fa7ab2bfc99cbe5e5bcbf5684f5a997f920afbbe2f253d2feb1001d5e3c8b3 golangci-lint-1.39.0-linux-armv6.tar.gz
|
6937f62f8e2329e94822dc11c10b871ace5557ae1fcc4ee2f9980cd6aecbc159 golangci-lint-1.42.0-linux-amd64.tar.gz
|
||||||
c8f9634115beddb4ed9129c1f7ecd4c97c99d07aeef33e3707234097eeb51b7b golangci-lint-1.39.0-linux-mips64le.tar.gz
|
2cf8d23d96cd854a537b355dab2962b960b88a06b615232599f066afd233f246 golangci-lint-1.42.0-linux-arm64.tar.gz
|
||||||
d1234c213b74751f1af413302dde0e9a6d4d29aecef034af7abb07dc1b6e887f golangci-lint-1.39.0-linux-arm64.tar.gz
|
08b003d1ed61367473886defc957af5301066e62338e5d96a319c34dadc4c1d1 golangci-lint-1.42.0-linux-armv6.tar.gz
|
||||||
df25d9267168323b163147acb823ab0215a8a3bb6898a4a9320afdfedde66817 golangci-lint-1.39.0-linux-386.tar.gz
|
c7c00ec4845e806a1f32685f5b150219e180bd6d6a9d584be8d27f0c41d7a1bf golangci-lint-1.42.0-linux-armv7.tar.gz
|
||||||
1767e75fba357b7651b1a796d38453558f371c60af805505ec99e166908c04b5 golangci-lint-1.39.0-linux-ppc64le.tar.gz
|
3650fcf29eb3d8ee326d77791a896b15259eb2d5bf77437dc72e7efe5af6bd40 golangci-lint-1.42.0-linux-mips64.tar.gz
|
||||||
25fd75bf3186b3d930ecae10185689968fd18fd8fa6f9f555d6beb04348c20f6 golangci-lint-1.39.0-linux-s390x.tar.gz
|
f51ae003fdbca4fef78ba73e2eb736a939c8eaa178cd452234213b489da5a420 golangci-lint-1.42.0-linux-mips64le.tar.gz
|
||||||
3a73aa7468087caa62673c8adea99b4e4dff846dc72707222db85f8679b40cbf golangci-lint-1.39.0-linux-amd64.tar.gz
|
1b0bb7b8b22cc4ea7da44fd5ad5faaf6111d0677e01cc6f961b62a96537de2c6 golangci-lint-1.42.0-linux-ppc64le.tar.gz
|
||||||
578caceccf81739bda67dbfec52816709d03608c6878888ecdc0e186a094a41b golangci-lint-1.39.0-linux-mips64.tar.gz
|
8cb56927eb75e572450efbe0ff0f9cf3f56dc9faa81d9e8d30d6559fc1d06e6d golangci-lint-1.42.0-linux-riscv64.tar.gz
|
||||||
494b66ba0e32c8ddf6c4f6b1d05729b110900f6017eda943057e43598c17d7a8 golangci-lint-1.39.0-windows-386.zip
|
5ac41cd31825a176b21505a371a7b307cd9cdf17df0f35bbb3bf1466f9356ccc golangci-lint-1.42.0-linux-s390x.tar.gz
|
||||||
52ec2e13a3cbb47147244dff8cfc35103563deb76e0459133058086fc35fb2c7 golangci-lint-1.39.0-windows-amd64.zip
|
e1cebd2af621ac4b64c20937df92c3819264f2174c92f51e196db1e64ae097e0 golangci-lint-1.42.0-windows-386.zip
|
||||||
|
7e70fcde8e87a17cae0455df07d257ebc86669f3968d568e12727fa24bbe9883 golangci-lint-1.42.0-windows-amd64.zip
|
||||||
|
59da7ce1bda432616bfc28ae663e52c3675adee8d9bf5959fafd657c159576ab golangci-lint-1.42.0-windows-armv6.zip
|
||||||
|
65f62dda937bfcede0326ac77abe947ce1548931e6e13298ca036cb31f224db5 golangci-lint-1.42.0-windows-armv7.zip
|
||||||
|
|||||||
183
build/ci.go
183
build/ci.go
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build none
|
||||||
// +build none
|
// +build none
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -54,6 +55,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -128,19 +130,13 @@ var (
|
|||||||
|
|
||||||
// Distros for which packages are created.
|
// Distros for which packages are created.
|
||||||
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||||
// Note: wily is unsupported because it was officially deprecated on Launchpad.
|
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
||||||
// Note: yakkety is unsupported because it was officially deprecated on Launchpad.
|
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy
|
||||||
// Note: zesty is unsupported because it was officially deprecated on Launchpad.
|
|
||||||
// Note: artful is unsupported because it was officially deprecated on Launchpad.
|
|
||||||
// Note: cosmic is unsupported because it was officially deprecated on Launchpad.
|
|
||||||
// Note: disco is unsupported because it was officially deprecated on Launchpad.
|
|
||||||
// Note: eoan is unsupported because it was officially deprecated on Launchpad.
|
|
||||||
debDistroGoBoots = map[string]string{
|
debDistroGoBoots = map[string]string{
|
||||||
"trusty": "golang-1.11",
|
"trusty": "golang-1.11",
|
||||||
"xenial": "golang-go",
|
"xenial": "golang-go",
|
||||||
"bionic": "golang-go",
|
"bionic": "golang-go",
|
||||||
"focal": "golang-go",
|
"focal": "golang-go",
|
||||||
"groovy": "golang-go",
|
|
||||||
"hirsute": "golang-go",
|
"hirsute": "golang-go",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +148,7 @@ var (
|
|||||||
// This is the version of go that will be downloaded by
|
// This is the version of go that will be downloaded by
|
||||||
//
|
//
|
||||||
// go run ci.go install -dlgo
|
// go run ci.go install -dlgo
|
||||||
dlgoVersion = "1.16.4"
|
dlgoVersion = "1.17.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||||
@@ -183,7 +179,7 @@ func main() {
|
|||||||
case "archive":
|
case "archive":
|
||||||
doArchive(os.Args[2:])
|
doArchive(os.Args[2:])
|
||||||
case "docker":
|
case "docker":
|
||||||
doDockerImage(os.Args[2:])
|
doDocker(os.Args[2:])
|
||||||
case "debsrc":
|
case "debsrc":
|
||||||
doDebianSource(os.Args[2:])
|
doDebianSource(os.Args[2:])
|
||||||
case "nsis":
|
case "nsis":
|
||||||
@@ -264,6 +260,11 @@ func buildFlags(env build.Environment) (flags []string) {
|
|||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
ld = append(ld, "-s")
|
ld = append(ld, "-s")
|
||||||
}
|
}
|
||||||
|
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||||
|
// alpine Linux.
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000")
|
||||||
|
}
|
||||||
if len(ld) > 0 {
|
if len(ld) > 0 {
|
||||||
flags = append(flags, "-ldflags", strings.Join(ld, " "))
|
flags = append(flags, "-ldflags", strings.Join(ld, " "))
|
||||||
}
|
}
|
||||||
@@ -281,6 +282,7 @@ func doTest(cmdline []string) {
|
|||||||
cc = flag.String("cc", "", "Sets C compiler binary")
|
cc = flag.String("cc", "", "Sets C compiler binary")
|
||||||
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||||
verbose = flag.Bool("v", false, "Whether to log verbosely")
|
verbose = flag.Bool("v", false, "Whether to log verbosely")
|
||||||
|
race = flag.Bool("race", false, "Execute the race detector")
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
||||||
@@ -301,6 +303,9 @@ func doTest(cmdline []string) {
|
|||||||
if *verbose {
|
if *verbose {
|
||||||
gotest.Args = append(gotest.Args, "-v")
|
gotest.Args = append(gotest.Args, "-v")
|
||||||
}
|
}
|
||||||
|
if *race {
|
||||||
|
gotest.Args = append(gotest.Args, "-race")
|
||||||
|
}
|
||||||
|
|
||||||
packages := []string{"./..."}
|
packages := []string{"./..."}
|
||||||
if len(flag.CommandLine.Args()) > 0 {
|
if len(flag.CommandLine.Args()) > 0 {
|
||||||
@@ -329,7 +334,7 @@ func doLint(cmdline []string) {
|
|||||||
|
|
||||||
// downloadLinter downloads and unpacks golangci-lint.
|
// downloadLinter downloads and unpacks golangci-lint.
|
||||||
func downloadLinter(cachedir string) string {
|
func downloadLinter(cachedir string) string {
|
||||||
const version = "1.39.0"
|
const version = "1.42.0"
|
||||||
|
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||||
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
|
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
|
||||||
@@ -449,15 +454,17 @@ func maybeSkipArchive(env build.Environment) {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
||||||
log.Printf("skipping archive creation because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
|
log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds the docker images and optionally uploads them to Docker Hub.
|
// Builds the docker images and optionally uploads them to Docker Hub.
|
||||||
func doDockerImage(cmdline []string) {
|
func doDocker(cmdline []string) {
|
||||||
var (
|
var (
|
||||||
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
image = flag.Bool("image", false, `Whether to build and push an arch specific docker image`)
|
||||||
|
manifest = flag.String("manifest", "", `Push a multi-arch docker image for the specified architectures (usually "amd64,arm64")`)
|
||||||
|
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
||||||
@@ -465,6 +472,15 @@ func doDockerImage(cmdline []string) {
|
|||||||
env := build.Env()
|
env := build.Env()
|
||||||
maybeSkipArchive(env)
|
maybeSkipArchive(env)
|
||||||
|
|
||||||
|
// Retrieve the upload credentials and authenticate
|
||||||
|
user := getenvBase64("DOCKER_HUB_USERNAME")
|
||||||
|
pass := getenvBase64("DOCKER_HUB_PASSWORD")
|
||||||
|
|
||||||
|
if len(user) > 0 && len(pass) > 0 {
|
||||||
|
auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
|
||||||
|
auther.Stdin = bytes.NewReader(pass)
|
||||||
|
build.MustRun(auther)
|
||||||
|
}
|
||||||
// Retrieve the version infos to build and push to the following paths:
|
// Retrieve the version infos to build and push to the following paths:
|
||||||
// - ethereum/client-go:latest - Pushes to the master branch, Geth only
|
// - ethereum/client-go:latest - Pushes to the master branch, Geth only
|
||||||
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only
|
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only
|
||||||
@@ -480,27 +496,132 @@ func doDockerImage(cmdline []string) {
|
|||||||
case env.Branch == "master":
|
case env.Branch == "master":
|
||||||
tags = []string{"latest"}
|
tags = []string{"latest"}
|
||||||
case strings.HasPrefix(env.Tag, "v1."):
|
case strings.HasPrefix(env.Tag, "v1."):
|
||||||
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), params.Version}
|
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
|
||||||
}
|
}
|
||||||
// Build the docker images via CLI (don't pull in the `moby` dep to call 3 commands)
|
// If architecture specific image builds are requested, build and push them
|
||||||
build.MustRunCommand("docker", "build", "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
|
if *image {
|
||||||
build.MustRunCommand("docker", "build", "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
|
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
|
||||||
|
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
|
||||||
|
|
||||||
// Retrieve the upload credentials and authenticate
|
// Tag and upload the images to Docker Hub
|
||||||
user := getenvBase64("DOCKER_HUB_USERNAME")
|
for _, tag := range tags {
|
||||||
pass := getenvBase64("DOCKER_HUB_PASSWORD")
|
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, runtime.GOARCH)
|
||||||
|
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, runtime.GOARCH)
|
||||||
|
|
||||||
if len(user) > 0 && len(pass) > 0 {
|
// If the image already exists (non version tag), check the build
|
||||||
auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
|
// number to prevent overwriting a newer commit if concurrent builds
|
||||||
auther.Stdin = bytes.NewReader(pass)
|
// are running. This is still a tiny bit racey if two published are
|
||||||
build.MustRun(auther)
|
// done at the same time, but that's extremely unlikely even on the
|
||||||
|
// master branch.
|
||||||
|
for _, img := range []string{gethImage, toolImage} {
|
||||||
|
if exec.Command("docker", "pull", img).Run() != nil {
|
||||||
|
continue // Generally the only failure is a missing image, which is good
|
||||||
|
}
|
||||||
|
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
||||||
|
}
|
||||||
|
buildnum = bytes.TrimSpace(buildnum)
|
||||||
|
|
||||||
|
if len(buildnum) > 0 && len(env.Buildnum) > 0 {
|
||||||
|
oldnum, err := strconv.Atoi(string(buildnum))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse old image build number: %v", err)
|
||||||
|
}
|
||||||
|
newnum, err := strconv.Atoi(env.Buildnum)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse current build number: %v", err)
|
||||||
|
}
|
||||||
|
if oldnum > newnum {
|
||||||
|
log.Fatalf("Current build number %d not newer than existing %d", newnum, oldnum)
|
||||||
|
} else {
|
||||||
|
log.Printf("Updating %s from build %d to %d", img, oldnum, newnum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), gethImage)
|
||||||
|
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), toolImage)
|
||||||
|
build.MustRunCommand("docker", "push", gethImage)
|
||||||
|
build.MustRunCommand("docker", "push", toolImage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Tag and upload the images to Docker Hub
|
// If multi-arch image manifest push is requested, assemble it
|
||||||
for _, tag := range tags {
|
if len(*manifest) != 0 {
|
||||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), fmt.Sprintf("%s:%s", *upload, tag))
|
// Since different architectures are pushed by different builders, wait
|
||||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), fmt.Sprintf("%s:alltools-%s", *upload, tag))
|
// until all required images are updated.
|
||||||
build.MustRunCommand("docker", "push", fmt.Sprintf("%s:%s", *upload, tag))
|
var mismatch bool
|
||||||
build.MustRunCommand("docker", "push", fmt.Sprintf("%s:alltools-%s", *upload, tag))
|
for i := 0; i < 2; i++ { // 2 attempts, second is race check
|
||||||
|
mismatch = false // hope there's no mismatch now
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
for _, arch := range strings.Split(*manifest, ",") {
|
||||||
|
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, arch)
|
||||||
|
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, arch)
|
||||||
|
|
||||||
|
for _, img := range []string{gethImage, toolImage} {
|
||||||
|
if out, err := exec.Command("docker", "pull", img).CombinedOutput(); err != nil {
|
||||||
|
log.Printf("Required image %s unavailable: %v\nOutput: %s", img, err, out)
|
||||||
|
mismatch = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
||||||
|
}
|
||||||
|
buildnum = bytes.TrimSpace(buildnum)
|
||||||
|
|
||||||
|
if string(buildnum) != env.Buildnum {
|
||||||
|
log.Printf("Build number mismatch on %s: want %s, have %s", img, env.Buildnum, buildnum)
|
||||||
|
mismatch = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mismatch {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mismatch {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mismatch {
|
||||||
|
// Build numbers mismatching, retry in a short time to
|
||||||
|
// avoid concurrent failes in both publisher images. If
|
||||||
|
// however the retry failed too, it means the concurrent
|
||||||
|
// builder is still crunching, let that do the publish.
|
||||||
|
if i == 0 {
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if mismatch {
|
||||||
|
log.Println("Relinquishing publish to other builder")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Assemble and push the Geth manifest image
|
||||||
|
for _, tag := range tags {
|
||||||
|
gethImage := fmt.Sprintf("%s:%s", *upload, tag)
|
||||||
|
|
||||||
|
var gethSubImages []string
|
||||||
|
for _, arch := range strings.Split(*manifest, ",") {
|
||||||
|
gethSubImages = append(gethSubImages, gethImage+"-"+arch)
|
||||||
|
}
|
||||||
|
build.MustRunCommand("docker", append([]string{"manifest", "create", gethImage}, gethSubImages...)...)
|
||||||
|
build.MustRunCommand("docker", "manifest", "push", gethImage)
|
||||||
|
}
|
||||||
|
// Assemble and push the alltools manifest image
|
||||||
|
for _, tag := range tags {
|
||||||
|
toolImage := fmt.Sprintf("%s:alltools-%s", *upload, tag)
|
||||||
|
|
||||||
|
var toolSubImages []string
|
||||||
|
for _, arch := range strings.Split(*manifest, ",") {
|
||||||
|
toolSubImages = append(toolSubImages, toolImage+"-"+arch)
|
||||||
|
}
|
||||||
|
build.MustRunCommand("docker", append([]string{"manifest", "create", toolImage}, toolSubImages...)...)
|
||||||
|
build.MustRunCommand("docker", "manifest", "push", toolImage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/signer/core"
|
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||||
"github.com/ethereum/go-ethereum/signer/fourbyte"
|
"github.com/ethereum/go-ethereum/signer/fourbyte"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ func parse(data []byte) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
messages := core.ValidationMessages{}
|
messages := apitypes.ValidationMessages{}
|
||||||
db.ValidateCallData(nil, data, &messages)
|
db.ValidateCallData(nil, data, &messages)
|
||||||
for _, m := range messages.Messages {
|
for _, m := range messages.Messages {
|
||||||
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/signer/core"
|
"github.com/ethereum/go-ethereum/signer/core"
|
||||||
|
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||||
"github.com/ethereum/go-ethereum/signer/fourbyte"
|
"github.com/ethereum/go-ethereum/signer/fourbyte"
|
||||||
"github.com/ethereum/go-ethereum/signer/rules"
|
"github.com/ethereum/go-ethereum/signer/rules"
|
||||||
"github.com/ethereum/go-ethereum/signer/storage"
|
"github.com/ethereum/go-ethereum/signer/storage"
|
||||||
|
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
@@ -657,7 +657,7 @@ func signer(c *cli.Context) error {
|
|||||||
cors := utils.SplitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
cors := utils.SplitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
srv := rpc.NewServer()
|
srv := rpc.NewServer()
|
||||||
err := node.RegisterApisFromWhitelist(rpcAPI, []string{"account"}, srv, false)
|
err := node.RegisterApis(rpcAPI, []string{"account"}, srv, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not register API: %w", err)
|
utils.Fatalf("Could not register API: %w", err)
|
||||||
}
|
}
|
||||||
@@ -923,7 +923,7 @@ func testExternalUI(api *core.SignerAPI) {
|
|||||||
time.Sleep(delay)
|
time.Sleep(delay)
|
||||||
data := hexutil.Bytes([]byte{})
|
data := hexutil.Bytes([]byte{})
|
||||||
to := common.NewMixedcaseAddress(a)
|
to := common.NewMixedcaseAddress(a)
|
||||||
tx := core.SendTxArgs{
|
tx := apitypes.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: 0x1,
|
Nonce: 0x1,
|
||||||
Value: hexutil.Big(*big.NewInt(6)),
|
Value: hexutil.Big(*big.NewInt(6)),
|
||||||
@@ -1055,11 +1055,11 @@ func GenDoc(ctx *cli.Context) {
|
|||||||
data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04})
|
data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04})
|
||||||
add("SignTxRequest", desc, &core.SignTxRequest{
|
add("SignTxRequest", desc, &core.SignTxRequest{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
Callinfo: []core.ValidationInfo{
|
Callinfo: []apitypes.ValidationInfo{
|
||||||
{Typ: "Warning", Message: "Something looks odd, show this message as a warning"},
|
{Typ: "Warning", Message: "Something looks odd, show this message as a warning"},
|
||||||
{Typ: "Info", Message: "User should see this as well"},
|
{Typ: "Info", Message: "User should see this as well"},
|
||||||
},
|
},
|
||||||
Transaction: core.SendTxArgs{
|
Transaction: apitypes.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: 0x1,
|
Nonce: 0x1,
|
||||||
Value: hexutil.Big(*big.NewInt(6)),
|
Value: hexutil.Big(*big.NewInt(6)),
|
||||||
@@ -1075,7 +1075,7 @@ func GenDoc(ctx *cli.Context) {
|
|||||||
add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+
|
add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+
|
||||||
", because the UI is free to make modifications to the transaction.",
|
", because the UI is free to make modifications to the transaction.",
|
||||||
&core.SignTxResponse{Approved: true,
|
&core.SignTxResponse{Approved: true,
|
||||||
Transaction: core.SendTxArgs{
|
Transaction: apitypes.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: 0x4,
|
Nonce: 0x4,
|
||||||
Value: hexutil.Big(*big.NewInt(6)),
|
Value: hexutil.Big(*big.NewInt(6)),
|
||||||
|
|||||||
@@ -39,16 +39,6 @@ type Chain struct {
|
|||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chain) WriteTo(writer io.Writer) error {
|
|
||||||
for _, block := range c.blocks {
|
|
||||||
if err := rlp.Encode(writer, block); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of the chain.
|
// Len returns the length of the chain.
|
||||||
func (c *Chain) Len() int {
|
func (c *Chain) Len() int {
|
||||||
return len(c.blocks)
|
return len(c.blocks)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||||
@@ -241,9 +242,17 @@ func (s *Suite) createSendAndRecvConns(isEth66 bool) (*Conn, *Conn, error) {
|
|||||||
return sendConn, recvConn, nil
|
return sendConn, recvConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message {
|
||||||
|
if c.negotiatedProtoVersion == 66 {
|
||||||
|
_, msg := c.readAndServe66(chain, timeout)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
return c.readAndServe65(chain, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
// readAndServe serves GetBlockHeaders requests while waiting
|
// readAndServe serves GetBlockHeaders requests while waiting
|
||||||
// on another message from the node.
|
// on another message from the node.
|
||||||
func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message {
|
func (c *Conn) readAndServe65(chain *Chain, timeout time.Duration) Message {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
for time.Since(start) < timeout {
|
for time.Since(start) < timeout {
|
||||||
c.SetReadDeadline(time.Now().Add(5 * time.Second))
|
c.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||||
@@ -278,8 +287,8 @@ func (c *Conn) readAndServe66(chain *Chain, timeout time.Duration) (uint64, Mess
|
|||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case *Ping:
|
case *Ping:
|
||||||
c.Write(&Pong{})
|
c.Write(&Pong{})
|
||||||
case *GetBlockHeaders:
|
case GetBlockHeaders:
|
||||||
headers, err := chain.GetHeaders(*msg)
|
headers, err := chain.GetHeaders(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errorf("could not get headers for inbound header request: %v", err)
|
return 0, errorf("could not get headers for inbound header request: %v", err)
|
||||||
}
|
}
|
||||||
@@ -649,58 +658,68 @@ func (s *Suite) hashAnnounce(isEth66 bool) error {
|
|||||||
return fmt.Errorf("peering failed: %v", err)
|
return fmt.Errorf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
// create NewBlockHashes announcement
|
// create NewBlockHashes announcement
|
||||||
nextBlock := s.fullChain.blocks[s.chain.Len()]
|
type anno struct {
|
||||||
newBlockHash := &NewBlockHashes{
|
Hash common.Hash // Hash of one particular block being announced
|
||||||
{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()},
|
Number uint64 // Number of one particular block being announced
|
||||||
}
|
}
|
||||||
|
nextBlock := s.fullChain.blocks[s.chain.Len()]
|
||||||
|
announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()}
|
||||||
|
newBlockHash := &NewBlockHashes{announcement}
|
||||||
if err := sendConn.Write(newBlockHash); err != nil {
|
if err := sendConn.Write(newBlockHash); err != nil {
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
return fmt.Errorf("failed to write to connection: %v", err)
|
||||||
}
|
}
|
||||||
|
// Announcement sent, now wait for a header request
|
||||||
|
var (
|
||||||
|
id uint64
|
||||||
|
msg Message
|
||||||
|
blockHeaderReq GetBlockHeaders
|
||||||
|
)
|
||||||
if isEth66 {
|
if isEth66 {
|
||||||
// expect GetBlockHeaders request, and respond
|
id, msg = sendConn.Read66()
|
||||||
id, msg := sendConn.Read66()
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case GetBlockHeaders:
|
case GetBlockHeaders:
|
||||||
blockHeaderReq := msg
|
blockHeaderReq = msg
|
||||||
if blockHeaderReq.Amount != 1 {
|
|
||||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
|
||||||
}
|
|
||||||
if blockHeaderReq.Origin.Hash != nextBlock.Hash() {
|
|
||||||
return fmt.Errorf("unexpected block header requested: %v", pretty.Sdump(blockHeaderReq))
|
|
||||||
}
|
|
||||||
resp := ð.BlockHeadersPacket66{
|
|
||||||
RequestId: id,
|
|
||||||
BlockHeadersPacket: eth.BlockHeadersPacket{
|
|
||||||
nextBlock.Header(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := sendConn.Write66(resp, BlockHeaders{}.Code()); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
||||||
}
|
}
|
||||||
|
if blockHeaderReq.Amount != 1 {
|
||||||
|
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
||||||
|
}
|
||||||
|
if blockHeaderReq.Origin.Hash != announcement.Hash {
|
||||||
|
return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
|
||||||
|
pretty.Sdump(announcement),
|
||||||
|
pretty.Sdump(blockHeaderReq))
|
||||||
|
}
|
||||||
|
if err := sendConn.Write66(ð.BlockHeadersPacket66{
|
||||||
|
RequestId: id,
|
||||||
|
BlockHeadersPacket: eth.BlockHeadersPacket{
|
||||||
|
nextBlock.Header(),
|
||||||
|
},
|
||||||
|
}, BlockHeaders{}.Code()); err != nil {
|
||||||
|
return fmt.Errorf("failed to write to connection: %v", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// expect GetBlockHeaders request, and respond
|
msg = sendConn.Read()
|
||||||
switch msg := sendConn.Read().(type) {
|
switch msg := msg.(type) {
|
||||||
case *GetBlockHeaders:
|
case *GetBlockHeaders:
|
||||||
blockHeaderReq := *msg
|
blockHeaderReq = *msg
|
||||||
if blockHeaderReq.Amount != 1 {
|
|
||||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
|
||||||
}
|
|
||||||
if blockHeaderReq.Origin.Hash != nextBlock.Hash() {
|
|
||||||
return fmt.Errorf("unexpected block header requested: %v", pretty.Sdump(blockHeaderReq))
|
|
||||||
}
|
|
||||||
if err := sendConn.Write(&BlockHeaders{nextBlock.Header()}); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
||||||
}
|
}
|
||||||
|
if blockHeaderReq.Amount != 1 {
|
||||||
|
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
||||||
|
}
|
||||||
|
if blockHeaderReq.Origin.Hash != announcement.Hash {
|
||||||
|
return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
|
||||||
|
pretty.Sdump(announcement),
|
||||||
|
pretty.Sdump(blockHeaderReq))
|
||||||
|
}
|
||||||
|
if err := sendConn.Write(&BlockHeaders{nextBlock.Header()}); err != nil {
|
||||||
|
return fmt.Errorf("failed to write to connection: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// wait for block announcement
|
// wait for block announcement
|
||||||
msg := recvConn.readAndServe(s.chain, timeout)
|
msg = recvConn.readAndServe(s.chain, timeout)
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case *NewBlockHashes:
|
case *NewBlockHashes:
|
||||||
hashes := *msg
|
hashes := *msg
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func TestEthSuite(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create new test suite: %v", err)
|
t.Fatalf("could not create new test suite: %v", err)
|
||||||
}
|
}
|
||||||
for _, test := range suite.AllEthTests() {
|
for _, test := range suite.Eth66Tests() {
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
|
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
|
||||||
if result[0].Failed {
|
if result[0].Failed {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@@ -80,25 +79,57 @@ func BasicPing(t *utesting.T) {
|
|||||||
To: te.remoteEndpoint(),
|
To: te.remoteEndpoint(),
|
||||||
Expiration: futureExpiration(),
|
Expiration: futureExpiration(),
|
||||||
})
|
})
|
||||||
|
if err := te.checkPingPong(pingHash); err != nil {
|
||||||
reply, _, _ := te.read(te.l1)
|
|
||||||
if err := te.checkPong(reply, pingHash); err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkPong verifies that reply is a valid PONG matching the given ping hash.
|
// checkPingPong verifies that the remote side sends both a PONG with the
|
||||||
|
// correct hash, and a PING.
|
||||||
|
// The two packets do not have to be in any particular order.
|
||||||
|
func (te *testenv) checkPingPong(pingHash []byte) error {
|
||||||
|
var (
|
||||||
|
pings int
|
||||||
|
pongs int
|
||||||
|
)
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
reply, _, err := te.read(te.l1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch reply.Kind() {
|
||||||
|
case v4wire.PongPacket:
|
||||||
|
if err := te.checkPong(reply, pingHash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pongs++
|
||||||
|
case v4wire.PingPacket:
|
||||||
|
pings++
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("expected PING or PONG, got %v %v", reply.Name(), reply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pongs == 1 && pings == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("expected 1 PING (got %d) and 1 PONG (got %d)", pings, pongs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkPong verifies that reply is a valid PONG matching the given ping hash,
|
||||||
|
// and a PING. The two packets do not have to be in any particular order.
|
||||||
func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
|
func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
|
||||||
if reply == nil || reply.Kind() != v4wire.PongPacket {
|
if reply == nil {
|
||||||
return fmt.Errorf("expected PONG reply, got %v", reply)
|
return fmt.Errorf("expected PONG reply, got nil")
|
||||||
|
}
|
||||||
|
if reply.Kind() != v4wire.PongPacket {
|
||||||
|
return fmt.Errorf("expected PONG reply, got %v %v", reply.Name(), reply)
|
||||||
}
|
}
|
||||||
pong := reply.(*v4wire.Pong)
|
pong := reply.(*v4wire.Pong)
|
||||||
if !bytes.Equal(pong.ReplyTok, pingHash) {
|
if !bytes.Equal(pong.ReplyTok, pingHash) {
|
||||||
return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash)
|
return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash)
|
||||||
}
|
}
|
||||||
wantEndpoint := te.localEndpoint(te.l1)
|
if want := te.localEndpoint(te.l1); !want.IP.Equal(pong.To.IP) || want.UDP != pong.To.UDP {
|
||||||
if !reflect.DeepEqual(pong.To, wantEndpoint) {
|
return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, want)
|
||||||
return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, wantEndpoint)
|
|
||||||
}
|
}
|
||||||
if v4wire.Expired(pong.Expiration) {
|
if v4wire.Expired(pong.Expiration) {
|
||||||
return fmt.Errorf("PONG is expired (%v)", pong.Expiration)
|
return fmt.Errorf("PONG is expired (%v)", pong.Expiration)
|
||||||
@@ -118,9 +149,7 @@ func PingWrongTo(t *utesting.T) {
|
|||||||
To: wrongEndpoint,
|
To: wrongEndpoint,
|
||||||
Expiration: futureExpiration(),
|
Expiration: futureExpiration(),
|
||||||
})
|
})
|
||||||
|
if err := te.checkPingPong(pingHash); err != nil {
|
||||||
reply, _, _ := te.read(te.l1)
|
|
||||||
if err := te.checkPong(reply, pingHash); err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,8 +167,7 @@ func PingWrongFrom(t *utesting.T) {
|
|||||||
Expiration: futureExpiration(),
|
Expiration: futureExpiration(),
|
||||||
})
|
})
|
||||||
|
|
||||||
reply, _, _ := te.read(te.l1)
|
if err := te.checkPingPong(pingHash); err != nil {
|
||||||
if err := te.checkPong(reply, pingHash); err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,8 +188,7 @@ func PingExtraData(t *utesting.T) {
|
|||||||
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
|
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
|
||||||
})
|
})
|
||||||
|
|
||||||
reply, _, _ := te.read(te.l1)
|
if err := te.checkPingPong(pingHash); err != nil {
|
||||||
if err := te.checkPong(reply, pingHash); err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,8 +209,7 @@ func PingExtraDataWrongFrom(t *utesting.T) {
|
|||||||
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
|
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
|
||||||
}
|
}
|
||||||
pingHash := te.send(te.l1, &req)
|
pingHash := te.send(te.l1, &req)
|
||||||
reply, _, _ := te.read(te.l1)
|
if err := te.checkPingPong(pingHash); err != nil {
|
||||||
if err := te.checkPong(reply, pingHash); err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,9 +265,9 @@ func BondThenPingWithWrongFrom(t *utesting.T) {
|
|||||||
To: te.remoteEndpoint(),
|
To: te.remoteEndpoint(),
|
||||||
Expiration: futureExpiration(),
|
Expiration: futureExpiration(),
|
||||||
})
|
})
|
||||||
|
if reply, _, err := te.read(te.l1); err != nil {
|
||||||
reply, _, _ := te.read(te.l1)
|
t.Fatal(err)
|
||||||
if err := te.checkPong(reply, pingHash); err != nil {
|
} else if err := te.checkPong(reply, pingHash); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ The `evm t8n` tool is a stateless state transition utility. It is a utility
|
|||||||
which can
|
which can
|
||||||
|
|
||||||
1. Take a prestate, including
|
1. Take a prestate, including
|
||||||
- Accounts,
|
- Accounts,
|
||||||
- Block context information,
|
- Block context information,
|
||||||
- Previous blockshashes (*optional)
|
- Previous blockshashes (*optional)
|
||||||
2. Apply a set of transactions,
|
2. Apply a set of transactions,
|
||||||
3. Apply a mining-reward (*optional),
|
3. Apply a mining-reward (*optional),
|
||||||
4. And generate a post-state, including
|
4. And generate a post-state, including
|
||||||
- State root, transaction root, receipt root,
|
- State root, transaction root, receipt root,
|
||||||
- Information about rejected transactions,
|
- Information about rejected transactions,
|
||||||
- Optionally: a full or partial post-state dump
|
- Optionally: a full or partial post-state dump
|
||||||
|
|
||||||
## Specification
|
## Specification
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ Command line params that has to be supported are
|
|||||||
--output.result result Determines where to put the result (stateroot, txroot etc) of the post-state.
|
--output.result result Determines where to put the result (stateroot, txroot etc) of the post-state.
|
||||||
`stdout` - into the stdout output
|
`stdout` - into the stdout output
|
||||||
`stderr` - into the stderr output
|
`stderr` - into the stderr output
|
||||||
|
--output.body value If set, the RLP of the transactions (block body) will be written to this file.
|
||||||
|
--input.txs stdin stdin or file name of where to find the transactions to apply. If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions.The '.rlp' format is identical to the output.body format. (default: "txs.json")
|
||||||
--state.fork value Name of ruleset to use.
|
--state.fork value Name of ruleset to use.
|
||||||
--state.chainid value ChainID to use (default: 1)
|
--state.chainid value ChainID to use (default: 1)
|
||||||
--state.reward value Mining reward. Set to -1 to disable (default: 0)
|
--state.reward value Mining reward. Set to -1 to disable (default: 0)
|
||||||
@@ -110,7 +112,10 @@ Two resulting files:
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rejected": [
|
"rejected": [
|
||||||
1
|
{
|
||||||
|
"index": 1,
|
||||||
|
"error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -156,7 +161,10 @@ Output:
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rejected": [
|
"rejected": [
|
||||||
1
|
{
|
||||||
|
"index": 1,
|
||||||
|
"error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,9 +176,9 @@ Mining rewards and ommer rewards might need to be added. This is how those are a
|
|||||||
|
|
||||||
- `block_reward` is the block mining reward for the miner (`0xaa`), of a block at height `N`.
|
- `block_reward` is the block mining reward for the miner (`0xaa`), of a block at height `N`.
|
||||||
- For each ommer (mined by `0xbb`), with blocknumber `N-delta`
|
- For each ommer (mined by `0xbb`), with blocknumber `N-delta`
|
||||||
- (where `delta` is the difference between the current block and the ommer)
|
- (where `delta` is the difference between the current block and the ommer)
|
||||||
- The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward`
|
- The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward`
|
||||||
- The account `0xaa` (block miner) is awarded `block_reward / 32`
|
- The account `0xaa` (block miner) is awarded `block_reward / 32`
|
||||||
|
|
||||||
To make `state_t8n` apply these, the following inputs are required:
|
To make `state_t8n` apply these, the following inputs are required:
|
||||||
|
|
||||||
@@ -200,7 +208,7 @@ Example:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
When applying this, using a reward of `0x08`
|
When applying this, using a reward of `0x80`
|
||||||
Output:
|
Output:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -220,7 +228,7 @@ Output:
|
|||||||
### Future EIPS
|
### Future EIPS
|
||||||
|
|
||||||
It is also possible to experiment with future eips that are not yet defined in a hard fork.
|
It is also possible to experiment with future eips that are not yet defined in a hard fork.
|
||||||
Example, putting EIP-1344 into Frontier:
|
Example, putting EIP-1344 into Frontier:
|
||||||
```
|
```
|
||||||
./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json
|
./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json
|
||||||
```
|
```
|
||||||
@@ -229,41 +237,102 @@ Example, putting EIP-1344 into Frontier:
|
|||||||
|
|
||||||
The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`.
|
The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`.
|
||||||
If a required blockhash is not provided, the exit code should be `4`:
|
If a required blockhash is not provided, the exit code should be `4`:
|
||||||
Example where blockhashes are provided:
|
Example where blockhashes are provided:
|
||||||
```
|
```
|
||||||
./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
|
./evm --verbosity=1 t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
|
||||||
|
INFO [07-27|11:53:40.960] Trie dumping started root=b7341d..857ea1
|
||||||
|
INFO [07-27|11:53:40.960] Trie dumping complete accounts=3 elapsed="103.298µs"
|
||||||
|
INFO [07-27|11:53:40.960] Wrote file file=alloc.json
|
||||||
|
INFO [07-27|11:53:40.960] Wrote file file=result.json
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
|
cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
||||||
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
|
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
|
||||||
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""}
|
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""}
|
||||||
{"output":"","gasUsed":"0x17","time":142709}
|
{"output":"","gasUsed":"0x17","time":156276}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, the caller has not provided the required blockhash:
|
In this example, the caller has not provided the required blockhash:
|
||||||
```
|
```
|
||||||
./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace
|
./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace
|
||||||
```
|
|
||||||
```
|
|
||||||
ERROR(4): getHash(3) invoked, blockhash for that block not provided
|
ERROR(4): getHash(3) invoked, blockhash for that block not provided
|
||||||
```
|
```
|
||||||
Error code: 4
|
Error code: 4
|
||||||
|
|
||||||
### Chaining
|
### Chaining
|
||||||
|
|
||||||
Another thing that can be done, is to chain invocations:
|
Another thing that can be done, is to chain invocations:
|
||||||
```
|
```
|
||||||
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
|
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
|
||||||
INFO [01-21|22:41:22.963] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
INFO [07-27|11:53:41.049] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||||
INFO [01-21|22:41:22.966] rejected tx index=0 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
INFO [07-27|11:53:41.050] Trie dumping started root=84208a..ae4e13
|
||||||
INFO [01-21|22:41:22.967] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
INFO [07-27|11:53:41.050] Trie dumping complete accounts=3 elapsed="59.412µs"
|
||||||
|
INFO [07-27|11:53:41.050] Wrote file file=result.json
|
||||||
|
INFO [07-27|11:53:41.051] rejected tx index=0 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||||
|
INFO [07-27|11:53:41.051] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||||
|
INFO [07-27|11:53:41.052] Trie dumping started root=84208a..ae4e13
|
||||||
|
INFO [07-27|11:53:41.052] Trie dumping complete accounts=3 elapsed="45.734µs"
|
||||||
|
INFO [07-27|11:53:41.052] Wrote file file=alloc.json
|
||||||
|
INFO [07-27|11:53:41.052] Wrote file file=result.json
|
||||||
|
|
||||||
```
|
```
|
||||||
What happened here, is that we first applied two identical transactions, so the second one was rejected.
|
What happened here, is that we first applied two identical transactions, so the second one was rejected.
|
||||||
Then, taking the poststate alloc as the input for the next state, we tried again to include
|
Then, taking the poststate alloc as the input for the next state, we tried again to include
|
||||||
the same two transactions: this time, both failed due to too low nonce.
|
the same two transactions: this time, both failed due to too low nonce.
|
||||||
|
|
||||||
In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the
|
In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the
|
||||||
actual blocknumber (exposed to the EVM) would not increase.
|
actual blocknumber (exposed to the EVM) would not increase.
|
||||||
|
|
||||||
|
### Transactions in RLP form
|
||||||
|
|
||||||
|
It is possible to provide already-signed transactions as input to, using an `input.txs` which ends with the `rlp` suffix.
|
||||||
|
The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible
|
||||||
|
to use the evm to go from `json` input to `rlp` input.
|
||||||
|
|
||||||
|
The following command takes **json** the transactions in `./testdata/13/txs.json` and signs them. After execution, they are output to `signed_txs.rlp`.:
|
||||||
|
```
|
||||||
|
./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./testdata/13/txs.json --input.env=./testdata/13/env.json --output.result=alloc_jsontx.json --output.body=signed_txs.rlp
|
||||||
|
INFO [07-27|11:53:41.124] Trie dumping started root=e4b924..6aef61
|
||||||
|
INFO [07-27|11:53:41.124] Trie dumping complete accounts=3 elapsed="94.284µs"
|
||||||
|
INFO [07-27|11:53:41.125] Wrote file file=alloc.json
|
||||||
|
INFO [07-27|11:53:41.125] Wrote file file=alloc_jsontx.json
|
||||||
|
INFO [07-27|11:53:41.125] Wrote file file=signed_txs.rlp
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The `output.body` is the rlp-list of transactions, encoded in hex and placed in a string a'la `json` encoding rules:
|
||||||
|
```
|
||||||
|
cat signed_txs.rlp
|
||||||
|
"0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
||||||
|
```
|
||||||
|
|
||||||
|
We can use `rlpdump` to check what the contents are:
|
||||||
|
```
|
||||||
|
rlpdump -hex $(cat signed_txs.rlp | jq -r )
|
||||||
|
[
|
||||||
|
02f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904,
|
||||||
|
02f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9,
|
||||||
|
]
|
||||||
|
```
|
||||||
|
Now, we can now use those (or any other already signed transactions), as input, like so:
|
||||||
|
```
|
||||||
|
./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./signed_txs.rlp --input.env=./testdata/13/env.json --output.result=alloc_rlptx.json
|
||||||
|
INFO [07-27|11:53:41.253] Trie dumping started root=e4b924..6aef61
|
||||||
|
INFO [07-27|11:53:41.253] Trie dumping complete accounts=3 elapsed="128.445µs"
|
||||||
|
INFO [07-27|11:53:41.253] Wrote file file=alloc.json
|
||||||
|
INFO [07-27|11:53:41.255] Wrote file file=alloc_rlptx.json
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
You might have noticed that the results from these two invocations were stored in two separate files.
|
||||||
|
And we can now finally check that they match.
|
||||||
|
```
|
||||||
|
cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot
|
||||||
|
"0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61"
|
||||||
|
"0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61"
|
||||||
|
```
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func disasmCmd(ctx *cli.Context) error {
|
|||||||
case ctx.GlobalIsSet(InputFlag.Name):
|
case ctx.GlobalIsSet(InputFlag.Name):
|
||||||
in = ctx.GlobalString(InputFlag.Name)
|
in = ctx.GlobalString(InputFlag.Name)
|
||||||
default:
|
default:
|
||||||
return errors.New("Missing filename or --input value")
|
return errors.New("missing filename or --input value")
|
||||||
}
|
}
|
||||||
|
|
||||||
code := strings.TrimSpace(in)
|
code := strings.TrimSpace(in)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
@@ -46,13 +47,14 @@ type Prestate struct {
|
|||||||
// ExecutionResult contains the execution status after running a state test, any
|
// ExecutionResult contains the execution status after running a state test, any
|
||||||
// error that might have occurred and a dump of the final state if requested.
|
// error that might have occurred and a dump of the final state if requested.
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
StateRoot common.Hash `json:"stateRoot"`
|
StateRoot common.Hash `json:"stateRoot"`
|
||||||
TxRoot common.Hash `json:"txRoot"`
|
TxRoot common.Hash `json:"txRoot"`
|
||||||
ReceiptRoot common.Hash `json:"receiptRoot"`
|
ReceiptRoot common.Hash `json:"receiptRoot"`
|
||||||
LogsHash common.Hash `json:"logsHash"`
|
LogsHash common.Hash `json:"logsHash"`
|
||||||
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Receipts types.Receipts `json:"receipts"`
|
Receipts types.Receipts `json:"receipts"`
|
||||||
Rejected []*rejectedTx `json:"rejected,omitempty"`
|
Rejected []*rejectedTx `json:"rejected,omitempty"`
|
||||||
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ommer struct {
|
type ommer struct {
|
||||||
@@ -62,23 +64,28 @@ type ommer struct {
|
|||||||
|
|
||||||
//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
|
//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
|
||||||
type stEnv struct {
|
type stEnv struct {
|
||||||
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
|
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
|
||||||
Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
|
Difficulty *big.Int `json:"currentDifficulty"`
|
||||||
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
|
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
||||||
Number uint64 `json:"currentNumber" gencodec:"required"`
|
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
|
||||||
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
Number uint64 `json:"currentNumber" gencodec:"required"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
|
||||||
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
|
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
||||||
|
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type stEnvMarshaling struct {
|
type stEnvMarshaling struct {
|
||||||
Coinbase common.UnprefixedAddress
|
Coinbase common.UnprefixedAddress
|
||||||
Difficulty *math.HexOrDecimal256
|
Difficulty *math.HexOrDecimal256
|
||||||
GasLimit math.HexOrDecimal64
|
ParentDifficulty *math.HexOrDecimal256
|
||||||
Number math.HexOrDecimal64
|
GasLimit math.HexOrDecimal64
|
||||||
Timestamp math.HexOrDecimal64
|
Number math.HexOrDecimal64
|
||||||
BaseFee *math.HexOrDecimal256
|
Timestamp math.HexOrDecimal64
|
||||||
|
ParentTimestamp math.HexOrDecimal64
|
||||||
|
BaseFee *math.HexOrDecimal256
|
||||||
}
|
}
|
||||||
|
|
||||||
type rejectedTx struct {
|
type rejectedTx struct {
|
||||||
@@ -152,7 +159,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
}
|
}
|
||||||
vmConfig.Tracer = tracer
|
vmConfig.Tracer = tracer
|
||||||
vmConfig.Debug = (tracer != nil)
|
vmConfig.Debug = (tracer != nil)
|
||||||
statedb.Prepare(tx.Hash(), blockHash, txIndex)
|
statedb.Prepare(tx.Hash(), txIndex)
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
|
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
|
||||||
@@ -197,7 +204,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
// These three are non-consensus fields:
|
// These three are non-consensus fields:
|
||||||
//receipt.BlockHash
|
//receipt.BlockHash
|
||||||
@@ -247,6 +254,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
LogsHash: rlpHash(statedb.Logs()),
|
LogsHash: rlpHash(statedb.Logs()),
|
||||||
Receipts: receipts,
|
Receipts: receipts,
|
||||||
Rejected: rejectedTxs,
|
Rejected: rejectedTxs,
|
||||||
|
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
|
||||||
}
|
}
|
||||||
return statedb, execRs, nil
|
return statedb, execRs, nil
|
||||||
}
|
}
|
||||||
@@ -274,3 +282,23 @@ func rlpHash(x interface{}) (h common.Hash) {
|
|||||||
hw.Sum(h[:0])
|
hw.Sum(h[:0])
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calcDifficulty is based on ethash.CalcDifficulty. This method is used in case
|
||||||
|
// the caller does not provide an explicit difficulty, but instead provides only
|
||||||
|
// parent timestamp + difficulty.
|
||||||
|
// Note: this method only works for ethash engine.
|
||||||
|
func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64,
|
||||||
|
parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int {
|
||||||
|
uncleHash := parentUncleHash
|
||||||
|
if uncleHash == (common.Hash{}) {
|
||||||
|
uncleHash = types.EmptyUncleHash
|
||||||
|
}
|
||||||
|
parent := &types.Header{
|
||||||
|
ParentHash: common.Hash{},
|
||||||
|
UncleHash: uncleHash,
|
||||||
|
Difficulty: parentDifficulty,
|
||||||
|
Number: new(big.Int).SetUint64(number - 1),
|
||||||
|
Time: parentTime,
|
||||||
|
}
|
||||||
|
return ethash.CalcDifficulty(config, currentTime, parent)
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ var (
|
|||||||
Name: "trace",
|
Name: "trace",
|
||||||
Usage: "Output full trace logs to files <txhash>.jsonl",
|
Usage: "Output full trace logs to files <txhash>.jsonl",
|
||||||
}
|
}
|
||||||
TraceDisableMemoryFlag = cli.BoolFlag{
|
TraceDisableMemoryFlag = cli.BoolTFlag{
|
||||||
Name: "trace.nomemory",
|
Name: "trace.nomemory",
|
||||||
Usage: "Disable full memory dump in traces",
|
Usage: "Disable full memory dump in traces",
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ var (
|
|||||||
Name: "trace.nostack",
|
Name: "trace.nostack",
|
||||||
Usage: "Disable stack output in traces",
|
Usage: "Disable stack output in traces",
|
||||||
}
|
}
|
||||||
TraceDisableReturnDataFlag = cli.BoolFlag{
|
TraceDisableReturnDataFlag = cli.BoolTFlag{
|
||||||
Name: "trace.noreturndata",
|
Name: "trace.noreturndata",
|
||||||
Usage: "Disable return data output in traces",
|
Usage: "Disable return data output in traces",
|
||||||
}
|
}
|
||||||
@@ -79,8 +79,10 @@ var (
|
|||||||
Value: "env.json",
|
Value: "env.json",
|
||||||
}
|
}
|
||||||
InputTxsFlag = cli.StringFlag{
|
InputTxsFlag = cli.StringFlag{
|
||||||
Name: "input.txs",
|
Name: "input.txs",
|
||||||
Usage: "`stdin` or file name of where to find the transactions to apply.",
|
Usage: "`stdin` or file name of where to find the transactions to apply. " +
|
||||||
|
"If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions." +
|
||||||
|
"The '.rlp' format is identical to the output.body format.",
|
||||||
Value: "txs.json",
|
Value: "txs.json",
|
||||||
}
|
}
|
||||||
RewardFlag = cli.Int64Flag{
|
RewardFlag = cli.Int64Flag{
|
||||||
|
|||||||
@@ -16,38 +16,47 @@ var _ = (*stEnvMarshaling)(nil)
|
|||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (s stEnv) MarshalJSON() ([]byte, error) {
|
func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||||
type stEnv struct {
|
type stEnv struct {
|
||||||
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
||||||
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
||||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
|
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||||
}
|
}
|
||||||
var enc stEnv
|
var enc stEnv
|
||||||
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
||||||
enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
|
enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
|
||||||
|
enc.ParentDifficulty = (*math.HexOrDecimal256)(s.ParentDifficulty)
|
||||||
enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
|
enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
|
||||||
enc.Number = math.HexOrDecimal64(s.Number)
|
enc.Number = math.HexOrDecimal64(s.Number)
|
||||||
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
|
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
|
||||||
|
enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp)
|
||||||
enc.BlockHashes = s.BlockHashes
|
enc.BlockHashes = s.BlockHashes
|
||||||
enc.Ommers = s.Ommers
|
enc.Ommers = s.Ommers
|
||||||
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
||||||
|
enc.ParentUncleHash = s.ParentUncleHash
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||||
type stEnv struct {
|
type stEnv struct {
|
||||||
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
||||||
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
||||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
|
ParentUncleHash *common.Hash `json:"parentUncleHash"`
|
||||||
}
|
}
|
||||||
var dec stEnv
|
var dec stEnv
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@@ -57,10 +66,12 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||||||
return errors.New("missing required field 'currentCoinbase' for stEnv")
|
return errors.New("missing required field 'currentCoinbase' for stEnv")
|
||||||
}
|
}
|
||||||
s.Coinbase = common.Address(*dec.Coinbase)
|
s.Coinbase = common.Address(*dec.Coinbase)
|
||||||
if dec.Difficulty == nil {
|
if dec.Difficulty != nil {
|
||||||
return errors.New("missing required field 'currentDifficulty' for stEnv")
|
s.Difficulty = (*big.Int)(dec.Difficulty)
|
||||||
|
}
|
||||||
|
if dec.ParentDifficulty != nil {
|
||||||
|
s.ParentDifficulty = (*big.Int)(dec.ParentDifficulty)
|
||||||
}
|
}
|
||||||
s.Difficulty = (*big.Int)(dec.Difficulty)
|
|
||||||
if dec.GasLimit == nil {
|
if dec.GasLimit == nil {
|
||||||
return errors.New("missing required field 'currentGasLimit' for stEnv")
|
return errors.New("missing required field 'currentGasLimit' for stEnv")
|
||||||
}
|
}
|
||||||
@@ -73,6 +84,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||||||
return errors.New("missing required field 'currentTimestamp' for stEnv")
|
return errors.New("missing required field 'currentTimestamp' for stEnv")
|
||||||
}
|
}
|
||||||
s.Timestamp = uint64(*dec.Timestamp)
|
s.Timestamp = uint64(*dec.Timestamp)
|
||||||
|
if dec.ParentTimestamp != nil {
|
||||||
|
s.ParentTimestamp = uint64(*dec.ParentTimestamp)
|
||||||
|
}
|
||||||
if dec.BlockHashes != nil {
|
if dec.BlockHashes != nil {
|
||||||
s.BlockHashes = dec.BlockHashes
|
s.BlockHashes = dec.BlockHashes
|
||||||
}
|
}
|
||||||
@@ -82,5 +96,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.BaseFee != nil {
|
if dec.BaseFee != nil {
|
||||||
s.BaseFee = (*big.Int)(dec.BaseFee)
|
s.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
}
|
}
|
||||||
|
if dec.ParentUncleHash != nil {
|
||||||
|
s.ParentUncleHash = *dec.ParentUncleHash
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
174
cmd/evm/internal/t8ntool/transaction.go
Normal file
174
cmd/evm/internal/t8ntool/transaction.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2021 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 t8ntool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type result struct {
|
||||||
|
Error error
|
||||||
|
Address common.Address
|
||||||
|
Hash common.Hash
|
||||||
|
IntrinsicGas uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON with a hash.
|
||||||
|
func (r *result) MarshalJSON() ([]byte, error) {
|
||||||
|
type xx struct {
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
Address *common.Address `json:"address,omitempty"`
|
||||||
|
Hash *common.Hash `json:"hash,omitempty"`
|
||||||
|
IntrinsicGas uint64 `json:"intrinsicGas,omitempty"`
|
||||||
|
}
|
||||||
|
var out xx
|
||||||
|
if r.Error != nil {
|
||||||
|
out.Error = r.Error.Error()
|
||||||
|
}
|
||||||
|
if r.Address != (common.Address{}) {
|
||||||
|
out.Address = &r.Address
|
||||||
|
}
|
||||||
|
if r.Hash != (common.Hash{}) {
|
||||||
|
out.Hash = &r.Hash
|
||||||
|
}
|
||||||
|
out.IntrinsicGas = r.IntrinsicGas
|
||||||
|
return json.Marshal(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Transaction(ctx *cli.Context) error {
|
||||||
|
// Configure the go-ethereum logger
|
||||||
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
|
log.Root().SetHandler(glogger)
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
// We need to load the transactions. May be either in stdin input or in files.
|
||||||
|
// Check if anything needs to be read from stdin
|
||||||
|
var (
|
||||||
|
txStr = ctx.String(InputTxsFlag.Name)
|
||||||
|
inputData = &input{}
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
|
)
|
||||||
|
// Construct the chainconfig
|
||||||
|
if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
|
||||||
|
return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
|
||||||
|
} else {
|
||||||
|
chainConfig = cConf
|
||||||
|
}
|
||||||
|
// Set the chain id
|
||||||
|
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
||||||
|
var body hexutil.Bytes
|
||||||
|
if txStr == stdinSelector {
|
||||||
|
decoder := json.NewDecoder(os.Stdin)
|
||||||
|
if err := decoder.Decode(inputData); err != nil {
|
||||||
|
return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err))
|
||||||
|
}
|
||||||
|
// Decode the body of already signed transactions
|
||||||
|
body = common.FromHex(inputData.TxRlp)
|
||||||
|
} else {
|
||||||
|
// Read input from file
|
||||||
|
inFile, err := os.Open(txStr)
|
||||||
|
if err != nil {
|
||||||
|
return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
|
||||||
|
}
|
||||||
|
defer inFile.Close()
|
||||||
|
decoder := json.NewDecoder(inFile)
|
||||||
|
if strings.HasSuffix(txStr, ".rlp") {
|
||||||
|
if err := decoder.Decode(&body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NewError(ErrorIO, errors.New("only rlp supported"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signer := types.MakeSigner(chainConfig, new(big.Int))
|
||||||
|
// We now have the transactions in 'body', which is supposed to be an
|
||||||
|
// rlp list of transactions
|
||||||
|
it, err := rlp.NewListIterator([]byte(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var results []result
|
||||||
|
for it.Next() {
|
||||||
|
var tx types.Transaction
|
||||||
|
err := rlp.DecodeBytes(it.Value(), &tx)
|
||||||
|
if err != nil {
|
||||||
|
results = append(results, result{Error: err})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := result{Hash: tx.Hash()}
|
||||||
|
if sender, err := types.Sender(signer, &tx); err != nil {
|
||||||
|
r.Error = err
|
||||||
|
results = append(results, r)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
r.Address = sender
|
||||||
|
}
|
||||||
|
// Check intrinsic gas
|
||||||
|
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
|
||||||
|
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil {
|
||||||
|
r.Error = err
|
||||||
|
results = append(results, r)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
r.IntrinsicGas = gas
|
||||||
|
if tx.Gas() < gas {
|
||||||
|
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
|
||||||
|
results = append(results, r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Validate <256bit fields
|
||||||
|
switch {
|
||||||
|
case tx.Value().BitLen() > 256:
|
||||||
|
r.Error = errors.New("value exceeds 256 bits")
|
||||||
|
case tx.GasPrice().BitLen() > 256:
|
||||||
|
r.Error = errors.New("gasPrice exceeds 256 bits")
|
||||||
|
case tx.GasTipCap().BitLen() > 256:
|
||||||
|
r.Error = errors.New("maxPriorityFeePerGas exceeds 256 bits")
|
||||||
|
case tx.GasFeeCap().BitLen() > 256:
|
||||||
|
r.Error = errors.New("maxFeePerGas exceeds 256 bits")
|
||||||
|
case tx.GasFeeCap().Cmp(tx.GasTipCap()) < 0:
|
||||||
|
r.Error = errors.New("maxFeePerGas < maxPriorityFeePerGas")
|
||||||
|
case new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
|
||||||
|
r.Error = errors.New("gas * gasPrice exceeds 256 bits")
|
||||||
|
case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
|
||||||
|
r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
|
||||||
|
}
|
||||||
|
results = append(results, r)
|
||||||
|
}
|
||||||
|
out, err := json.MarshalIndent(results, "", " ")
|
||||||
|
fmt.Println(string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
@@ -64,17 +65,23 @@ func (n *NumberedError) Error() string {
|
|||||||
return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
|
return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NumberedError) Code() int {
|
func (n *NumberedError) ExitCode() int {
|
||||||
return n.errorCode
|
return n.errorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compile-time conformance test
|
||||||
|
var (
|
||||||
|
_ cli.ExitCoder = (*NumberedError)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type input struct {
|
type input struct {
|
||||||
Alloc core.GenesisAlloc `json:"alloc,omitempty"`
|
Alloc core.GenesisAlloc `json:"alloc,omitempty"`
|
||||||
Env *stEnv `json:"env,omitempty"`
|
Env *stEnv `json:"env,omitempty"`
|
||||||
Txs []*txWithKey `json:"txs,omitempty"`
|
Txs []*txWithKey `json:"txs,omitempty"`
|
||||||
|
TxRlp string `json:"txsRlp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main(ctx *cli.Context) error {
|
func Transition(ctx *cli.Context) error {
|
||||||
// Configure the go-ethereum logger
|
// Configure the go-ethereum logger
|
||||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
@@ -100,10 +107,10 @@ func Main(ctx *cli.Context) error {
|
|||||||
if ctx.Bool(TraceFlag.Name) {
|
if ctx.Bool(TraceFlag.Name) {
|
||||||
// Configure the EVM logger
|
// Configure the EVM logger
|
||||||
logConfig := &vm.LogConfig{
|
logConfig := &vm.LogConfig{
|
||||||
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
||||||
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
|
EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
|
||||||
DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
|
EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
|
||||||
Debug: true,
|
Debug: true,
|
||||||
}
|
}
|
||||||
var prevFile *os.File
|
var prevFile *os.File
|
||||||
// This one closes the last file
|
// This one closes the last file
|
||||||
@@ -199,11 +206,44 @@ func Main(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
defer inFile.Close()
|
defer inFile.Close()
|
||||||
decoder := json.NewDecoder(inFile)
|
decoder := json.NewDecoder(inFile)
|
||||||
if err := decoder.Decode(&txsWithKeys); err != nil {
|
if strings.HasSuffix(txStr, ".rlp") {
|
||||||
return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
var body hexutil.Bytes
|
||||||
|
if err := decoder.Decode(&body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var txs types.Transactions
|
||||||
|
if err := rlp.DecodeBytes(body, &txs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, tx := range txs {
|
||||||
|
txsWithKeys = append(txsWithKeys, &txWithKey{
|
||||||
|
key: nil,
|
||||||
|
tx: tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := decoder.Decode(&txsWithKeys); err != nil {
|
||||||
|
return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
txsWithKeys = inputData.Txs
|
if len(inputData.TxRlp) > 0 {
|
||||||
|
// Decode the body of already signed transactions
|
||||||
|
body := common.FromHex(inputData.TxRlp)
|
||||||
|
var txs types.Transactions
|
||||||
|
if err := rlp.DecodeBytes(body, &txs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, tx := range txs {
|
||||||
|
txsWithKeys = append(txsWithKeys, &txWithKey{
|
||||||
|
key: nil,
|
||||||
|
tx: tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// JSON encoded transactions
|
||||||
|
txsWithKeys = inputData.Txs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// We may have to sign the transactions.
|
// We may have to sign the transactions.
|
||||||
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
|
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
|
||||||
@@ -217,6 +257,20 @@ func Main(ctx *cli.Context) error {
|
|||||||
return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if env := prestate.Env; env.Difficulty == nil {
|
||||||
|
// If difficulty was not provided by caller, we need to calculate it.
|
||||||
|
switch {
|
||||||
|
case env.ParentDifficulty == nil:
|
||||||
|
return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
|
||||||
|
case env.Number == 0:
|
||||||
|
return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
|
||||||
|
case env.Timestamp <= env.ParentTimestamp:
|
||||||
|
return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
|
||||||
|
env.Timestamp, env.ParentTimestamp))
|
||||||
|
}
|
||||||
|
prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
|
||||||
|
env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
|
||||||
|
}
|
||||||
// Run the test and aggregate the result
|
// Run the test and aggregate the result
|
||||||
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
|
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -360,18 +414,20 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(stdOutObject) > 0 {
|
if len(stdOutObject) > 0 {
|
||||||
b, err := json.MarshalIndent(stdOutObject, "", " ")
|
b, err := json.MarshalIndent(stdOutObject, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
||||||
}
|
}
|
||||||
os.Stdout.Write(b)
|
os.Stdout.Write(b)
|
||||||
|
os.Stdout.WriteString("\n")
|
||||||
}
|
}
|
||||||
if len(stdErrObject) > 0 {
|
if len(stdErrObject) > 0 {
|
||||||
b, err := json.MarshalIndent(stdErrObject, "", " ")
|
b, err := json.MarshalIndent(stdErrObject, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
||||||
}
|
}
|
||||||
os.Stderr.Write(b)
|
os.Stderr.Write(b)
|
||||||
|
os.Stderr.WriteString("\n")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ var (
|
|||||||
Name: "receiver",
|
Name: "receiver",
|
||||||
Usage: "The transaction receiver (execution context)",
|
Usage: "The transaction receiver (execution context)",
|
||||||
}
|
}
|
||||||
DisableMemoryFlag = cli.BoolFlag{
|
DisableMemoryFlag = cli.BoolTFlag{
|
||||||
Name: "nomemory",
|
Name: "nomemory",
|
||||||
Usage: "disable memory output",
|
Usage: "disable memory output",
|
||||||
}
|
}
|
||||||
@@ -125,14 +125,9 @@ var (
|
|||||||
Name: "nostorage",
|
Name: "nostorage",
|
||||||
Usage: "disable storage output",
|
Usage: "disable storage output",
|
||||||
}
|
}
|
||||||
DisableReturnDataFlag = cli.BoolFlag{
|
DisableReturnDataFlag = cli.BoolTFlag{
|
||||||
Name: "noreturndata",
|
Name: "noreturndata",
|
||||||
Usage: "disable return data output",
|
Usage: "enable return data output",
|
||||||
}
|
|
||||||
EVMInterpreterFlag = cli.StringFlag{
|
|
||||||
Name: "vm.evm",
|
|
||||||
Usage: "External EVM configuration (default = built-in interpreter)",
|
|
||||||
Value: "",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,7 +135,7 @@ var stateTransitionCommand = cli.Command{
|
|||||||
Name: "transition",
|
Name: "transition",
|
||||||
Aliases: []string{"t8n"},
|
Aliases: []string{"t8n"},
|
||||||
Usage: "executes a full state transition",
|
Usage: "executes a full state transition",
|
||||||
Action: t8ntool.Main,
|
Action: t8ntool.Transition,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
t8ntool.TraceFlag,
|
t8ntool.TraceFlag,
|
||||||
t8ntool.TraceDisableMemoryFlag,
|
t8ntool.TraceDisableMemoryFlag,
|
||||||
@@ -159,6 +154,18 @@ var stateTransitionCommand = cli.Command{
|
|||||||
t8ntool.VerbosityFlag,
|
t8ntool.VerbosityFlag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
|
t8ntool.VerbosityFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
@@ -185,7 +192,6 @@ func init() {
|
|||||||
DisableStackFlag,
|
DisableStackFlag,
|
||||||
DisableStorageFlag,
|
DisableStorageFlag,
|
||||||
DisableReturnDataFlag,
|
DisableReturnDataFlag,
|
||||||
EVMInterpreterFlag,
|
|
||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
compileCommand,
|
compileCommand,
|
||||||
@@ -193,6 +199,7 @@ func init() {
|
|||||||
runCommand,
|
runCommand,
|
||||||
stateTestCommand,
|
stateTestCommand,
|
||||||
stateTransitionCommand,
|
stateTransitionCommand,
|
||||||
|
transactionCommand,
|
||||||
}
|
}
|
||||||
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
|
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
@@ -201,7 +208,7 @@ func main() {
|
|||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
code := 1
|
code := 1
|
||||||
if ec, ok := err.(*t8ntool.NumberedError); ok {
|
if ec, ok := err.(*t8ntool.NumberedError); ok {
|
||||||
code = ec.Code()
|
code = ec.ExitCode()
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
|
|||||||
@@ -108,11 +108,11 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
|
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
|
||||||
log.Root().SetHandler(glogger)
|
log.Root().SetHandler(glogger)
|
||||||
logconfig := &vm.LogConfig{
|
logconfig := &vm.LogConfig{
|
||||||
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
|
EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
|
||||||
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
||||||
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
|
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
|
||||||
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
|
EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name),
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
Debug: ctx.GlobalBool(DebugFlag.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -211,9 +211,8 @@ func runCmd(ctx *cli.Context) error {
|
|||||||
Coinbase: genesisConfig.Coinbase,
|
Coinbase: genesisConfig.Coinbase,
|
||||||
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
Tracer: tracer,
|
Tracer: tracer,
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
||||||
EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ func stateTestCmd(ctx *cli.Context) error {
|
|||||||
|
|
||||||
// Configure the EVM logger
|
// Configure the EVM logger
|
||||||
config := &vm.LogConfig{
|
config := &vm.LogConfig{
|
||||||
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
|
EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
|
||||||
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
||||||
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
|
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
|
||||||
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
|
EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name),
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
tracer vm.Tracer
|
tracer vm.Tracer
|
||||||
|
|||||||
308
cmd/evm/t8n_test.go
Normal file
308
cmd/evm/t8n_test.go
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/reexec"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/cmdtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
// Run the app if we've been exec'd as "ethkey-test" in runEthkey.
|
||||||
|
reexec.Register("evm-test", func() {
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
})
|
||||||
|
// check if we have been reexec'd
|
||||||
|
if reexec.Init() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
type testT8n struct {
|
||||||
|
*cmdtest.TestCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type t8nInput struct {
|
||||||
|
inAlloc string
|
||||||
|
inTxs string
|
||||||
|
inEnv string
|
||||||
|
stFork string
|
||||||
|
stReward string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args *t8nInput) get(base string) []string {
|
||||||
|
var out []string
|
||||||
|
if opt := args.inAlloc; opt != "" {
|
||||||
|
out = append(out, "--input.alloc")
|
||||||
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
|
}
|
||||||
|
if opt := args.inTxs; opt != "" {
|
||||||
|
out = append(out, "--input.txs")
|
||||||
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
|
}
|
||||||
|
if opt := args.inEnv; opt != "" {
|
||||||
|
out = append(out, "--input.env")
|
||||||
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
|
}
|
||||||
|
if opt := args.stFork; opt != "" {
|
||||||
|
out = append(out, "--state.fork", opt)
|
||||||
|
}
|
||||||
|
if opt := args.stReward; opt != "" {
|
||||||
|
out = append(out, "--state.reward", opt)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type t8nOutput struct {
|
||||||
|
alloc bool
|
||||||
|
result bool
|
||||||
|
body bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args *t8nOutput) get() (out []string) {
|
||||||
|
if args.body {
|
||||||
|
out = append(out, "--output.body", "stdout")
|
||||||
|
} else {
|
||||||
|
out = append(out, "--output.body", "") // empty means ignore
|
||||||
|
}
|
||||||
|
if args.result {
|
||||||
|
out = append(out, "--output.result", "stdout")
|
||||||
|
} else {
|
||||||
|
out = append(out, "--output.result", "")
|
||||||
|
}
|
||||||
|
if args.alloc {
|
||||||
|
out = append(out, "--output.alloc", "stdout")
|
||||||
|
} else {
|
||||||
|
out = append(out, "--output.alloc", "")
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestT8n(t *testing.T) {
|
||||||
|
tt := new(testT8n)
|
||||||
|
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
|
||||||
|
for i, tc := range []struct {
|
||||||
|
base string
|
||||||
|
input t8nInput
|
||||||
|
output t8nOutput
|
||||||
|
expExitCode int
|
||||||
|
expOut string
|
||||||
|
}{
|
||||||
|
{ // Test exit (3) on bad config
|
||||||
|
base: "./testdata/1",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "Frontier+1346", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expExitCode: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
base: "./testdata/1",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "Byzantium", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // blockhash test
|
||||||
|
base: "./testdata/3",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "Berlin", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // missing blockhash test
|
||||||
|
base: "./testdata/4",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "Berlin", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expExitCode: 4,
|
||||||
|
},
|
||||||
|
{ // Ommer test
|
||||||
|
base: "./testdata/5",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "Byzantium", "0x80",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // Sign json transactions
|
||||||
|
base: "./testdata/13",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "London", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{body: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // Already signed transactions
|
||||||
|
base: "./testdata/13",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "signed_txs.rlp", "env.json", "London", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{result: true},
|
||||||
|
expOut: "exp2.json",
|
||||||
|
},
|
||||||
|
{ // Difficulty calculation - no uncles
|
||||||
|
base: "./testdata/14",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "London", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // Difficulty calculation - with uncles
|
||||||
|
base: "./testdata/14",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.uncles.json", "London", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{result: true},
|
||||||
|
expOut: "exp2.json",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
|
||||||
|
args := []string{"t8n"}
|
||||||
|
args = append(args, tc.output.get()...)
|
||||||
|
args = append(args, tc.input.get(tc.base)...)
|
||||||
|
tt.Run("evm-test", args...)
|
||||||
|
tt.Logf("args: %v\n", strings.Join(args, " "))
|
||||||
|
// Compare the expected output, if provided
|
||||||
|
if tc.expOut != "" {
|
||||||
|
want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
||||||
|
}
|
||||||
|
have := tt.Output()
|
||||||
|
ok, err := cmpJson(have, want)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
t.Fatalf("test %d, json parsing failed: %v", i, err)
|
||||||
|
case !ok:
|
||||||
|
t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tt.WaitExit()
|
||||||
|
if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
|
||||||
|
t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type t9nInput struct {
|
||||||
|
inTxs string
|
||||||
|
stFork string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args *t9nInput) get(base string) []string {
|
||||||
|
var out []string
|
||||||
|
if opt := args.inTxs; opt != "" {
|
||||||
|
out = append(out, "--input.txs")
|
||||||
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
|
}
|
||||||
|
if opt := args.stFork; opt != "" {
|
||||||
|
out = append(out, "--state.fork", opt)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestT9n(t *testing.T) {
|
||||||
|
tt := new(testT8n)
|
||||||
|
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
|
||||||
|
for i, tc := range []struct {
|
||||||
|
base string
|
||||||
|
input t9nInput
|
||||||
|
expExitCode int
|
||||||
|
expOut string
|
||||||
|
}{
|
||||||
|
{ // London txs on homestead
|
||||||
|
base: "./testdata/15",
|
||||||
|
input: t9nInput{
|
||||||
|
inTxs: "signed_txs.rlp",
|
||||||
|
stFork: "Homestead",
|
||||||
|
},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // London txs on London
|
||||||
|
base: "./testdata/15",
|
||||||
|
input: t9nInput{
|
||||||
|
inTxs: "signed_txs.rlp",
|
||||||
|
stFork: "London",
|
||||||
|
},
|
||||||
|
expOut: "exp2.json",
|
||||||
|
},
|
||||||
|
{ // An RLP list (a blockheader really)
|
||||||
|
base: "./testdata/15",
|
||||||
|
input: t9nInput{
|
||||||
|
inTxs: "blockheader.rlp",
|
||||||
|
stFork: "London",
|
||||||
|
},
|
||||||
|
expOut: "exp3.json",
|
||||||
|
},
|
||||||
|
{ // Transactions with too low gas
|
||||||
|
base: "./testdata/16",
|
||||||
|
input: t9nInput{
|
||||||
|
inTxs: "signed_txs.rlp",
|
||||||
|
stFork: "London",
|
||||||
|
},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
{ // Transactions with value exceeding 256 bits
|
||||||
|
base: "./testdata/17",
|
||||||
|
input: t9nInput{
|
||||||
|
inTxs: "signed_txs.rlp",
|
||||||
|
stFork: "London",
|
||||||
|
},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
|
||||||
|
args := []string{"t9n"}
|
||||||
|
args = append(args, tc.input.get(tc.base)...)
|
||||||
|
|
||||||
|
tt.Run("evm-test", args...)
|
||||||
|
tt.Logf("args:\n go run . %v\n", strings.Join(args, " "))
|
||||||
|
// Compare the expected output, if provided
|
||||||
|
if tc.expOut != "" {
|
||||||
|
want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
||||||
|
}
|
||||||
|
have := tt.Output()
|
||||||
|
ok, err := cmpJson(have, want)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
t.Logf(string(have))
|
||||||
|
t.Fatalf("test %d, json parsing failed: %v", i, err)
|
||||||
|
case !ok:
|
||||||
|
t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tt.WaitExit()
|
||||||
|
if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
|
||||||
|
t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmpJson compares the JSON in two byte slices.
|
||||||
|
func cmpJson(a, b []byte) (bool, error) {
|
||||||
|
var j, j2 interface{}
|
||||||
|
if err := json.Unmarshal(a, &j); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, &j2); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return reflect.DeepEqual(j2, j), nil
|
||||||
|
}
|
||||||
43
cmd/evm/testdata/1/exp.json
vendored
Normal file
43
cmd/evm/testdata/1/exp.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": {
|
||||||
|
"balance": "0xfeed1a9d",
|
||||||
|
"nonce": "0x1"
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0x5ffd4878be161d74",
|
||||||
|
"nonce": "0xac"
|
||||||
|
},
|
||||||
|
"0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0xa410"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13",
|
||||||
|
"txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d",
|
||||||
|
"receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [
|
||||||
|
{
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0x5208",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x5208",
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rejected": [
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": "0x20000"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
cmd/evm/testdata/12/alloc.json
vendored
Normal file
11
cmd/evm/testdata/12/alloc.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "84000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
"0x00" : "0x00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
cmd/evm/testdata/12/env.json
vendored
Normal file
10
cmd/evm/testdata/12/env.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentDifficulty" : "0x020000",
|
||||||
|
"currentNumber" : "0x01",
|
||||||
|
"currentTimestamp" : "0x03e8",
|
||||||
|
"previousHash" : "0xfda4419b3660e99f37e536dae1ab081c180136bb38c837a93e93d9aab58553b2",
|
||||||
|
"currentGasLimit" : "0x0f4240",
|
||||||
|
"currentBaseFee" : "0x20"
|
||||||
|
}
|
||||||
|
|
||||||
40
cmd/evm/testdata/12/readme.md
vendored
Normal file
40
cmd/evm/testdata/12/readme.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
## Test 1559 balance + gasCap
|
||||||
|
|
||||||
|
This test contains an EIP-1559 consensus issue which happened on Ropsten, where
|
||||||
|
`geth` did not properly account for the value transfer while doing the check on `max_fee_per_gas * gas_limit`.
|
||||||
|
|
||||||
|
Before the issue was fixed, this invocation allowed the transaction to pass into a block:
|
||||||
|
```
|
||||||
|
dir=./testdata/12 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout
|
||||||
|
```
|
||||||
|
|
||||||
|
With the fix applied, the result is:
|
||||||
|
```
|
||||||
|
dir=./testdata/12 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout
|
||||||
|
INFO [07-21|19:03:50.276] rejected tx index=0 hash=ccc996..d83435 from=0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B error="insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032"
|
||||||
|
INFO [07-21|19:03:50.276] Trie dumping started root=e05f81..6597a5
|
||||||
|
INFO [07-21|19:03:50.276] Trie dumping complete accounts=1 elapsed="39.549µs"
|
||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0x501bd00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0xe05f81f8244a76503ceec6f88abfcd03047a612a1001217f37d30984536597a5",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [],
|
||||||
|
"rejected": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"error": "insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The transaction is rejected.
|
||||||
20
cmd/evm/testdata/12/txs.json
vendored
Normal file
20
cmd/evm/testdata/12/txs.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"input" : "0x",
|
||||||
|
"gas" : "0x5208",
|
||||||
|
"nonce" : "0x0",
|
||||||
|
"to" : "0x1111111111111111111111111111111111111111",
|
||||||
|
"value" : "0x20",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"chainId" : "0x1",
|
||||||
|
"type" : "0x2",
|
||||||
|
"maxFeePerGas" : "0xfa0",
|
||||||
|
"maxPriorityFeePerGas" : "0x20",
|
||||||
|
"accessList" : [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
23
cmd/evm/testdata/13/alloc.json
vendored
Normal file
23
cmd/evm/testdata/13/alloc.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"0x1111111111111111111111111111111111111111" : {
|
||||||
|
"balance" : "0x010000000000",
|
||||||
|
"code" : "0xfe",
|
||||||
|
"nonce" : "0x01",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x010000000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x01",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xd02d72e067e77158444ef2020ff2d325f929b363" : {
|
||||||
|
"balance" : "0x01000000000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x01",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
cmd/evm/testdata/13/env.json
vendored
Normal file
12
cmd/evm/testdata/13/env.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentDifficulty" : "0x020000",
|
||||||
|
"currentNumber" : "0x01",
|
||||||
|
"currentTimestamp" : "0x079e",
|
||||||
|
"previousHash" : "0xcb23ee65a163121f640673b41788ee94633941405f95009999b502eedfbbfd4f",
|
||||||
|
"currentGasLimit" : "0x40000000",
|
||||||
|
"currentBaseFee" : "0x036b",
|
||||||
|
"blockHashes" : {
|
||||||
|
"0" : "0xcb23ee65a163121f640673b41788ee94633941405f95009999b502eedfbbfd4f"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
cmd/evm/testdata/13/exp.json
vendored
Normal file
3
cmd/evm/testdata/13/exp.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"body": "0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
||||||
|
}
|
||||||
38
cmd/evm/testdata/13/exp2.json
vendored
Normal file
38
cmd/evm/testdata/13/exp2.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61",
|
||||||
|
"txRoot": "0x013509c8563d41c0ae4bf38f2d6d19fc6512a1d0d6be045079c8c9f68bf45f9d",
|
||||||
|
"receiptRoot": "0xa532a08aa9f62431d6fe5d924951b8efb86ed3c54d06fee77788c3767dd13420",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [
|
||||||
|
{
|
||||||
|
"type": "0x2",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x0",
|
||||||
|
"cumulativeGasUsed": "0x84d0",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x84d0",
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "0x2",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x0",
|
||||||
|
"cumulativeGasUsed": "0x109a0",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x84d0",
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": "0x20000"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
cmd/evm/testdata/13/readme.md
vendored
Normal file
4
cmd/evm/testdata/13/readme.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
## Input transactions in RLP form
|
||||||
|
|
||||||
|
This testdata folder is used to examplify how transaction input can be provided in rlp form.
|
||||||
|
Please see the README in `evm` folder for how this is performed.
|
||||||
1
cmd/evm/testdata/13/signed_txs.rlp
vendored
Normal file
1
cmd/evm/testdata/13/signed_txs.rlp
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
||||||
34
cmd/evm/testdata/13/txs.json
vendored
Normal file
34
cmd/evm/testdata/13/txs.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"input" : "0x",
|
||||||
|
"gas" : "0x84d0",
|
||||||
|
"nonce" : "0x1",
|
||||||
|
"to" : "0x1111111111111111111111111111111111111111",
|
||||||
|
"value" : "0x0",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298",
|
||||||
|
"chainId" : "0x1",
|
||||||
|
"type" : "0x2",
|
||||||
|
"maxFeePerGas" : "0xfa0",
|
||||||
|
"maxPriorityFeePerGas" : "0x0",
|
||||||
|
"accessList" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input" : "0x",
|
||||||
|
"gas" : "0x84d0",
|
||||||
|
"nonce" : "0x2",
|
||||||
|
"to" : "0x1111111111111111111111111111111111111111",
|
||||||
|
"value" : "0x0",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298",
|
||||||
|
"chainId" : "0x1",
|
||||||
|
"type" : "0x2",
|
||||||
|
"maxFeePerGas" : "0xfa0",
|
||||||
|
"maxPriorityFeePerGas" : "0x0",
|
||||||
|
"accessList" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
12
cmd/evm/testdata/14/alloc.json
vendored
Normal file
12
cmd/evm/testdata/14/alloc.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0x5ffd4878be161d74",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0xac",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
|
||||||
|
"balance": "0xfeedbead",
|
||||||
|
"nonce" : "0x00"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
cmd/evm/testdata/14/env.json
vendored
Normal file
9
cmd/evm/testdata/14/env.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"currentGasLimit": "0x750a163df65e8a",
|
||||||
|
"currentBaseFee": "0x500",
|
||||||
|
"currentNumber": "12800000",
|
||||||
|
"currentTimestamp": "100015",
|
||||||
|
"parentTimestamp" : "99999",
|
||||||
|
"parentDifficulty" : "0x2000000000000"
|
||||||
|
}
|
||||||
10
cmd/evm/testdata/14/env.uncles.json
vendored
Normal file
10
cmd/evm/testdata/14/env.uncles.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"currentGasLimit": "0x750a163df65e8a",
|
||||||
|
"currentBaseFee": "0x500",
|
||||||
|
"currentNumber": "12800000",
|
||||||
|
"currentTimestamp": "100035",
|
||||||
|
"parentTimestamp" : "99999",
|
||||||
|
"parentDifficulty" : "0x2000000000000",
|
||||||
|
"parentUncleHash" : "0x000000000000000000000000000000000000000000000000000000000000beef"
|
||||||
|
}
|
||||||
11
cmd/evm/testdata/14/exp.json
vendored
Normal file
11
cmd/evm/testdata/14/exp.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"currentDifficulty": "0x2000020000000",
|
||||||
|
"receipts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
11
cmd/evm/testdata/14/exp2.json
vendored
Normal file
11
cmd/evm/testdata/14/exp2.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [],
|
||||||
|
"currentDifficulty": "0x1ff8020000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
41
cmd/evm/testdata/14/readme.md
vendored
Normal file
41
cmd/evm/testdata/14/readme.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
## Difficulty calculation
|
||||||
|
|
||||||
|
This test shows how the `evm t8n` can be used to calculate the (ethash) difficulty, if none is provided by the caller.
|
||||||
|
|
||||||
|
Calculating it (with an empty set of txs) using `London` rules (and no provided unclehash for the parent block):
|
||||||
|
```
|
||||||
|
[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.json --output.result=stdout --state.fork=London
|
||||||
|
INFO [08-30|20:43:09.352] Trie dumping started root=6f0588..7f4bdc
|
||||||
|
INFO [08-30|20:43:09.352] Trie dumping complete accounts=2 elapsed="82.533µs"
|
||||||
|
INFO [08-30|20:43:09.352] Wrote file file=alloc.json
|
||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [],
|
||||||
|
"currentDifficulty": "0x2000020000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Same thing, but this time providing a non-empty (and non-`emptyKeccak`) unclehash, which leads to a slightly different result:
|
||||||
|
```
|
||||||
|
[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.uncles.json --output.result=stdout --state.fork=London
|
||||||
|
INFO [08-30|20:44:33.102] Trie dumping started root=6f0588..7f4bdc
|
||||||
|
INFO [08-30|20:44:33.102] Trie dumping complete accounts=2 elapsed="72.91µs"
|
||||||
|
INFO [08-30|20:44:33.102] Wrote file file=alloc.json
|
||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [],
|
||||||
|
"currentDifficulty": "0x1ff8020000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
1
cmd/evm/testdata/14/txs.json
vendored
Normal file
1
cmd/evm/testdata/14/txs.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
1
cmd/evm/testdata/15/blockheader.rlp
vendored
Normal file
1
cmd/evm/testdata/15/blockheader.rlp
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"0xf901f0a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0101020383010203a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"
|
||||||
10
cmd/evm/testdata/15/exp.json
vendored
Normal file
10
cmd/evm/testdata/15/exp.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported",
|
||||||
|
"hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported",
|
||||||
|
"hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a"
|
||||||
|
}
|
||||||
|
]
|
||||||
12
cmd/evm/testdata/15/exp2.json
vendored
Normal file
12
cmd/evm/testdata/15/exp2.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"address": "0xd02d72e067e77158444ef2020ff2d325f929b363",
|
||||||
|
"hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||||
|
"intrinsicGas": 21000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xd02d72e067e77158444ef2020ff2d325f929b363",
|
||||||
|
"hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||||
|
"intrinsicGas": 21000
|
||||||
|
}
|
||||||
|
]
|
||||||
47
cmd/evm/testdata/15/exp3.json
vendored
Normal file
47
cmd/evm/testdata/15/exp3.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "rlp: expected List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "rlp: expected List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "rlp: expected List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "rlp: expected List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "rlp: expected List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "rlp: expected input list for types.AccessListTx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "transaction type not supported"
|
||||||
|
}
|
||||||
|
]
|
||||||
1
cmd/evm/testdata/15/signed_txs.rlp
vendored
Normal file
1
cmd/evm/testdata/15/signed_txs.rlp
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
||||||
4
cmd/evm/testdata/15/signed_txs.rlp.json
vendored
Normal file
4
cmd/evm/testdata/15/signed_txs.rlp.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"txsRlp" : "0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
||||||
|
}
|
||||||
|
|
||||||
13
cmd/evm/testdata/16/exp.json
vendored
Normal file
13
cmd/evm/testdata/16/exp.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"hash": "0x7cc3d1a8540a44736750f03bb4d85c0113be4b3472a71bf82241a3b261b479e6",
|
||||||
|
"intrinsicGas": 21000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "intrinsic gas too low: have 82, want 21000",
|
||||||
|
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"hash": "0x3b2d2609e4361562edb9169314f4c05afc6dbf5d706bf9dda5abe242ab76a22b",
|
||||||
|
"intrinsicGas": 21000
|
||||||
|
}
|
||||||
|
]
|
||||||
1
cmd/evm/testdata/16/signed_txs.rlp
vendored
Normal file
1
cmd/evm/testdata/16/signed_txs.rlp
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"0xf8cab86401f8610180018252089411111111111111111111111111111111111111112080c001a0937f65ef1deece46c473b99962678fb7c38425cf303d1e8fa9717eb4b9d012b5a01940c5a5647c4940217ffde1051a5fd92ec8551e275c1787f81f50a2ad84de43b86201f85f018001529411111111111111111111111111111111111111112080c001a0241c3aec732205542a87fef8c76346741e85480bce5a42d05a9a73dac892f84ca04f52e2dfce57f3a02ed10e085e1a154edf38a726da34127c85fc53b4921759c8"
|
||||||
34
cmd/evm/testdata/16/unsigned_txs.json
vendored
Normal file
34
cmd/evm/testdata/16/unsigned_txs.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"input" : "0x",
|
||||||
|
"gas" : "0x5208",
|
||||||
|
"nonce" : "0x0",
|
||||||
|
"to" : "0x1111111111111111111111111111111111111111",
|
||||||
|
"value" : "0x20",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"chainId" : "0x1",
|
||||||
|
"type" : "0x1",
|
||||||
|
"gasPrice": "0x1",
|
||||||
|
"accessList" : [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input" : "0x",
|
||||||
|
"gas" : "0x52",
|
||||||
|
"nonce" : "0x0",
|
||||||
|
"to" : "0x1111111111111111111111111111111111111111",
|
||||||
|
"value" : "0x20",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"chainId" : "0x1",
|
||||||
|
"type" : "0x1",
|
||||||
|
"gasPrice": "0x1",
|
||||||
|
"accessList" : [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
22
cmd/evm/testdata/17/exp.json
vendored
Normal file
22
cmd/evm/testdata/17/exp.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"error": "value exceeds 256 bits",
|
||||||
|
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"hash": "0xfbd91685dcbf8172f0e8c53e2ddbb4d26707840da6b51a74371f62a33868fd82",
|
||||||
|
"intrinsicGas": 21000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "gasPrice exceeds 256 bits",
|
||||||
|
"address": "0x1b57ccef1fe5fb73f1e64530fb4ebd9cf1655964",
|
||||||
|
"hash": "0x45dc05035cada83748e4c1fe617220106b331eca054f44c2304d5654a9fb29d5",
|
||||||
|
"intrinsicGas": 21000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "invalid transaction v, r, s values",
|
||||||
|
"hash": "0xf06691c2a803ab7f3c81d06a0c0a896f80f311105c599fc59a9fdbc669356d35"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "invalid transaction v, r, s values",
|
||||||
|
"hash": "0x84703b697ad5b0db25e4f1f98fb6b1adce85b9edb2232eeba9cedd8c6601694b"
|
||||||
|
}
|
||||||
|
]
|
||||||
46
cmd/evm/testdata/17/rlpdata.txt
vendored
Normal file
46
cmd/evm/testdata/17/rlpdata.txt
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
[
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"d",
|
||||||
|
5208,
|
||||||
|
d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
|
||||||
|
010000000000000000000000000000000000000000000000000000000000000001,
|
||||||
|
"",
|
||||||
|
1b,
|
||||||
|
c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549d,
|
||||||
|
6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
010000000000000000000000000000000000000000000000000000000000000001,
|
||||||
|
5208,
|
||||||
|
d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
|
||||||
|
11,
|
||||||
|
"",
|
||||||
|
1b,
|
||||||
|
c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549d,
|
||||||
|
6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
11,
|
||||||
|
5208,
|
||||||
|
d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
|
||||||
|
11,
|
||||||
|
"",
|
||||||
|
1b,
|
||||||
|
c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549daa,
|
||||||
|
6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
11,
|
||||||
|
5208,
|
||||||
|
d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
|
||||||
|
11,
|
||||||
|
"",
|
||||||
|
1b,
|
||||||
|
c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549d,
|
||||||
|
6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28bb,
|
||||||
|
],
|
||||||
|
]
|
||||||
1
cmd/evm/testdata/17/signed_txs.rlp
vendored
Normal file
1
cmd/evm/testdata/17/signed_txs.rlp
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"0xf901c8f880806482520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0a1010000000000000000000000000000000000000000000000000000000000000001801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28f88080a101000000000000000000000000000000000000000000000000000000000000000182520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d011801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28f860801182520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d011801ba1c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549daaa06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28f860801182520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d011801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da16180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28bb"
|
||||||
37
cmd/evm/testdata/3/exp.json
vendored
Normal file
37
cmd/evm/testdata/3/exp.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0x095e7baea6a6c7c4c2dfeb977efac326af552d87": {
|
||||||
|
"code": "0x600140",
|
||||||
|
"balance": "0xde0b6b3a76586a0"
|
||||||
|
},
|
||||||
|
"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
|
||||||
|
"balance": "0x521f"
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0xde0b6b3a7622741",
|
||||||
|
"nonce": "0x1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0xb7341da3f9f762a6884eaa186c32942734c146b609efee11c4b0214c44857ea1",
|
||||||
|
"txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b",
|
||||||
|
"receiptRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [
|
||||||
|
{
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0x521f",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x521f",
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": "0x20000"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
cmd/evm/testdata/5/exp.json
vendored
Normal file
22
cmd/evm/testdata/5/exp.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": {
|
||||||
|
"balance": "0x88"
|
||||||
|
},
|
||||||
|
"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": {
|
||||||
|
"balance": "0x70"
|
||||||
|
},
|
||||||
|
"0xcccccccccccccccccccccccccccccccccccccccc": {
|
||||||
|
"balance": "0x60"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0xa7312add33811645c6aa65d928a1a4f49d65d448801912c069a0aa8fe9c1f393",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [],
|
||||||
|
"currentDifficulty": "0x20000"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ function showjson(){
|
|||||||
function demo(){
|
function demo(){
|
||||||
echo "$ticks"
|
echo "$ticks"
|
||||||
echo "$1"
|
echo "$1"
|
||||||
|
$1
|
||||||
|
echo ""
|
||||||
echo "$ticks"
|
echo "$ticks"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
@@ -152,9 +154,7 @@ echo ""
|
|||||||
echo "The \`BLOCKHASH\` opcode requires blockhashes to be provided by the caller, inside the \`env\`."
|
echo "The \`BLOCKHASH\` opcode requires blockhashes to be provided by the caller, inside the \`env\`."
|
||||||
echo "If a required blockhash is not provided, the exit code should be \`4\`:"
|
echo "If a required blockhash is not provided, the exit code should be \`4\`:"
|
||||||
echo "Example where blockhashes are provided: "
|
echo "Example where blockhashes are provided: "
|
||||||
cmd="./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
|
demo "./evm --verbosity=1 t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
|
||||||
tick && echo $cmd && tick
|
|
||||||
$cmd 2>&1 >/dev/null
|
|
||||||
cmd="cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2"
|
cmd="cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2"
|
||||||
tick && echo $cmd && tick
|
tick && echo $cmd && tick
|
||||||
echo "$ticks"
|
echo "$ticks"
|
||||||
@@ -164,13 +164,11 @@ echo ""
|
|||||||
|
|
||||||
echo "In this example, the caller has not provided the required blockhash:"
|
echo "In this example, the caller has not provided the required blockhash:"
|
||||||
cmd="./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace"
|
cmd="./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace"
|
||||||
tick && echo $cmd && tick
|
tick && echo $cmd && $cmd
|
||||||
tick
|
|
||||||
$cmd
|
|
||||||
errc=$?
|
errc=$?
|
||||||
tick
|
tick
|
||||||
echo "Error code: $errc"
|
echo "Error code: $errc"
|
||||||
|
echo ""
|
||||||
|
|
||||||
echo "### Chaining"
|
echo "### Chaining"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -189,3 +187,28 @@ echo ""
|
|||||||
echo "In order to meaningfully chain invocations, one would need to provide meaningful new \`env\`, otherwise the"
|
echo "In order to meaningfully chain invocations, one would need to provide meaningful new \`env\`, otherwise the"
|
||||||
echo "actual blocknumber (exposed to the EVM) would not increase."
|
echo "actual blocknumber (exposed to the EVM) would not increase."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
echo "### Transactions in RLP form"
|
||||||
|
echo ""
|
||||||
|
echo "It is possible to provide already-signed transactions as input to, using an \`input.txs\` which ends with the \`rlp\` suffix."
|
||||||
|
echo "The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible"
|
||||||
|
echo "to use the evm to go from \`json\` input to \`rlp\` input."
|
||||||
|
echo ""
|
||||||
|
echo "The following command takes **json** the transactions in \`./testdata/13/txs.json\` and signs them. After execution, they are output to \`signed_txs.rlp\`.:"
|
||||||
|
demo "./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./testdata/13/txs.json --input.env=./testdata/13/env.json --output.result=alloc_jsontx.json --output.body=signed_txs.rlp"
|
||||||
|
echo "The \`output.body\` is the rlp-list of transactions, encoded in hex and placed in a string a'la \`json\` encoding rules:"
|
||||||
|
demo "cat signed_txs.rlp"
|
||||||
|
echo "We can use \`rlpdump\` to check what the contents are: "
|
||||||
|
echo "$ticks"
|
||||||
|
echo "rlpdump -hex \$(cat signed_txs.rlp | jq -r )"
|
||||||
|
rlpdump -hex $(cat signed_txs.rlp | jq -r )
|
||||||
|
echo "$ticks"
|
||||||
|
echo "Now, we can now use those (or any other already signed transactions), as input, like so: "
|
||||||
|
demo "./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./signed_txs.rlp --input.env=./testdata/13/env.json --output.result=alloc_rlptx.json"
|
||||||
|
|
||||||
|
echo "You might have noticed that the results from these two invocations were stored in two separate files. "
|
||||||
|
echo "And we can now finally check that they match."
|
||||||
|
echo "$ticks"
|
||||||
|
echo "cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot"
|
||||||
|
cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot
|
||||||
|
echo "$ticks"
|
||||||
|
|||||||
@@ -741,7 +741,7 @@ func authTwitter(url string, tokenV1, tokenV2 string) (string, string, string, c
|
|||||||
return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
||||||
}
|
}
|
||||||
var avatar string
|
var avatar string
|
||||||
if parts = regexp.MustCompile("src=\"([^\"]+twimg.com/profile_images[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
|
if parts = regexp.MustCompile(`src="([^"]+twimg\.com/profile_images[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
|
||||||
avatar = parts[1]
|
avatar = parts[1]
|
||||||
}
|
}
|
||||||
return username + "@twitter", username, avatar, address, nil
|
return username + "@twitter", username, avatar, address, nil
|
||||||
@@ -867,7 +867,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
|
|||||||
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
||||||
}
|
}
|
||||||
var avatar string
|
var avatar string
|
||||||
if parts = regexp.MustCompile("src=\"([^\"]+fbcdn.net[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
|
if parts = regexp.MustCompile(`src="([^"]+fbcdn\.net[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
|
||||||
avatar = parts[1]
|
avatar = parts[1]
|
||||||
}
|
}
|
||||||
return username + "@facebook", avatar, address, nil
|
return username + "@facebook", avatar, address, nil
|
||||||
|
|||||||
@@ -268,11 +268,16 @@ func accountCreate(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.SetNodeConfig(ctx, &cfg.Node)
|
utils.SetNodeConfig(ctx, &cfg.Node)
|
||||||
scryptN, scryptP, keydir, err := cfg.Node.AccountConfig()
|
keydir, err := cfg.Node.KeyDirConfig()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read configuration: %v", err)
|
utils.Fatalf("Failed to read configuration: %v", err)
|
||||||
}
|
}
|
||||||
|
scryptN := keystore.StandardScryptN
|
||||||
|
scryptP := keystore.StandardScryptP
|
||||||
|
if cfg.Node.UseLightweightKDF {
|
||||||
|
scryptN = keystore.LightScryptN
|
||||||
|
scryptP = keystore.LightScryptP
|
||||||
|
}
|
||||||
|
|
||||||
password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ It expects the genesis file as argument.`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
Description: `
|
Description: `
|
||||||
@@ -92,11 +91,15 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to
|
|||||||
utils.MetricsHTTPFlag,
|
utils.MetricsHTTPFlag,
|
||||||
utils.MetricsPortFlag,
|
utils.MetricsPortFlag,
|
||||||
utils.MetricsEnableInfluxDBFlag,
|
utils.MetricsEnableInfluxDBFlag,
|
||||||
|
utils.MetricsEnableInfluxDBV2Flag,
|
||||||
utils.MetricsInfluxDBEndpointFlag,
|
utils.MetricsInfluxDBEndpointFlag,
|
||||||
utils.MetricsInfluxDBDatabaseFlag,
|
utils.MetricsInfluxDBDatabaseFlag,
|
||||||
utils.MetricsInfluxDBUsernameFlag,
|
utils.MetricsInfluxDBUsernameFlag,
|
||||||
utils.MetricsInfluxDBPasswordFlag,
|
utils.MetricsInfluxDBPasswordFlag,
|
||||||
utils.MetricsInfluxDBTagsFlag,
|
utils.MetricsInfluxDBTagsFlag,
|
||||||
|
utils.MetricsInfluxDBTokenFlag,
|
||||||
|
utils.MetricsInfluxDBBucketFlag,
|
||||||
|
utils.MetricsInfluxDBOrganizationFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
|
|||||||
@@ -27,10 +27,15 @@ import (
|
|||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/external"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/scwallet"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
@@ -63,7 +68,12 @@ var tomlSettings = toml.Config{
|
|||||||
return field
|
return field
|
||||||
},
|
},
|
||||||
MissingField: func(rt reflect.Type, field string) error {
|
MissingField: func(rt reflect.Type, field string) error {
|
||||||
link := ""
|
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
||||||
|
if deprecated(id) {
|
||||||
|
log.Warn("Config field is deprecated and won't have an effect", "name", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var link string
|
||||||
if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
|
if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
|
||||||
link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
|
link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
|
||||||
}
|
}
|
||||||
@@ -129,6 +139,11 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to create the protocol stack: %v", err)
|
utils.Fatalf("Failed to create the protocol stack: %v", err)
|
||||||
}
|
}
|
||||||
|
// Node doesn't by default populate account manager backends
|
||||||
|
if err := setAccountManagerBackends(stack); err != nil {
|
||||||
|
utils.Fatalf("Failed to set account manager backends: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
utils.SetEthConfig(ctx, stack, &cfg.Eth)
|
utils.SetEthConfig(ctx, stack, &cfg.Eth)
|
||||||
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
|
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
|
||||||
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
|
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
|
||||||
@@ -227,4 +242,86 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
|
|||||||
if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) {
|
if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) {
|
||||||
cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name)
|
cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
|
||||||
|
cfg.Metrics.EnableInfluxDBV2 = ctx.GlobalBool(utils.MetricsEnableInfluxDBV2Flag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(utils.MetricsInfluxDBTokenFlag.Name) {
|
||||||
|
cfg.Metrics.InfluxDBToken = ctx.GlobalString(utils.MetricsInfluxDBTokenFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(utils.MetricsInfluxDBBucketFlag.Name) {
|
||||||
|
cfg.Metrics.InfluxDBBucket = ctx.GlobalString(utils.MetricsInfluxDBBucketFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(utils.MetricsInfluxDBOrganizationFlag.Name) {
|
||||||
|
cfg.Metrics.InfluxDBOrganization = ctx.GlobalString(utils.MetricsInfluxDBOrganizationFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deprecated(field string) bool {
|
||||||
|
switch field {
|
||||||
|
case "ethconfig.Config.EVMInterpreter":
|
||||||
|
return true
|
||||||
|
case "ethconfig.Config.EWASMInterpreter":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAccountManagerBackends(stack *node.Node) error {
|
||||||
|
conf := stack.Config()
|
||||||
|
am := stack.AccountManager()
|
||||||
|
keydir := stack.KeyStoreDir()
|
||||||
|
scryptN := keystore.StandardScryptN
|
||||||
|
scryptP := keystore.StandardScryptP
|
||||||
|
if conf.UseLightweightKDF {
|
||||||
|
scryptN = keystore.LightScryptN
|
||||||
|
scryptP = keystore.LightScryptP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble the supported backends
|
||||||
|
if len(conf.ExternalSigner) > 0 {
|
||||||
|
log.Info("Using external signer", "url", conf.ExternalSigner)
|
||||||
|
if extapi, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
|
||||||
|
am.AddBackend(extapi)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error connecting to external signer: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, we're using EITHER external signer OR local signers.
|
||||||
|
// If/when we implement some form of lockfile for USB and keystore wallets,
|
||||||
|
// we can have both, but it's very confusing for the user to see the same
|
||||||
|
// accounts in both externally and locally, plus very racey.
|
||||||
|
am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP))
|
||||||
|
if conf.USB {
|
||||||
|
// Start a USB hub for Ledger hardware wallets
|
||||||
|
if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
|
||||||
|
} else {
|
||||||
|
am.AddBackend(ledgerhub)
|
||||||
|
}
|
||||||
|
// Start a USB hub for Trezor hardware wallets (HID version)
|
||||||
|
if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
|
||||||
|
} else {
|
||||||
|
am.AddBackend(trezorhub)
|
||||||
|
}
|
||||||
|
// Start a USB hub for Trezor hardware wallets (WebUSB version)
|
||||||
|
if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
|
||||||
|
} else {
|
||||||
|
am.AddBackend(trezorhub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(conf.SmartCardDaemonPath) > 0 {
|
||||||
|
// Start a smart card hub
|
||||||
|
if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err))
|
||||||
|
} else {
|
||||||
|
am.AddBackend(schub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,8 +134,6 @@ func remoteConsole(ctx *cli.Context) error {
|
|||||||
path = filepath.Join(path, "rinkeby")
|
path = filepath.Join(path, "rinkeby")
|
||||||
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
||||||
path = filepath.Join(path, "goerli")
|
path = filepath.Join(path, "goerli")
|
||||||
} else if ctx.GlobalBool(utils.CalaverasFlag.Name) {
|
|
||||||
path = filepath.Join(path, "calaveras")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
endpoint = fmt.Sprintf("%s/geth.ipc", path)
|
endpoint = fmt.Sprintf("%s/geth.ipc", path)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ at block: 0 ({{niltime}})
|
|||||||
datadir: {{.Datadir}}
|
datadir: {{.Datadir}}
|
||||||
modules: {{apis}}
|
modules: {{apis}}
|
||||||
|
|
||||||
To exit, press ctrl-d
|
To exit, press ctrl-d or type exit
|
||||||
> {{.InputLine "exit"}}
|
> {{.InputLine "exit"}}
|
||||||
`)
|
`)
|
||||||
geth.ExpectExit()
|
geth.ExpectExit()
|
||||||
@@ -149,7 +149,7 @@ at block: 0 ({{niltime}}){{if ipc}}
|
|||||||
datadir: {{datadir}}{{end}}
|
datadir: {{datadir}}{{end}}
|
||||||
modules: {{apis}}
|
modules: {{apis}}
|
||||||
|
|
||||||
To exit, press ctrl-d
|
To exit, press ctrl-d or type exit
|
||||||
> {{.InputLine "exit" }}
|
> {{.InputLine "exit" }}
|
||||||
`)
|
`)
|
||||||
attach.ExpectExit()
|
attach.ExpectExit()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -75,7 +76,6 @@ Remove blockchain and state databases`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Usage: "Inspect the storage size for each type of data in the database",
|
Usage: "Inspect the storage size for each type of data in the database",
|
||||||
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
||||||
@@ -91,7 +91,6 @@ Remove blockchain and state databases`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
dbCompactCmd = cli.Command{
|
dbCompactCmd = cli.Command{
|
||||||
@@ -105,7 +104,6 @@ Remove blockchain and state databases`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
},
|
},
|
||||||
@@ -125,7 +123,6 @@ corruption if it is aborted during execution'!`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Description: "This command looks up the specified database key from the database.",
|
Description: "This command looks up the specified database key from the database.",
|
||||||
}
|
}
|
||||||
@@ -141,7 +138,6 @@ corruption if it is aborted during execution'!`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Description: `This command deletes the specified database key from the database.
|
Description: `This command deletes the specified database key from the database.
|
||||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||||
@@ -158,7 +154,6 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Description: `This command sets a given database key to the given value.
|
Description: `This command sets a given database key to the given value.
|
||||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||||
@@ -175,7 +170,6 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Description: "This command looks up the specified database key from the database.",
|
Description: "This command looks up the specified database key from the database.",
|
||||||
}
|
}
|
||||||
@@ -191,7 +185,6 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
},
|
},
|
||||||
Description: "This command displays information about the freezer index.",
|
Description: "This command displays information about the freezer index.",
|
||||||
}
|
}
|
||||||
@@ -343,14 +336,15 @@ func dbGet(ctx *cli.Context) error {
|
|||||||
db := utils.MakeChainDatabase(ctx, stack, true)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
key, err := hexutil.Decode(ctx.Args().Get(0))
|
key, err := parseHexOrString(ctx.Args().Get(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Could not decode the key", "error", err)
|
log.Info("Could not decode the key", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := db.Get(key)
|
data, err := db.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Get operation failed", "error", err)
|
log.Info("Get operation failed", "key", fmt.Sprintf("0x%#x", key), "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("key %#x: %#x\n", key, data)
|
fmt.Printf("key %#x: %#x\n", key, data)
|
||||||
@@ -368,7 +362,7 @@ func dbDelete(ctx *cli.Context) error {
|
|||||||
db := utils.MakeChainDatabase(ctx, stack, false)
|
db := utils.MakeChainDatabase(ctx, stack, false)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
key, err := hexutil.Decode(ctx.Args().Get(0))
|
key, err := parseHexOrString(ctx.Args().Get(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Could not decode the key", "error", err)
|
log.Info("Could not decode the key", "error", err)
|
||||||
return err
|
return err
|
||||||
@@ -378,7 +372,7 @@ func dbDelete(ctx *cli.Context) error {
|
|||||||
fmt.Printf("Previous value: %#x\n", data)
|
fmt.Printf("Previous value: %#x\n", data)
|
||||||
}
|
}
|
||||||
if err = db.Delete(key); err != nil {
|
if err = db.Delete(key); err != nil {
|
||||||
log.Info("Delete operation returned an error", "error", err)
|
log.Info("Delete operation returned an error", "key", fmt.Sprintf("0x%#x", key), "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -401,7 +395,7 @@ func dbPut(ctx *cli.Context) error {
|
|||||||
data []byte
|
data []byte
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
key, err = hexutil.Decode(ctx.Args().Get(0))
|
key, err = parseHexOrString(ctx.Args().Get(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Could not decode the key", "error", err)
|
log.Info("Could not decode the key", "error", err)
|
||||||
return err
|
return err
|
||||||
@@ -507,3 +501,12 @@ func freezerInspect(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes
|
||||||
|
func parseHexOrString(str string) ([]byte, error) {
|
||||||
|
b, err := hexutil.Decode(str)
|
||||||
|
if errors.Is(err, hexutil.ErrMissingPrefix) {
|
||||||
|
return []byte(str), nil
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -137,14 +137,18 @@ func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
|
|||||||
name: name,
|
name: name,
|
||||||
geth: runGeth(t, args...),
|
geth: runGeth(t, args...),
|
||||||
}
|
}
|
||||||
// wait before we can attach to it. TODO: probe for it properly
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
var err error
|
|
||||||
ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
|
ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
|
||||||
if g.rpc, err = rpc.Dial(ipcpath); err != nil {
|
// We can't know exactly how long geth will take to start, so we try 10
|
||||||
t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
|
// times over a 5 second period.
|
||||||
|
var err error
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
if g.rpc, err = rpc.Dial(ipcpath); err == nil {
|
||||||
|
return g
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return g
|
t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initGeth(t *testing.T) string {
|
func initGeth(t *testing.T) string {
|
||||||
|
|||||||
@@ -118,13 +118,13 @@ var (
|
|||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.MinerNotifyFlag,
|
utils.MinerNotifyFlag,
|
||||||
utils.MinerGasTargetFlag,
|
utils.LegacyMinerGasTargetFlag,
|
||||||
utils.MinerGasLimitFlag,
|
utils.MinerGasLimitFlag,
|
||||||
utils.MinerGasPriceFlag,
|
utils.MinerGasPriceFlag,
|
||||||
utils.MinerEtherbaseFlag,
|
utils.MinerEtherbaseFlag,
|
||||||
utils.MinerExtraDataFlag,
|
utils.MinerExtraDataFlag,
|
||||||
utils.MinerRecommitIntervalFlag,
|
utils.MinerRecommitIntervalFlag,
|
||||||
utils.MinerNoVerfiyFlag,
|
utils.MinerNoVerifyFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
utils.NoDiscoverFlag,
|
utils.NoDiscoverFlag,
|
||||||
utils.DiscoveryV5Flag,
|
utils.DiscoveryV5Flag,
|
||||||
@@ -138,7 +138,6 @@ var (
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
@@ -148,8 +147,6 @@ var (
|
|||||||
utils.GpoPercentileFlag,
|
utils.GpoPercentileFlag,
|
||||||
utils.GpoMaxGasPriceFlag,
|
utils.GpoMaxGasPriceFlag,
|
||||||
utils.GpoIgnoreGasPriceFlag,
|
utils.GpoIgnoreGasPriceFlag,
|
||||||
utils.EWASMInterpreterFlag,
|
|
||||||
utils.EVMInterpreterFlag,
|
|
||||||
utils.MinerNotifyFullFlag,
|
utils.MinerNotifyFullFlag,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
utils.CatalystFlag,
|
utils.CatalystFlag,
|
||||||
@@ -161,12 +158,6 @@ var (
|
|||||||
utils.HTTPPortFlag,
|
utils.HTTPPortFlag,
|
||||||
utils.HTTPCORSDomainFlag,
|
utils.HTTPCORSDomainFlag,
|
||||||
utils.HTTPVirtualHostsFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
utils.LegacyRPCEnabledFlag,
|
|
||||||
utils.LegacyRPCListenAddrFlag,
|
|
||||||
utils.LegacyRPCPortFlag,
|
|
||||||
utils.LegacyRPCCORSDomainFlag,
|
|
||||||
utils.LegacyRPCVirtualHostsFlag,
|
|
||||||
utils.LegacyRPCApiFlag,
|
|
||||||
utils.GraphQLEnabledFlag,
|
utils.GraphQLEnabledFlag,
|
||||||
utils.GraphQLCORSDomainFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
utils.GraphQLVirtualHostsFlag,
|
utils.GraphQLVirtualHostsFlag,
|
||||||
@@ -182,6 +173,7 @@ var (
|
|||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.InsecureUnlockAllowedFlag,
|
utils.InsecureUnlockAllowedFlag,
|
||||||
utils.RPCGlobalGasCapFlag,
|
utils.RPCGlobalGasCapFlag,
|
||||||
|
utils.RPCGlobalEVMTimeoutFlag,
|
||||||
utils.RPCGlobalTxFeeCapFlag,
|
utils.RPCGlobalTxFeeCapFlag,
|
||||||
utils.AllowUnprotectedTxs,
|
utils.AllowUnprotectedTxs,
|
||||||
}
|
}
|
||||||
@@ -197,6 +189,10 @@ var (
|
|||||||
utils.MetricsInfluxDBUsernameFlag,
|
utils.MetricsInfluxDBUsernameFlag,
|
||||||
utils.MetricsInfluxDBPasswordFlag,
|
utils.MetricsInfluxDBPasswordFlag,
|
||||||
utils.MetricsInfluxDBTagsFlag,
|
utils.MetricsInfluxDBTagsFlag,
|
||||||
|
utils.MetricsEnableInfluxDBV2Flag,
|
||||||
|
utils.MetricsInfluxDBTokenFlag,
|
||||||
|
utils.MetricsInfluxDBBucketFlag,
|
||||||
|
utils.MetricsInfluxDBOrganizationFlag,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -276,9 +272,6 @@ func prepare(ctx *cli.Context) {
|
|||||||
case ctx.GlobalIsSet(utils.GoerliFlag.Name):
|
case ctx.GlobalIsSet(utils.GoerliFlag.Name):
|
||||||
log.Info("Starting Geth on Görli testnet...")
|
log.Info("Starting Geth on Görli testnet...")
|
||||||
|
|
||||||
case ctx.GlobalIsSet(utils.CalaverasFlag.Name):
|
|
||||||
log.Info("Starting Geth on Calaveras testnet...")
|
|
||||||
|
|
||||||
case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
|
case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
|
||||||
log.Info("Starting Geth in ephemeral dev mode...")
|
log.Info("Starting Geth in ephemeral dev mode...")
|
||||||
|
|
||||||
@@ -412,7 +405,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
|
|||||||
}
|
}
|
||||||
ethBackend, ok := backend.(*eth.EthAPIBackend)
|
ethBackend, ok := backend.(*eth.EthAPIBackend)
|
||||||
if !ok {
|
if !ok {
|
||||||
utils.Fatalf("Ethereum service not running: %v", err)
|
utils.Fatalf("Ethereum service not running")
|
||||||
}
|
}
|
||||||
// Set the gas price to the limits from the CLI and start mining
|
// Set the gas price to the limits from the CLI and start mining
|
||||||
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/state/pruner"
|
"github.com/ethereum/go-ethereum/core/state/pruner"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
@@ -232,7 +233,7 @@ func verifyState(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := snaptree.Verify(root); err != nil {
|
if err := snaptree.Verify(root); err != nil {
|
||||||
log.Error("Failed to verfiy state", "root", root, "err", err)
|
log.Error("Failed to verify state", "root", root, "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Verified the state", "root", root)
|
log.Info("Verified the state", "root", root)
|
||||||
@@ -287,7 +288,7 @@ func traverseState(ctx *cli.Context) error {
|
|||||||
accIter := trie.NewIterator(t.NodeIterator(nil))
|
accIter := trie.NewIterator(t.NodeIterator(nil))
|
||||||
for accIter.Next() {
|
for accIter.Next() {
|
||||||
accounts += 1
|
accounts += 1
|
||||||
var acc state.Account
|
var acc types.StateAccount
|
||||||
if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
|
if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
|
||||||
log.Error("Invalid account encountered during traversal", "err", err)
|
log.Error("Invalid account encountered during traversal", "err", err)
|
||||||
return err
|
return err
|
||||||
@@ -393,7 +394,7 @@ func traverseRawState(ctx *cli.Context) error {
|
|||||||
// dig into the storage trie further.
|
// dig into the storage trie further.
|
||||||
if accIter.Leaf() {
|
if accIter.Leaf() {
|
||||||
accounts += 1
|
accounts += 1
|
||||||
var acc state.Account
|
var acc types.StateAccount
|
||||||
if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
|
if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
|
||||||
log.Error("Invalid account encountered during traversal", "err", err)
|
log.Error("Invalid account encountered during traversal", "err", err)
|
||||||
return errors.New("invalid account")
|
return errors.New("invalid account")
|
||||||
|
|||||||
55
cmd/geth/testdata/vcheck/vulnerabilities.json
vendored
55
cmd/geth/testdata/vcheck/vulnerabilities.json
vendored
@@ -52,13 +52,16 @@
|
|||||||
"check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$"
|
"check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "GethCrash",
|
"name": "Geth DoS via MULMOD",
|
||||||
"uid": "GETH-2020-04",
|
"uid": "GETH-2020-04",
|
||||||
"summary": "A denial-of-service issue can be used to crash Geth nodes during block processing",
|
"summary": "A denial-of-service issue can be used to crash Geth nodes during block processing",
|
||||||
"description": "Full details to be disclosed at a later date",
|
"description": "Affected versions suffer from a vulnerability which can be exploited through the `MULMOD` operation, by specifying a modulo of `0`: `mulmod(a,b,0)`, causing a `panic` in the underlying library. \nThe crash was in the `uint256` library, where a buffer [underflowed](https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L442).\n\n\tif `d == 0`, `dLen` remains `0`\n\nand https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L451 will try to access index `[-1]`.\n\nThe `uint256` library was first merged in this [commit](https://github.com/ethereum/go-ethereum/commit/cf6674539c589f80031f3371a71c6a80addbe454), on 2020-06-08. \nExploiting this vulnerabilty would cause all vulnerable nodes to drop off the network. \n\nThe issue was brought to our attention through a [bug report](https://github.com/ethereum/go-ethereum/issues/21367), showing a `panic` occurring on sync from genesis on the Ropsten network.\n \nIt was estimated that the least obvious way to fix this would be to merge the fix into `uint256`, make a new release of that library and then update the geth-dependency.\n",
|
||||||
"links": [
|
"links": [
|
||||||
"https://blog.ethereum.org/2020/11/12/geth_security_release/",
|
"https://blog.ethereum.org/2020/11/12/geth_security_release/",
|
||||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m"
|
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m",
|
||||||
|
"https://github.com/holiman/uint256/releases/tag/v1.1.1",
|
||||||
|
"https://github.com/holiman/uint256/pull/80",
|
||||||
|
"https://github.com/ethereum/go-ethereum/pull/21368"
|
||||||
],
|
],
|
||||||
"introduced": "v1.9.16",
|
"introduced": "v1.9.16",
|
||||||
"fixed": "v1.9.18",
|
"fixed": "v1.9.18",
|
||||||
@@ -66,5 +69,51 @@
|
|||||||
"severity": "Critical",
|
"severity": "Critical",
|
||||||
"CVE": "CVE-2020-26242",
|
"CVE": "CVE-2020-26242",
|
||||||
"check": "Geth\\/v1\\.9.(16|17).*$"
|
"check": "Geth\\/v1\\.9.(16|17).*$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LES Server DoS via GetProofsV2",
|
||||||
|
"uid": "GETH-2020-05",
|
||||||
|
"summary": "A DoS vulnerability can make a LES server crash.",
|
||||||
|
"description": "A DoS vulnerability can make a LES server crash via malicious GetProofsV2 request from a connected LES client.\n\nThe vulnerability was patched in #21896.\n\nThis vulnerability only concern users explicitly running geth as a light server",
|
||||||
|
"links": [
|
||||||
|
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-r33q-22hv-j29q",
|
||||||
|
"https://github.com/ethereum/go-ethereum/pull/21896"
|
||||||
|
],
|
||||||
|
"introduced": "v1.8.0",
|
||||||
|
"fixed": "v1.9.25",
|
||||||
|
"published": "2020-12-10",
|
||||||
|
"severity": "Medium",
|
||||||
|
"CVE": "CVE-2020-26264",
|
||||||
|
"check": "(Geth\\/v1\\.8\\.*)|(Geth\\/v1\\.9\\.\\d-.*)|(Geth\\/v1\\.9\\.1\\d-.*)|(Geth\\/v1\\.9\\.(20|21|22|23|24)-.*)$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SELFDESTRUCT-recreate consensus flaw",
|
||||||
|
"uid": "GETH-2020-06",
|
||||||
|
"introduced": "v1.9.4",
|
||||||
|
"fixed": "v1.9.20",
|
||||||
|
"summary": "A consensus-vulnerability in Geth could cause a chain split, where vulnerable versions refuse to accept the canonical chain.",
|
||||||
|
"description": "A flaw was repoted at 2020-08-11 by John Youngseok Yang (Software Platform Lab), where a particular sequence of transactions could cause a consensus failure.\n\n- Tx 1:\n - `sender` invokes `caller`.\n - `caller` invokes `0xaa`. `0xaa` has 3 wei, does a self-destruct-to-self\n - `caller` does a `1 wei` -call to `0xaa`, who thereby has 1 wei (the code in `0xaa` still executed, since the tx is still ongoing, but doesn't redo the selfdestruct, it takes a different path if callvalue is non-zero)\n\n-Tx 2:\n - `sender` does a 5-wei call to 0xaa. No exec (since no code). \n\nIn geth, the result would be that `0xaa` had `6 wei`, whereas OE reported (correctly) `5` wei. Furthermore, in geth, if the second tx was not executed, the `0xaa` would be destructed, resulting in `0 wei`. Thus obviously wrong. \n\nIt was determined that the root cause was this [commit](https://github.com/ethereum/go-ethereum/commit/223b950944f494a5b4e0957fd9f92c48b09037ad) from [this PR](https://github.com/ethereum/go-ethereum/pull/19953). The semantics of `createObject` was subtly changd, into returning a non-nil object (with `deleted=true`) where it previously did not if the account had been destructed. This return value caused the new object to inherit the old `balance`.\n",
|
||||||
|
"links": [
|
||||||
|
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-xw37-57qp-9mm4"
|
||||||
|
],
|
||||||
|
"published": "2020-12-10",
|
||||||
|
"severity": "High",
|
||||||
|
"CVE": "CVE-2020-26265",
|
||||||
|
"check": "(Geth\\/v1\\.9\\.(4|5|6|7|8|9)-.*)|(Geth\\/v1\\.9\\.1\\d-.*)$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Not ready for London upgrade",
|
||||||
|
"uid": "GETH-2021-01",
|
||||||
|
"summary": "The client is not ready for the 'London' technical upgrade, and will deviate from the canonical chain when the London upgrade occurs (at block '12965000' around August 4, 2021.",
|
||||||
|
"description": "At (or around) August 4, Ethereum will undergo a technical upgrade called 'London'. Clients not upgraded will fail to progress on the canonical chain.",
|
||||||
|
"links": [
|
||||||
|
"https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md",
|
||||||
|
"https://notes.ethereum.org/@timbeiko/ropsten-postmortem"
|
||||||
|
],
|
||||||
|
"introduced": "v1.10.1",
|
||||||
|
"fixed": "v1.10.6",
|
||||||
|
"published": "2020-12-10",
|
||||||
|
"severity": "High",
|
||||||
|
"check": "(Geth\\/v1\\.10\\.(1|2|3|4|5)-.*)$"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
utils.MainnetFlag,
|
utils.MainnetFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.CalaverasFlag,
|
|
||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.ExitWhenSyncedFlag,
|
utils.ExitWhenSyncedFlag,
|
||||||
@@ -151,6 +150,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
utils.GraphQLCORSDomainFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
utils.GraphQLVirtualHostsFlag,
|
utils.GraphQLVirtualHostsFlag,
|
||||||
utils.RPCGlobalGasCapFlag,
|
utils.RPCGlobalGasCapFlag,
|
||||||
|
utils.RPCGlobalEVMTimeoutFlag,
|
||||||
utils.RPCGlobalTxFeeCapFlag,
|
utils.RPCGlobalTxFeeCapFlag,
|
||||||
utils.AllowUnprotectedTxs,
|
utils.AllowUnprotectedTxs,
|
||||||
utils.JSpathFlag,
|
utils.JSpathFlag,
|
||||||
@@ -182,12 +182,11 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
utils.MinerNotifyFlag,
|
utils.MinerNotifyFlag,
|
||||||
utils.MinerNotifyFullFlag,
|
utils.MinerNotifyFullFlag,
|
||||||
utils.MinerGasPriceFlag,
|
utils.MinerGasPriceFlag,
|
||||||
utils.MinerGasTargetFlag,
|
|
||||||
utils.MinerGasLimitFlag,
|
utils.MinerGasLimitFlag,
|
||||||
utils.MinerEtherbaseFlag,
|
utils.MinerEtherbaseFlag,
|
||||||
utils.MinerExtraDataFlag,
|
utils.MinerExtraDataFlag,
|
||||||
utils.MinerRecommitIntervalFlag,
|
utils.MinerRecommitIntervalFlag,
|
||||||
utils.MinerNoVerfiyFlag,
|
utils.MinerNoVerifyFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -203,8 +202,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
Name: "VIRTUAL MACHINE",
|
Name: "VIRTUAL MACHINE",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
utils.EVMInterpreterFlag,
|
|
||||||
utils.EWASMInterpreterFlag,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -222,12 +219,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
Name: "ALIASED (deprecated)",
|
Name: "ALIASED (deprecated)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.LegacyRPCEnabledFlag,
|
|
||||||
utils.LegacyRPCListenAddrFlag,
|
|
||||||
utils.LegacyRPCPortFlag,
|
|
||||||
utils.LegacyRPCCORSDomainFlag,
|
|
||||||
utils.LegacyRPCVirtualHostsFlag,
|
|
||||||
utils.LegacyRPCApiFlag,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -482,7 +482,7 @@ ADD puppeth.png /dashboard/puppeth.png
|
|||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
CMD ["node", "/server.js"]
|
CMD ["node", "./server.js"]
|
||||||
`
|
`
|
||||||
|
|
||||||
// dashboardComposefile is the docker-compose.yml file required to deploy and
|
// dashboardComposefile is the docker-compose.yml file required to deploy and
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) {
|
|||||||
if port != 80 && port != 443 {
|
if port != 80 && port != 443 {
|
||||||
config += fmt.Sprintf(":%d", port)
|
config += fmt.Sprintf(":%d", port)
|
||||||
}
|
}
|
||||||
// Retrieve the IP blacklist
|
// Retrieve the IP banned list
|
||||||
banned := strings.Split(infos.envvars["BANNED"], ",")
|
banned := strings.Split(infos.envvars["BANNED"], ",")
|
||||||
|
|
||||||
// Run a sanity check to see if the port is reachable
|
// Run a sanity check to see if the port is reachable
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ FROM puppeth/blockscout:latest
|
|||||||
ADD genesis.json /genesis.json
|
ADD genesis.json /genesis.json
|
||||||
RUN \
|
RUN \
|
||||||
echo 'geth --cache 512 init /genesis.json' > explorer.sh && \
|
echo 'geth --cache 512 init /genesis.json' > explorer.sh && \
|
||||||
echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,shh,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \
|
echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug,txpool" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \
|
||||||
echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,shh,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \
|
echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug,txpool" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \
|
||||||
echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \
|
echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \
|
||||||
echo 'sleep 5' >> explorer.sh && \
|
echo 'sleep 5' >> explorer.sh && \
|
||||||
echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \
|
echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -32,8 +31,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/console/prompt"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/peterh/liner"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,17 +77,27 @@ type wizard struct {
|
|||||||
servers map[string]*sshClient // SSH connections to servers to administer
|
servers map[string]*sshClient // SSH connections to servers to administer
|
||||||
services map[string][]string // Ethereum services known to be running on servers
|
services map[string][]string // Ethereum services known to be running on servers
|
||||||
|
|
||||||
in *bufio.Reader // Wrapper around stdin to allow reading user input
|
lock sync.Mutex // Lock to protect configs during concurrent service discovery
|
||||||
lock sync.Mutex // Lock to protect configs during concurrent service discovery
|
}
|
||||||
|
|
||||||
|
// prompts the user for input with the given prompt string. Returns when a value is entered.
|
||||||
|
// Causes the wizard to exit if ctrl-d is pressed
|
||||||
|
func promptInput(p string) string {
|
||||||
|
for {
|
||||||
|
text, err := prompt.Stdin.PromptInput(p)
|
||||||
|
if err != nil {
|
||||||
|
if err != liner.ErrPromptAborted {
|
||||||
|
log.Crit("Failed to read user input", "err", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read reads a single line from stdin, trimming if from spaces.
|
// read reads a single line from stdin, trimming if from spaces.
|
||||||
func (w *wizard) read() string {
|
func (w *wizard) read() string {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(text)
|
return strings.TrimSpace(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,11 +105,7 @@ func (w *wizard) read() string {
|
|||||||
// non-emptyness.
|
// non-emptyness.
|
||||||
func (w *wizard) readString() string {
|
func (w *wizard) readString() string {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text != "" {
|
if text = strings.TrimSpace(text); text != "" {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
@@ -108,11 +115,7 @@ func (w *wizard) readString() string {
|
|||||||
// readDefaultString reads a single line from stdin, trimming if from spaces. If
|
// readDefaultString reads a single line from stdin, trimming if from spaces. If
|
||||||
// an empty line is entered, the default value is returned.
|
// an empty line is entered, the default value is returned.
|
||||||
func (w *wizard) readDefaultString(def string) string {
|
func (w *wizard) readDefaultString(def string) string {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text != "" {
|
if text = strings.TrimSpace(text); text != "" {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
@@ -124,11 +127,7 @@ func (w *wizard) readDefaultString(def string) string {
|
|||||||
// value is returned.
|
// value is returned.
|
||||||
func (w *wizard) readDefaultYesNo(def bool) bool {
|
func (w *wizard) readDefaultYesNo(def bool) bool {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
|
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
@@ -146,11 +145,7 @@ func (w *wizard) readDefaultYesNo(def bool) bool {
|
|||||||
// interpret it as a URL (http, https or file).
|
// interpret it as a URL (http, https or file).
|
||||||
func (w *wizard) readURL() *url.URL {
|
func (w *wizard) readURL() *url.URL {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
uri, err := url.Parse(strings.TrimSpace(text))
|
uri, err := url.Parse(strings.TrimSpace(text))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Invalid input, expected URL", "err", err)
|
log.Error("Invalid input, expected URL", "err", err)
|
||||||
@@ -164,11 +159,7 @@ func (w *wizard) readURL() *url.URL {
|
|||||||
// to parse into an integer.
|
// to parse into an integer.
|
||||||
func (w *wizard) readInt() int {
|
func (w *wizard) readInt() int {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -186,11 +177,7 @@ func (w *wizard) readInt() int {
|
|||||||
// returned.
|
// returned.
|
||||||
func (w *wizard) readDefaultInt(def int) int {
|
func (w *wizard) readDefaultInt(def int) int {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
@@ -208,11 +195,7 @@ func (w *wizard) readDefaultInt(def int) int {
|
|||||||
// default value is returned.
|
// default value is returned.
|
||||||
func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int {
|
func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
@@ -225,38 +208,11 @@ func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// readFloat reads a single line from stdin, trimming if from spaces, enforcing it
|
|
||||||
// to parse into a float.
|
|
||||||
func (w *wizard) readFloat() float64 {
|
|
||||||
for {
|
|
||||||
fmt.Printf("> ")
|
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Invalid input, expected float", "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing
|
// readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing
|
||||||
// it to parse into a float. If an empty line is entered, the default value is returned.
|
// it to parse into a float. If an empty line is entered, the default value is returned.
|
||||||
func (w *wizard) readDefaultFloat(def float64) float64 {
|
func (w *wizard) readDefaultFloat(def float64) float64 {
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
@@ -285,12 +241,7 @@ func (w *wizard) readPassword() string {
|
|||||||
// it to an Ethereum address.
|
// it to an Ethereum address.
|
||||||
func (w *wizard) readAddress() *common.Address {
|
func (w *wizard) readAddress() *common.Address {
|
||||||
for {
|
for {
|
||||||
// Read the address from the user
|
text := promptInput("> 0x")
|
||||||
fmt.Printf("> 0x")
|
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -311,11 +262,7 @@ func (w *wizard) readAddress() *common.Address {
|
|||||||
func (w *wizard) readDefaultAddress(def common.Address) common.Address {
|
func (w *wizard) readDefaultAddress(def common.Address) common.Address {
|
||||||
for {
|
for {
|
||||||
// Read the address from the user
|
// Read the address from the user
|
||||||
fmt.Printf("> 0x")
|
text := promptInput("> 0x")
|
||||||
text, err := w.in.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
@@ -334,8 +281,9 @@ func (w *wizard) readJSON() string {
|
|||||||
var blob json.RawMessage
|
var blob json.RawMessage
|
||||||
|
|
||||||
for {
|
for {
|
||||||
fmt.Printf("> ")
|
text := promptInput("> ")
|
||||||
if err := json.NewDecoder(w.in).Decode(&blob); err != nil {
|
reader := strings.NewReader(text)
|
||||||
|
if err := json.NewDecoder(reader).Decode(&blob); err != nil {
|
||||||
log.Error("Invalid JSON, please try again", "err", err)
|
log.Error("Invalid JSON, please try again", "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -351,10 +299,7 @@ func (w *wizard) readIPAddress() string {
|
|||||||
for {
|
for {
|
||||||
// Read the IP address from the user
|
// Read the IP address from the user
|
||||||
fmt.Printf("> ")
|
fmt.Printf("> ")
|
||||||
text, err := w.in.ReadString('\n')
|
text := promptInput("> ")
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read user input", "err", err)
|
|
||||||
}
|
|
||||||
if text = strings.TrimSpace(text); text == "" {
|
if text = strings.TrimSpace(text); text == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,20 +63,20 @@ func (w *wizard) deployEthstats() {
|
|||||||
fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret)
|
fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret)
|
||||||
infos.secret = w.readDefaultString(infos.secret)
|
infos.secret = w.readDefaultString(infos.secret)
|
||||||
}
|
}
|
||||||
// Gather any blacklists to ban from reporting
|
// Gather any banned lists to ban from reporting
|
||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
|
fmt.Printf("Keep existing IP %v in the banned list (y/n)? (default = yes)\n", infos.banned)
|
||||||
if !w.readDefaultYesNo(true) {
|
if !w.readDefaultYesNo(true) {
|
||||||
// The user might want to clear the entire list, although generally probably not
|
// The user might want to clear the entire list, although generally probably not
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
|
fmt.Printf("Clear out the banned list and start over (y/n)? (default = no)\n")
|
||||||
if w.readDefaultYesNo(false) {
|
if w.readDefaultYesNo(false) {
|
||||||
infos.banned = nil
|
infos.banned = nil
|
||||||
}
|
}
|
||||||
// Offer the user to explicitly add/remove certain IP addresses
|
// Offer the user to explicitly add/remove certain IP addresses
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Which additional IP addresses should be blacklisted?")
|
fmt.Println("Which additional IP addresses should be in the banned list?")
|
||||||
for {
|
for {
|
||||||
if ip := w.readIPAddress(); ip != "" {
|
if ip := w.readIPAddress(); ip != "" {
|
||||||
infos.banned = append(infos.banned, ip)
|
infos.banned = append(infos.banned, ip)
|
||||||
@@ -85,7 +85,7 @@ func (w *wizard) deployEthstats() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Which IP addresses should not be blacklisted?")
|
fmt.Println("Which IP addresses should not be in the banned list?")
|
||||||
for {
|
for {
|
||||||
if ip := w.readIPAddress(); ip != "" {
|
if ip := w.readIPAddress(); ip != "" {
|
||||||
for i, addr := range infos.banned {
|
for i, addr := range infos.banned {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -38,7 +37,6 @@ func makeWizard(network string) *wizard {
|
|||||||
},
|
},
|
||||||
servers: make(map[string]*sshClient),
|
servers: make(map[string]*sshClient),
|
||||||
services: make(map[string][]string),
|
services: make(map[string][]string),
|
||||||
in: bufio.NewReader(os.Stdin),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"container/list"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -26,18 +28,20 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hexMode = flag.String("hex", "", "dump given hex data")
|
hexMode = flag.String("hex", "", "dump given hex data")
|
||||||
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
|
reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
|
||||||
single = flag.Bool("single", false, "print only the first element, discard the rest")
|
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
|
||||||
|
single = flag.Bool("single", false, "print only the first element, discard the rest")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>] [filename]")
|
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>][-reverse] [filename]")
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
fmt.Fprintln(os.Stderr, `
|
fmt.Fprintln(os.Stderr, `
|
||||||
Dumps RLP data from the given file in readable form.
|
Dumps RLP data from the given file in readable form.
|
||||||
@@ -73,23 +77,40 @@ func main() {
|
|||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
out := os.Stdout
|
||||||
s := rlp.NewStream(r, 0)
|
if *reverseMode {
|
||||||
for {
|
data, err := textToRlp(r)
|
||||||
if err := dump(s, 0); err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
die(err)
|
||||||
die(err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Printf("0x%x\n", data)
|
||||||
if *single {
|
return
|
||||||
break
|
} else {
|
||||||
|
err := rlpToText(r, out)
|
||||||
|
if err != nil {
|
||||||
|
die(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dump(s *rlp.Stream, depth int) error {
|
func rlpToText(r io.Reader, out io.Writer) error {
|
||||||
|
s := rlp.NewStream(r, 0)
|
||||||
|
for {
|
||||||
|
if err := dump(s, 0, out); err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Fprintln(out)
|
||||||
|
if *single {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dump(s *rlp.Stream, depth int, out io.Writer) error {
|
||||||
kind, size, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -101,28 +122,28 @@ func dump(s *rlp.Stream, depth int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(str) == 0 || !*noASCII && isASCII(str) {
|
if len(str) == 0 || !*noASCII && isASCII(str) {
|
||||||
fmt.Printf("%s%q", ws(depth), str)
|
fmt.Fprintf(out, "%s%q", ws(depth), str)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s%x", ws(depth), str)
|
fmt.Fprintf(out, "%s%x", ws(depth), str)
|
||||||
}
|
}
|
||||||
case rlp.List:
|
case rlp.List:
|
||||||
s.List()
|
s.List()
|
||||||
defer s.ListEnd()
|
defer s.ListEnd()
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
fmt.Print(ws(depth) + "[]")
|
fmt.Fprintf(out, ws(depth)+"[]")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(ws(depth) + "[")
|
fmt.Fprintln(out, ws(depth)+"[")
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
fmt.Print(",\n")
|
fmt.Fprint(out, ",\n")
|
||||||
}
|
}
|
||||||
if err := dump(s, depth+1); err == rlp.EOL {
|
if err := dump(s, depth+1, out); err == rlp.EOL {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Print(ws(depth) + "]")
|
fmt.Fprint(out, ws(depth)+"]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -145,3 +166,45 @@ func die(args ...interface{}) {
|
|||||||
fmt.Fprintln(os.Stderr, args...)
|
fmt.Fprintln(os.Stderr, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// textToRlp converts text into RLP (best effort).
|
||||||
|
func textToRlp(r io.Reader) ([]byte, error) {
|
||||||
|
// We're expecting the input to be well-formed, meaning that
|
||||||
|
// - each element is on a separate line
|
||||||
|
// - each line is either an (element OR a list start/end) + comma
|
||||||
|
// - an element is either hex-encoded bytes OR a quoted string
|
||||||
|
var (
|
||||||
|
scanner = bufio.NewScanner(r)
|
||||||
|
obj []interface{}
|
||||||
|
stack = list.New()
|
||||||
|
)
|
||||||
|
for scanner.Scan() {
|
||||||
|
t := strings.TrimSpace(scanner.Text())
|
||||||
|
if len(t) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case "[": // list start
|
||||||
|
stack.PushFront(obj)
|
||||||
|
obj = make([]interface{}, 0)
|
||||||
|
case "]", "],": // list end
|
||||||
|
parent := stack.Remove(stack.Front()).([]interface{})
|
||||||
|
obj = append(parent, obj)
|
||||||
|
case "[],": // empty list
|
||||||
|
obj = append(obj, make([]interface{}, 0))
|
||||||
|
default: // element
|
||||||
|
data := []byte(t)[:len(t)-1] // cut off comma
|
||||||
|
if data[0] == '"' { // ascii string
|
||||||
|
data = []byte(t)[1 : len(data)-1]
|
||||||
|
} else { // hex data
|
||||||
|
data = common.FromHex(string(data))
|
||||||
|
}
|
||||||
|
obj = append(obj, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := rlp.EncodeToBytes(obj[0])
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|||||||
65
cmd/rlpdump/rlpdump_test.go
Normal file
65
cmd/rlpdump/rlpdump_test.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoundtrip(t *testing.T) {
|
||||||
|
for i, want := range []string{
|
||||||
|
"0xf880806482520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0a1010000000000000000000000000000000000000000000000000000000000000001801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28",
|
||||||
|
"0xd5c0d3cb84746573742a2a808213378667617a6f6e6b",
|
||||||
|
"0xc780c0c1c0825208",
|
||||||
|
} {
|
||||||
|
var out strings.Builder
|
||||||
|
err := rlpToText(bytes.NewReader(common.FromHex(want)), &out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
text := out.String()
|
||||||
|
rlpBytes, err := textToRlp(strings.NewReader(text))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
have := fmt.Sprintf("0x%x", rlpBytes)
|
||||||
|
if have != want {
|
||||||
|
t.Errorf("test %d: have\n%v\nwant:\n%v\n", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextToRlp(t *testing.T) {
|
||||||
|
type tc struct {
|
||||||
|
text string
|
||||||
|
want string
|
||||||
|
}
|
||||||
|
cases := []tc{
|
||||||
|
{
|
||||||
|
text: `[
|
||||||
|
"",
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
],
|
||||||
|
5208,
|
||||||
|
]`,
|
||||||
|
want: "0xc780c0c1c0825208",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range cases {
|
||||||
|
have, err := textToRlp(strings.NewReader(tc.text))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if hexutil.Encode(have) != tc.want {
|
||||||
|
t.Errorf("test %d:\nhave %v\nwant %v", i, hexutil.Encode(have), tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user