Patch for concurrent iterator & others (onto v1.11.6) #386

Closed
roysc wants to merge 1565 commits from v1.11.6-statediff-v5 into master
1471 changed files with 250382 additions and 192719 deletions

7
.github/CODEOWNERS vendored
View File

@ -5,16 +5,15 @@ accounts/usbwallet @karalabe
accounts/scwallet @gballet accounts/scwallet @gballet
accounts/abi @gballet @MariusVanDerWijden accounts/abi @gballet @MariusVanDerWijden
cmd/clef @holiman cmd/clef @holiman
cmd/puppeth @karalabe
consensus @karalabe consensus @karalabe
core/ @karalabe @holiman @rjl493456442 core/ @karalabe @holiman @rjl493456442
eth/ @karalabe @holiman @rjl493456442 eth/ @karalabe @holiman @rjl493456442
eth/catalyst/ @gballet eth/catalyst/ @gballet
graphql/ @gballet eth/tracers/ @s1na
graphql/ @s1na
les/ @zsfelfoldi @rjl493456442 les/ @zsfelfoldi @rjl493456442
light/ @zsfelfoldi @rjl493456442 light/ @zsfelfoldi @rjl493456442
mobile/ @karalabe @ligi node/ @fjl
node/ @fjl @renaynay
p2p/ @fjl @zsfelfoldi p2p/ @fjl @zsfelfoldi
rpc/ @fjl @holiman rpc/ @fjl @holiman
p2p/simulations @fjl p2p/simulations @fjl

View File

@ -35,6 +35,6 @@ and help.
## Configuration, dependencies, and tests ## Configuration, dependencies, and tests
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide) Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/geth-developer/dev-guide)
for more details on configuring your environment, managing project dependencies for more details on configuring your environment, managing project dependencies
and testing procedures. and testing procedures.

View File

@ -9,6 +9,7 @@ assignees: ''
#### System information #### System information
Geth version: `geth version` Geth version: `geth version`
CL client & version: e.g. lighthouse/nimbus/prysm@v1.0.0
OS & Version: Windows/Linux/OSX OS & Version: Windows/Linux/OSX
Commit hash : (if `develop`) Commit hash : (if `develop`)
@ -27,4 +28,4 @@ Commit hash : (if `develop`)
[backtrace] [backtrace]
```` ````
When submitting logs: please submit them as text and not screenshots. When submitting logs: please submit them as text and not screenshots.

15
.github/workflows/checks.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: checks
on: [pull_request]
jobs:
linter-check:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: "1.19"
check-latest: true
- uses: actions/checkout@v2
- name: Run linter
run: go run build/ci.go lint

View File

@ -0,0 +1,21 @@
name: MANUAL Override Publish geth binary to release
on:
workflow_dispatch:
inputs:
giteaPublishTag:
description: 'Package to publish TO on gitea; e.g. v1.10.25-statediff-4.2.1-alpha'
required: true
cercContainerTag:
description: 'Tagged Container to extract geth binary FROM'
required: true
jobs:
build:
name: Manual override publish of geth binary FROM tagged release TO TAGGED package on git.vdb.to
runs-on: ubuntu-latest
steps:
- name: Copy ethereum binary file
run: docker run --rm --entrypoint cat git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{ github.event.inputs.cercContainerTag }} /usr/local/bin/geth > geth-linux-amd64
- name: curl
uses: enflo/curl-action@master
with:
curl: --user cerccicd:${{ secrets.GITEA_TOKEN }} --upload-file geth-linux-amd64 https://git.vdb.to/api/packages/cerc-io/generic/go-ethereum/${{ github.event.inputs.giteaPublishTag }}/geth-linux-amd64

21
.github/workflows/manual_publish.yaml vendored Normal file
View File

@ -0,0 +1,21 @@
name: MANUAL Override Publish geth binary to release
on:
workflow_dispatch:
inputs:
giteaPublishTag:
description: 'Package to publish TO on gitea; e.g. v1.10.25-statediff-4.2.1-alpha'
required: true
cercContainerTag:
description: 'Tagged Container to extract geth binary FROM'
required: true
jobs:
build:
name: Manual override publish of geth binary FROM tagged release TO TAGGED package on git.vdb.to
runs-on: ubuntu-latest
steps:
- name: Copy ethereum binary file
run: docker run --rm --entrypoint cat git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{ github.event.inputs.cercContainerTag }} /usr/local/bin/geth > geth-linux-amd64
- name: curl
uses: enflo/curl-action@master
with:
curl: --user circcicd:${{ secrets.GITEA_TOKEN }} --upload-file geth-linux-amd64 https://git.vdb.to/api/packages/cerc-io/generic/go-ethereum/${{ github.event.inputs.giteaPublishTag }}/geth-linux-amd64

7
.github/workflows/on-pr.yml vendored Normal file
View File

@ -0,0 +1,7 @@
name: Build and test
on: [pull_request]
jobs:
run-tests:
uses: ./.github/workflows/tests.yml

36
.github/workflows/publish.yaml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Publish geth to release
on:
release:
types: [published]
jobs:
run-tests:
uses: ./.github/workflows/tests.yml
build:
name: Run docker build and publish
needs: run-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run docker build
run: docker build -t cerc-io/go-ethereum -f Dockerfile .
- name: Get the version
id: vars
run: |
echo ::set-output name=sha::$(echo ${GITHUB_SHA:0:7})
echo ::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})
- name: Tag docker image
run: docker tag cerc-io/go-ethereum git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{steps.vars.outputs.sha}}
- name: Tag docker image
run: docker tag git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{steps.vars.outputs.sha}} git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{steps.vars.outputs.tag}}
- name: Docker Login
run: echo ${{ secrets.GITEA_TOKEN }} | docker login https://git.vdb.to -u cerccicd --password-stdin
- name: Docker Push
run: docker push git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{steps.vars.outputs.sha}}
- name: Docker Push TAGGED
run: docker push git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{steps.vars.outputs.tag}}
- name: Copy ethereum binary file
run: docker run --rm --entrypoint cat git.vdb.to/cerc-io/go-ethereum/go-ethereum:${{steps.vars.outputs.sha}} /usr/local/bin/geth > geth-linux-amd64
- name: curl
uses: enflo/curl-action@master
with:
curl: --user cerccicd:${{ secrets.GITEA_TOKEN }} --upload-file geth-linux-amd64 https://git.vdb.to/api/packages/cerc-io/generic/go-ethereum/${{steps.vars.outputs.tag}}/geth-linux-amd64

139
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,139 @@
name: Tests for Geth that are used in multiple jobs.
on:
workflow_call:
env:
stack-orchestrator-ref: ${{ github.event.inputs.stack-orchestrator-ref || 'e62830c982d4dfc5f3c1c2b12c1754a7e9b538f1'}}
ipld-eth-db-ref: ${{ github.event.inputs.ipld-eth-db-ref || '66cd1d9e696cfa72f9d272927f9e945905c9f093' }}
GOPATH: /tmp/go
jobs:
build:
name: Run docker build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run docker build
run: docker build -t cerc-io/go-ethereum .
geth-unit-test:
name: Run geth unit test
runs-on: ubuntu-latest
env:
GO111MODULE: on
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- uses: actions/setup-go@v3
with:
go-version: "1.19"
check-latest: true
- name: Checkout code
uses: actions/checkout@v2
- name: Run unit tests
run: |
make test
statediff-unit-test:
name: Run state diff unit test
runs-on: ubuntu-latest
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- uses: actions/setup-go@v3
with:
go-version: "1.19"
check-latest: true
- name: Checkout code
uses: actions/checkout@v2
- name: Run docker compose
run: |
docker-compose up -d
- name: Give the migration a few seconds
run: sleep 30;
- name: Run unit tests
run: make statedifftest
private-network-test:
name: Start Geth in a private network.
runs-on: ubuntu-latest
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- uses: actions/setup-go@v3
with:
go-version: "1.19"
check-latest: true
- name: Checkout code
uses: actions/checkout@v3
with:
path: "./go-ethereum"
- uses: actions/checkout@v3
with:
ref: ${{ env.stack-orchestrator-ref }}
path: "./stack-orchestrator/"
repository: cerc-io/mshaw_stack_hack
fetch-depth: 0
- uses: actions/checkout@v3
with:
ref: ${{ env.ipld-eth-db-ref }}
repository: cerc-io/ipld-eth-db
path: "./ipld-eth-db/"
fetch-depth: 0
- name: Create config file
run: |
echo vulcanize_ipld_eth_db=$GITHUB_WORKSPACE/ipld-eth-db/ > $GITHUB_WORKSPACE/config.sh
echo vulcanize_go_ethereum=$GITHUB_WORKSPACE/go-ethereum/ >> $GITHUB_WORKSPACE/config.sh
echo db_write=true >> $GITHUB_WORKSPACE/config.sh
echo genesis_file_path=start-up-files/go-ethereum/genesis.json >> $GITHUB_WORKSPACE/config.sh
cat $GITHUB_WORKSPACE/config.sh
- name: Compile Geth
run: |
cd $GITHUB_WORKSPACE/stack-orchestrator/helper-scripts
./compile-geth.sh -e docker -p $GITHUB_WORKSPACE/config.sh
cd -
- name: Run docker compose
run: |
docker-compose \
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" \
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" \
--env-file $GITHUB_WORKSPACE/config.sh \
up -d --build
- name: Make sure the /root/transaction_info/STATEFUL_TEST_DEPLOYED_ADDRESS exists within a certain time frame.
shell: bash
run: |
COUNT=0
ATTEMPTS=15
docker logs local_go-ethereum_1
docker compose -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" exec go-ethereum ps aux
until $(docker compose -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" cp go-ethereum:/root/transaction_info/STATEFUL_TEST_DEPLOYED_ADDRESS ./STATEFUL_TEST_DEPLOYED_ADDRESS) || [[ $COUNT -eq $ATTEMPTS ]]; do echo -e "$(( COUNT++ ))... \c"; sleep 10; done
[[ $COUNT -eq $ATTEMPTS ]] && echo "Could not find the successful contract deployment" && (exit 1)
cat ./STATEFUL_TEST_DEPLOYED_ADDRESS
echo "Address length: `wc ./STATEFUL_TEST_DEPLOYED_ADDRESS`"
sleep 15;
- name: Create a new transaction.
shell: bash
run: |
docker logs local_go-ethereum_1
docker compose -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" exec go-ethereum /bin/bash /root/transaction_info/NEW_TRANSACTION
echo $?
- name: Make sure we see entries in the header table
shell: bash
run: |
rows=$(docker compose -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" exec ipld-eth-db psql -U vdbm -d vulcanize_testing -AXqtc "SELECT COUNT(*) FROM eth.header_cids")
[[ "$rows" -lt "1" ]] && echo "We could not find any rows in postgres table." && (exit 1)
echo $rows
docker compose -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" -f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" exec ipld-eth-db psql -U vdbm -d vulcanize_testing -AXqtc "SELECT * FROM eth.header_cids"

16
.gitignore vendored
View File

@ -47,3 +47,19 @@ profile.cov
/dashboard/assets/package-lock.json /dashboard/assets/package-lock.json
**/yarn-error.log **/yarn-error.log
logs/
foundry/deployments/local-private-network/geth-linux-amd64
foundry/projects/local-private-network/geth-linux-amd64
# Helpful repos
related-repositories/foundry-test/**
related-repositories/hive/**
related-repositories/ipld-eth-db/**
related-repositories/foundry-test/
related-repositories/ipld-eth-db/
# files generated by statediffing tests
statediff/indexer/database/sql/statediffing_test_file.sql
statediff/statediffing_test_file.sql
statediff/known_gaps.sql
statediff/indexer/database/file/statediffing_test

5
.gitmodules vendored
View File

@ -1,3 +1,8 @@
[submodule "tests"] [submodule "tests"]
path = tests/testdata path = tests/testdata
url = https://github.com/ethereum/tests url = https://github.com/ethereum/tests
shallow = true
[submodule "evm-benchmarks"]
path = tests/evm-benchmarks
url = https://github.com/ipsilon/evm-benchmarks
shallow = true

View File

@ -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: 20m
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$
@ -12,17 +12,29 @@ run:
linters: linters:
disable-all: true disable-all: true
enable: enable:
- deadcode
- goconst - goconst
- goimports - goimports
- gosimple - gosimple
- govet - govet
- ineffassign - ineffassign
- misspell - misspell
# - staticcheck
- unconvert - unconvert
# - unused - typecheck
- varcheck - unused
- staticcheck
- bidichk
- durationcheck
- exportloopref
- whitespace
# - structcheck # lots of false positives
# - errcheck #lot of false positives
# - contextcheck
# - errchkjson # lots of false positives
# - errorlint # this check crashes
# - exhaustive # silly check
# - makezero # false positives
# - nilerr # several intentional
linters-settings: linters-settings:
gofmt: gofmt:
@ -33,18 +45,20 @@ linters-settings:
issues: issues:
exclude-rules: exclude-rules:
- path: crypto/blake2b/ - path: crypto/bn256/cloudflare/optate.go
linters:
- deadcode
- path: crypto/bn256/cloudflare
linters:
- deadcode
- path: p2p/discv5/
linters:
- deadcode
- path: core/vm/instructions_test.go
linters:
- goconst
- path: cmd/faucet/
linters: linters:
- deadcode - deadcode
- staticcheck
- path: internal/build/pgp.go
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
- path: core/vm/contracts.go
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
- path: accounts/usbwallet/trezor.go
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
- path: accounts/usbwallet/trezor/
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
exclude:
- 'SA1019: event.TypeMux is deprecated: use Feed'
- 'SA1019: strings.Title is deprecated'
- 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.'
- 'SA1029: should not use built-in type string as key for value'

294
.mailmap
View File

@ -1,123 +1,237 @@
Jeffrey Wilcke <jeffrey@ethereum.org> Aaron Buchwald <aaron.buchwald56@gmail.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
Viktor Trón <viktor.tron@gmail.com> Aaron Kumavis <kumavis@users.noreply.github.com>
Joseph Goulden <joegoulden@gmail.com> Abel Nieto <abel.nieto90@gmail.com>
Abel Nieto <abel.nieto90@gmail.com> <anietoro@uwaterloo.ca>
Nick Savers <nicksavers@gmail.com> Afri Schoedon <58883403+q9f@users.noreply.github.com>
Afri Schoedon <5chdn@users.noreply.github.com> <58883403+q9f@users.noreply.github.com>
Maran Hidskes <maran.hidskes@gmail.com> Alec Perseghin <aperseghin@gmail.com>
Taylor Gerring <taylor.gerring@gmail.com> Aleksey Smyrnov <i@soar.name>
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
Alex Leverington <alex@ethdev.com>
Alex Leverington <alex@ethdev.com> <subtly@users.noreply.github.com>
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com>
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com> <leshiy12345678@gmail.com>
Alexey Akhunov <akhounov@gmail.com>
Alon Muroch <alonmuroch@gmail.com>
Andrey Petrov <shazow@gmail.com>
Andrey Petrov <shazow@gmail.com> <andrey.petrov@shazow.net>
Arkadiy Paronyan <arkadiy@ethdev.com>
Armin Braun <me@obrown.io>
Aron Fischer <github@aron.guru> <homotopycolimit@users.noreply.github.com>
Austin Roberts <code@ausiv.com>
Austin Roberts <code@ausiv.com> <git@ausiv.com>
Bas van Kervel <bas@ethdev.com> Bas van Kervel <bas@ethdev.com>
Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl> Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl>
Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com> Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com>
Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com> Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com>
Sven Ehlert <sven@ethdev.com> Boqin Qin <bobbqqin@bupt.edu.cn>
Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
Vitalik Buterin <v@buterin.com>
Marian Oancea <contact@siteshop.ro>
Christoph Jentzsch <jentzsch.software@gmail.com>
Heiko Hees <heiko@heiko.org>
Alex Leverington <alex@ethdev.com>
Alex Leverington <alex@ethdev.com> <subtly@users.noreply.github.com>
Zsolt Felföldi <zsfelfoldi@gmail.com>
Gavin Wood <i@gavwood.com>
Martin Becze <mjbecze@gmail.com>
Martin Becze <mjbecze@gmail.com> <wanderer@users.noreply.github.com>
Dimitry Khokhlov <winsvega@mail.ru>
Roman Mandeleil <roman.mandeleil@gmail.com>
Alec Perseghin <aperseghin@gmail.com>
Alon Muroch <alonmuroch@gmail.com>
Arkadiy Paronyan <arkadiy@ethdev.com>
Jae Kwon <jkwon.work@gmail.com>
Aaron Kumavis <kumavis@users.noreply.github.com>
Nick Dodson <silentcicero@outlook.com>
Jason Carver <jacarver@linkedin.com>
Jason Carver <jacarver@linkedin.com> <ut96caarrs@snkmail.com>
Joseph Chow <ethereum@outlook.com>
Joseph Chow <ethereum@outlook.com> ethers <TODO>
Enrique Fynn <enriquefynn@gmail.com>
Vincent G <caktux@gmail.com>
RJ Catalano <catalanor0220@gmail.com>
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
Nchinda Nchinda <nchinda2@gmail.com>
Aron Fischer <github@aron.guru> <homotopycolimit@users.noreply.github.com>
Vlad Gluhovsky <gluk256@users.noreply.github.com>
Ville Sundell <github@solarius.fi>
Elliot Shepherd <elliot@identitii.com>
Yohann Léon <sybiload@gmail.com>
Gregg Dourgarian <greggd@tempworks.com>
Casey Detrio <cdetrio@gmail.com> Casey Detrio <cdetrio@gmail.com>
Jens Agerberg <github@agerberg.me> Cheng Li <lob4tt@gmail.com>
Nick Johnson <arachnid@notdot.net> Chris Ziogas <ziogaschr@gmail.com>
Chris Ziogas <ziogaschr@gmail.com> <ziogas_chr@hotmail.com>
Henning Diedrich <hd@eonblast.com> Christoph Jentzsch <jentzsch.software@gmail.com>
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
Diederik Loerakker <proto@protolambda.com>
Dimitry Khokhlov <winsvega@mail.ru>
Domino Valdano <dominoplural@gmail.com>
Domino Valdano <dominoplural@gmail.com> <jeff@okcupid.com>
Edgar Aroutiounian <edgar.factorial@gmail.com>
Elliot Shepherd <elliot@identitii.com>
Enrique Fynn <enriquefynn@gmail.com>
Enrique Fynn <me@enriquefynn.com>
Enrique Fynn <me@enriquefynn.com> <enriquefynn@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
Everton Fraga <ev@ethereum.org>
Felix Lange <fjl@twurst.com> Felix Lange <fjl@twurst.com>
Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com> Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com>
Максим Чусовлянов <mchusovlianov@gmail.com>
Louis Holbrook <dev@holbrook.no>
Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
Thomas Bocek <tom@tomp2p.net>
Victor Tran <vu.tran54@gmail.com>
Justin Drake <drakefjustin@gmail.com>
Frank Wang <eternnoir@gmail.com> Frank Wang <eternnoir@gmail.com>
Gary Rong <garyrong0905@gmail.com> Gary Rong <garyrong0905@gmail.com>
Gavin Wood <i@gavwood.com>
Gregg Dourgarian <greggd@tempworks.com>
Guillaume Ballet <gballet@gmail.com>
Guillaume Ballet <gballet@gmail.com> <3272758+gballet@users.noreply.github.com>
Guillaume Nicolas <guin56@gmail.com> Guillaume Nicolas <guin56@gmail.com>
Hanjiang Yu <delacroix.yu@gmail.com>
Hanjiang Yu <delacroix.yu@gmail.com> <42531996+de1acr0ix@users.noreply.github.com>
Heiko Hees <heiko@heiko.org>
Henning Diedrich <hd@eonblast.com>
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
Hwanjo Heo <34005989+hwanjo@users.noreply.github.com>
Iskander (Alex) Sharipov <quasilyte@gmail.com>
Iskander (Alex) Sharipov <quasilyte@gmail.com> <i.sharipov@corp.vk.com>
Jae Kwon <jkwon.work@gmail.com>
Janoš Guljaš <janos@resenje.org> <janos@users.noreply.github.com>
Janoš Guljaš <janos@resenje.org> Janos Guljas <janos@resenje.org>
Jared Wasinger <j-wasinger@hotmail.com>
Jason Carver <jacarver@linkedin.com>
Jason Carver <jacarver@linkedin.com> <ut96caarrs@snkmail.com>
Javier Peletier <jm@epiclabs.io>
Javier Peletier <jm@epiclabs.io> <jpeletier@users.noreply.github.com>
Jeffrey Wilcke <jeffrey@ethereum.org>
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
Jens Agerberg <github@agerberg.me>
Joseph Chow <ethereum@outlook.com>
Joseph Chow <ethereum@outlook.com> ethers <TODO>
Joseph Goulden <joegoulden@gmail.com>
Justin Drake <drakefjustin@gmail.com>
Kenso Trabing <ktrabing@acm.org>
Kenso Trabing <ktrabing@acm.org> <kenso.trabing@bloomwebsite.com>
Liang Ma <liangma@liangbit.com>
Liang Ma <liangma@liangbit.com> <liangma.ul@gmail.com>
Louis Holbrook <dev@holbrook.no>
Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
Maran Hidskes <maran.hidskes@gmail.com>
Marian Oancea <contact@siteshop.ro>
Martin Becze <mjbecze@gmail.com>
Martin Becze <mjbecze@gmail.com> <wanderer@users.noreply.github.com>
Martin Lundfall <martin.lundfall@protonmail.com>
Matt Garnett <14004106+lightclient@users.noreply.github.com>
Matthew Halpern <matthalp@gmail.com>
Matthew Halpern <matthalp@gmail.com> <matthalp@google.com>
Michael Riabzev <michael@starkware.co>
Nchinda Nchinda <nchinda2@gmail.com>
Nick Dodson <silentcicero@outlook.com>
Nick Johnson <arachnid@notdot.net>
Nick Savers <nicksavers@gmail.com>
Nishant Das <nishdas93@gmail.com>
Nishant Das <nishdas93@gmail.com> <nish1993@hotmail.com>
Olivier Hervieu <olivier.hervieu@gmail.com>
Pascal Dierich <pascal@merkleplant.xyz>
Pascal Dierich <pascal@merkleplant.xyz> <pascal@pascaldierich.com>
RJ Catalano <catalanor0220@gmail.com>
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
Ralph Caraveo <deckarep@gmail.com>
Rene Lubov <41963722+renaynay@users.noreply.github.com>
Robert Zaremba <robert@zaremba.ch>
Robert Zaremba <robert@zaremba.ch> <robert.zaremba@scale-it.pl>
Roman Mandeleil <roman.mandeleil@gmail.com>
Sorin Neacsu <sorin.neacsu@gmail.com> Sorin Neacsu <sorin.neacsu@gmail.com>
Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com> Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com>
Sven Ehlert <sven@ethdev.com>
Taylor Gerring <taylor.gerring@gmail.com>
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
Thomas Bocek <tom@tomp2p.net>
Tim Cooijmans <timcooijmans@gmail.com>
Valentin Wüstholz <wuestholz@gmail.com> Valentin Wüstholz <wuestholz@gmail.com>
Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com> Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com>
Armin Braun <me@obrown.io> Victor Tran <vu.tran54@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com> Viktor Trón <viktor.tron@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
Ville Sundell <github@solarius.fi>
Vincent G <caktux@gmail.com>
Vitalik Buterin <v@buterin.com>
Vlad Gluhovsky <gluk256@gmail.com>
Vlad Gluhovsky <gluk256@gmail.com> <gluk256@users.noreply.github.com>
Wenshao Zhong <wzhong20@uic.edu>
Wenshao Zhong <wzhong20@uic.edu> <11510383@mail.sustc.edu.cn>
Wenshao Zhong <wzhong20@uic.edu> <374662347@qq.com>
Will Villanueva <hello@willvillanueva.com>
Xiaobing Jiang <s7v7nislands@gmail.com>
Xudong Liu <33193253+r1cs@users.noreply.github.com>
Yohann Léon <sybiload@gmail.com>
Zachinquarantine <Zachinquarantine@protonmail.com>
Zachinquarantine <Zachinquarantine@protonmail.com> <zachinquarantine@yahoo.com>
Ziyuan Zhong <zzy.albert@163.com>
Zsolt Felföldi <zsfelfoldi@gmail.com>
meowsbits <b5c6@protonmail.com>
meowsbits <b5c6@protonmail.com> <45600330+meowsbits@users.noreply.github.com>
nedifi <103940716+nedifi@users.noreply.github.com>
Максим Чусовлянов <mchusovlianov@gmail.com>

View File

@ -5,18 +5,15 @@ jobs:
allow_failures: allow_failures:
- stage: build - stage: build
os: osx os: osx
go: 1.15.x
env: env:
- azure-osx - azure-osx
- azure-ios
- cocoapods-ios
include: include:
# This builder only tests code linters on latest version of Go # This builder only tests code linters on latest version of Go
- stage: lint - stage: lint
os: linux os: linux
dist: bionic dist: bionic
go: 1.16.x go: 1.20.x
env: env:
- lint - lint
git: git:
@ -24,29 +21,41 @@ jobs:
script: script:
- go run build/ci.go lint - go run build/ci.go lint
# This builder does the Ubuntu PPA upload # 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.20.x
env: env:
- ubuntu-ppa - docker
- GO111MODULE=on services:
- docker
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
addons: before_install:
apt: - export DOCKER_CLI_EXPERIMENTAL=enabled
packages:
- devscripts
- debhelper
- dput
- fakeroot
- python-bzrlib
- python-paramiko
script: script:
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
- stage: build
if: type = push
os: linux
arch: arm64
dist: bionic
go: 1.20.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 Linux Azure uploads # This builder does the Linux Azure uploads
- stage: build - stage: build
@ -54,7 +63,7 @@ jobs:
os: linux os: linux
dist: bionic dist: bionic
sudo: required sudo: required
go: 1.16.x go: 1.20.x
env: env:
- azure-linux - azure-linux
- GO111MODULE=on - GO111MODULE=on
@ -84,83 +93,13 @@ jobs:
- go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc - go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
# This builder does the Linux Azure MIPS xgo uploads # This builder does the OSX Azure uploads
- stage: build
if: type = push
os: linux
dist: bionic
services:
- docker
go: 1.16.x
env:
- azure-linux-mips
- GO111MODULE=on
git:
submodules: false # avoid cloning ethereum/tests
script:
- go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
- go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
- go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done
- go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
- go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done
- go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY signify SIGNIFY_KEY -upload gethstore/builds
- go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
# This builder does the Android Maven and Azure uploads
- stage: build
if: type = push
os: linux
dist: bionic
addons:
apt:
packages:
- openjdk-8-jdk
env:
- azure-android
- maven-android
- GO111MODULE=on
git:
submodules: false # avoid cloning ethereum/tests
before_install:
# Install Android and it's dependencies manually, Travis is stale
- export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
- curl https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip -o android.zip
- unzip -q android.zip -d $HOME/sdk && rm android.zip
- mv $HOME/sdk/cmdline-tools $HOME/sdk/latest && mkdir $HOME/sdk/cmdline-tools && mv $HOME/sdk/latest $HOME/sdk/cmdline-tools
- export PATH=$PATH:$HOME/sdk/cmdline-tools/latest/bin
- export ANDROID_HOME=$HOME/sdk
- yes | sdkmanager --licenses >/dev/null
- sdkmanager "platform-tools" "platforms;android-15" "platforms;android-19" "platforms;android-24" "ndk-bundle"
# Install Go to allow building with
- curl https://dl.google.com/go/go1.16.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go
- export GOPATH=$HOME/go
script:
# Build the Android archive and upload it to Maven Central and Azure
- mkdir -p $GOPATH/src/github.com/ethereum
- ln -s `pwd` $GOPATH/src/github.com/ethereum/go-ethereum
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -signify SIGNIFY_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- stage: build - stage: build
if: type = push if: type = push
os: osx os: osx
go: 1.16.x go: 1.20.x
env: env:
- azure-osx - azure-osx
- azure-ios
- cocoapods-ios
- GO111MODULE=on - GO111MODULE=on
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
@ -168,58 +107,67 @@ jobs:
- go run build/ci.go install -dlgo - go run build/ci.go install -dlgo
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
# Build the iOS framework and upload it to CocoaPods and Azure
- gem uninstall cocoapods -a -x
- gem install cocoapods
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
- sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi
- xctool -version
- xcrun simctl list
# Workaround for https://github.com/golang/go/issues/23749
- export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc'
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -signify SIGNIFY_KEY -deploy trunk -upload gethstore/builds
# These builders run the tests # These builders run the tests
- stage: build - stage: build
os: linux os: linux
arch: amd64 arch: amd64
dist: bionic dist: bionic
go: 1.16.x go: 1.20.x
env: env:
- GO111MODULE=on - GO111MODULE=on
script: script:
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test $TEST_PACKAGES
- stage: build - stage: build
if: type = pull_request if: type = pull_request
os: linux os: linux
arch: arm64 arch: arm64
dist: bionic dist: bionic
go: 1.16.x go: 1.19.x
env: env:
- GO111MODULE=on - GO111MODULE=on
script: script:
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test $TEST_PACKAGES
- stage: build - stage: build
os: linux os: linux
dist: bionic dist: bionic
go: 1.15.x go: 1.19.x
env: env:
- GO111MODULE=on - GO111MODULE=on
script: script:
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test $TEST_PACKAGES
# This builder does the Ubuntu PPA nightly uploads
- stage: build
if: type = cron || (type = push && tag ~= /^v[0-9]/)
os: linux
dist: bionic
go: 1.20.x
env:
- ubuntu-ppa
- GO111MODULE=on
git:
submodules: false # avoid cloning ethereum/tests
addons:
apt:
packages:
- devscripts
- debhelper
- dput
- fakeroot
- python-bzrlib
- python-paramiko
script:
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
# This builder does the Azure archive purges to avoid accumulating junk # This builder does the Azure archive purges to avoid accumulating junk
- stage: build - stage: build
if: type = cron if: type = cron
os: linux os: linux
dist: bionic dist: bionic
go: 1.16.x go: 1.20.x
env: env:
- azure-purge - azure-purge
- GO111MODULE=on - GO111MODULE=on
@ -227,3 +175,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.20.x
env:
- GO111MODULE=on
script:
- go run build/ci.go test -race $TEST_PACKAGES

260
AUTHORS
View File

@ -1,27 +1,46 @@
# This is the official list of go-ethereum authors for copyright purposes. # This is the official list of go-ethereum authors for copyright purposes.
6543 <6543@obermui.de>
a e r t h <aerth@users.noreply.github.com> a e r t h <aerth@users.noreply.github.com>
Aaron Buchwald <aaron.buchwald56@gmail.com>
Abel Nieto <abel.nieto90@gmail.com> Abel Nieto <abel.nieto90@gmail.com>
Abel Nieto <anietoro@uwaterloo.ca>
Adam Babik <a.babik@designfortress.com> Adam Babik <a.babik@designfortress.com>
Adam Schmideg <adamschmideg@users.noreply.github.com>
Aditya <adityasripal@gmail.com> Aditya <adityasripal@gmail.com>
Aditya Arora <arora.aditya520@gmail.com>
Adrià Cidre <adria.cidre@gmail.com> Adrià Cidre <adria.cidre@gmail.com>
Afanasii Kurakin <afanasy@users.noreply.github.com>
Afri Schoedon <5chdn@users.noreply.github.com> Afri Schoedon <5chdn@users.noreply.github.com>
Agustin Armellini Fischer <armellini13@gmail.com> Agustin Armellini Fischer <armellini13@gmail.com>
Ahyun <urbanart2251@gmail.com>
Airead <fgh1987168@gmail.com> Airead <fgh1987168@gmail.com>
Alan Chen <alanchchen@users.noreply.github.com> Alan Chen <alanchchen@users.noreply.github.com>
Alejandro Isaza <alejandro.isaza@gmail.com> Alejandro Isaza <alejandro.isaza@gmail.com>
Aleksey Smyrnov <i@soar.name>
Ales Katona <ales@coinbase.com> Ales Katona <ales@coinbase.com>
Alex Beregszaszi <alex@rtfs.hu>
Alex Leverington <alex@ethdev.com> Alex Leverington <alex@ethdev.com>
Alex Mazalov <mazalov@gmail.com>
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com>
Alex Prut <1648497+alexprut@users.noreply.github.com>
Alex Wu <wuyiding@gmail.com> Alex Wu <wuyiding@gmail.com>
Alexander van der Meij <alexandervdm@users.noreply.github.com>
Alexander Yastrebov <yastrebov.alex@gmail.com>
Alexandre Van de Sande <alex.vandesande@ethdev.com> Alexandre Van de Sande <alex.vandesande@ethdev.com>
Alexey Akhunov <akhounov@gmail.com>
Alexey Shekhirin <a.shekhirin@gmail.com>
alexwang <39109351+dipingxian2@users.noreply.github.com>
Ali Atiia <42751398+aliatiia@users.noreply.github.com>
Ali Hajimirza <Ali92hm@users.noreply.github.com> Ali Hajimirza <Ali92hm@users.noreply.github.com>
am2rican5 <am2rican5@gmail.com> am2rican5 <am2rican5@gmail.com>
AmitBRD <60668103+AmitBRD@users.noreply.github.com>
Anatole <62328077+a2br@users.noreply.github.com>
Andrea Franz <andrea@gravityblast.com> Andrea Franz <andrea@gravityblast.com>
Andrey Petrov <andrey.petrov@shazow.net> Andrei Maiboroda <andrei@ethereum.org>
Andrey Petrov <shazow@gmail.com> Andrey Petrov <shazow@gmail.com>
ANOTHEL <anothel1@naver.com> ANOTHEL <anothel1@naver.com>
Antoine Rondelet <rondelet.antoine@gmail.com> Antoine Rondelet <rondelet.antoine@gmail.com>
Antoine Toulme <atoulme@users.noreply.github.com>
Anton Evangelatov <anton.evangelatov@gmail.com> Anton Evangelatov <anton.evangelatov@gmail.com>
Antonio Salazar Cardozo <savedfastcool@gmail.com> Antonio Salazar Cardozo <savedfastcool@gmail.com>
Arba Sasmoyo <arba.sasmoyo@gmail.com> Arba Sasmoyo <arba.sasmoyo@gmail.com>
@ -29,19 +48,26 @@ Armani Ferrante <armaniferrante@berkeley.edu>
Armin Braun <me@obrown.io> Armin Braun <me@obrown.io>
Aron Fischer <github@aron.guru> Aron Fischer <github@aron.guru>
atsushi-ishibashi <atsushi.ishibashi@finatext.com> atsushi-ishibashi <atsushi.ishibashi@finatext.com>
Austin Roberts <code@ausiv.com>
ayeowch <ayeowch@gmail.com> ayeowch <ayeowch@gmail.com>
b00ris <b00ris@mail.ru> b00ris <b00ris@mail.ru>
b1ackd0t <blackd0t@protonmail.com>
bailantaotao <Edwin@maicoin.com> bailantaotao <Edwin@maicoin.com>
baizhenxuan <nkbai@163.com> baizhenxuan <nkbai@163.com>
Balaji Shetty Pachai <32358081+balajipachai@users.noreply.github.com>
Balint Gabor <balint.g@gmail.com> Balint Gabor <balint.g@gmail.com>
baptiste-b-pegasys <85155432+baptiste-b-pegasys@users.noreply.github.com>
Bas van Kervel <bas@ethdev.com> Bas van Kervel <bas@ethdev.com>
Benjamin Brent <benjamin@benjaminbrent.com> Benjamin Brent <benjamin@benjaminbrent.com>
benma <mbencun@gmail.com> benma <mbencun@gmail.com>
Benoit Verkindt <benoit.verkindt@gmail.com> Benoit Verkindt <benoit.verkindt@gmail.com>
Binacs <bin646891055@gmail.com>
bloonfield <bloonfield@163.com> bloonfield <bloonfield@163.com>
Bo <bohende@gmail.com> Bo <bohende@gmail.com>
Bo Ye <boy.e.computer.1982@outlook.com> Bo Ye <boy.e.computer.1982@outlook.com>
Bob Glickstein <bobg@users.noreply.github.com> Bob Glickstein <bobg@users.noreply.github.com>
Boqin Qin <bobbqqin@bupt.edu.cn>
Brandon Harden <b.harden92@gmail.com>
Brent <bmperrea@gmail.com> Brent <bmperrea@gmail.com>
Brian Schroeder <bts@gmail.com> Brian Schroeder <bts@gmail.com>
Bruno Škvorc <bruno@skvorc.me> Bruno Škvorc <bruno@skvorc.me>
@ -49,36 +75,58 @@ C. Brown <hackdom@majoolr.io>
Caesar Chad <BLUE.WEB.GEEK@gmail.com> Caesar Chad <BLUE.WEB.GEEK@gmail.com>
Casey Detrio <cdetrio@gmail.com> Casey Detrio <cdetrio@gmail.com>
CDsigma <cdsigma271@gmail.com> CDsigma <cdsigma271@gmail.com>
Ceelog <chenwei@ceelog.org>
Ceyhun Onur <ceyhun.onur@avalabs.org>
chabashilah <doumodoumo@gmail.com>
changhong <changhong.yu@shanbay.com> changhong <changhong.yu@shanbay.com>
Chase Wright <mysticryuujin@gmail.com> Chase Wright <mysticryuujin@gmail.com>
Chen Quan <terasum@163.com> Chen Quan <terasum@163.com>
Cheng Li <lob4tt@gmail.com>
chenglin <910372762@qq.com>
chenyufeng <yufengcode@gmail.com> chenyufeng <yufengcode@gmail.com>
Chris Pacia <ctpacia@gmail.com>
Chris Ziogas <ziogaschr@gmail.com>
Christian Muehlhaeuser <muesli@gmail.com> Christian Muehlhaeuser <muesli@gmail.com>
Christoph Jentzsch <jentzsch.software@gmail.com> Christoph Jentzsch <jentzsch.software@gmail.com>
chuwt <weitaochu@gmail.com>
cong <ackratos@users.noreply.github.com> cong <ackratos@users.noreply.github.com>
Connor Stein <connor.stein@mail.mcgill.ca>
Corey Lin <514971757@qq.com> Corey Lin <514971757@qq.com>
courtier <derinilter@gmail.com>
cpusoft <cpusoft@live.com> cpusoft <cpusoft@live.com>
Crispin Flowerday <crispin@bitso.com> Crispin Flowerday <crispin@bitso.com>
croath <croathliu@gmail.com> croath <croathliu@gmail.com>
cui <523516579@qq.com> cui <523516579@qq.com>
Dan DeGreef <dan.degreef@gmail.com>
Dan Kinsley <dan@joincivil.com> Dan Kinsley <dan@joincivil.com>
Dan Sosedoff <dan.sosedoff@gmail.com>
Daniel A. Nagy <nagy.da@gmail.com> Daniel A. Nagy <nagy.da@gmail.com>
Daniel Perez <daniel@perez.sh>
Daniel Sloof <goapsychadelic@gmail.com> Daniel Sloof <goapsychadelic@gmail.com>
Darioush Jalali <darioush.jalali@avalabs.org>
Darrel Herbst <dherbst@gmail.com> Darrel Herbst <dherbst@gmail.com>
Dave Appleton <calistralabs@gmail.com> Dave Appleton <calistralabs@gmail.com>
Dave McGregor <dave.s.mcgregor@gmail.com> Dave McGregor <dave.s.mcgregor@gmail.com>
David Cai <davidcai1993@yahoo.com>
David Huie <dahuie@gmail.com> David Huie <dahuie@gmail.com>
Denver <aeharvlee@gmail.com>
Derek Chiang <me@derekchiang.com>
Derek Gottfrid <derek@codecubed.com> Derek Gottfrid <derek@codecubed.com>
Di Peng <pendyaaa@gmail.com>
Diederik Loerakker <proto@protolambda.com>
Diego Siqueira <DiSiqueira@users.noreply.github.com> Diego Siqueira <DiSiqueira@users.noreply.github.com>
Diep Pham <mrfavadi@gmail.com> Diep Pham <mrfavadi@gmail.com>
dipingxian2 <39109351+dipingxian2@users.noreply.github.com> dipingxian2 <39109351+dipingxian2@users.noreply.github.com>
divergencetech <94644849+divergencetech@users.noreply.github.com>
dm4 <sunrisedm4@gmail.com> dm4 <sunrisedm4@gmail.com>
Dmitrij Koniajev <dimchansky@gmail.com> Dmitrij Koniajev <dimchansky@gmail.com>
Dmitry Shulyak <yashulyak@gmail.com> Dmitry Shulyak <yashulyak@gmail.com>
Dmitry Zenovich <dzenovich@gmail.com>
Domino Valdano <dominoplural@gmail.com> Domino Valdano <dominoplural@gmail.com>
Domino Valdano <jeff@okcupid.com>
Dragan Milic <dragan@netice9.com> Dragan Milic <dragan@netice9.com>
dragonvslinux <35779158+dragononcrypto@users.noreply.github.com> dragonvslinux <35779158+dragononcrypto@users.noreply.github.com>
Edgar Aroutiounian <edgar.factorial@gmail.com>
Eduard S <eduardsanou@posteo.net>
Egon Elbre <egonelbre@gmail.com> Egon Elbre <egonelbre@gmail.com>
Elad <theman@elad.im> Elad <theman@elad.im>
Eli <elihanover@yahoo.com> Eli <elihanover@yahoo.com>
@ -86,131 +134,189 @@ Elias Naur <elias.naur@gmail.com>
Elliot Shepherd <elliot@identitii.com> Elliot Shepherd <elliot@identitii.com>
Emil <mursalimovemeel@gmail.com> Emil <mursalimovemeel@gmail.com>
emile <emile@users.noreply.github.com> emile <emile@users.noreply.github.com>
Enrique Fynn <enriquefynn@gmail.com> Emmanuel T Odeke <odeke@ualberta.ca>
Eng Zer Jun <engzerjun@gmail.com>
Enrique Fynn <me@enriquefynn.com> Enrique Fynn <me@enriquefynn.com>
Enrique Ortiz <hi@enriqueortiz.dev>
EOS Classic <info@eos-classic.io> EOS Classic <info@eos-classic.io>
Erichin <erichinbato@gmail.com> Erichin <erichinbato@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com> Ernesto del Toro <ernesto.deltoro@gmail.com>
Ethan Buchman <ethan@coinculture.info> Ethan Buchman <ethan@coinculture.info>
ethersphere <thesw@rm.eth> ethersphere <thesw@rm.eth>
Eugene Lepeico <eugenelepeico@gmail.com>
Eugene Valeyev <evgen.povt@gmail.com> Eugene Valeyev <evgen.povt@gmail.com>
Evangelos Pappas <epappas@evalonlabs.com> Evangelos Pappas <epappas@evalonlabs.com>
Everton Fraga <ev@ethereum.org>
Evgeny <awesome.observer@yandex.com> Evgeny <awesome.observer@yandex.com>
Evgeny Danilenko <6655321@bk.ru> Evgeny Danilenko <6655321@bk.ru>
evgk <evgeniy.kamyshev@gmail.com> evgk <evgeniy.kamyshev@gmail.com>
Evolution404 <35091674+Evolution404@users.noreply.github.com>
EXEC <execvy@gmail.com>
Fabian Vogelsteller <fabian@frozeman.de> Fabian Vogelsteller <fabian@frozeman.de>
Fabio Barone <fabio.barone.co@gmail.com> Fabio Barone <fabio.barone.co@gmail.com>
Fabio Berger <fabioberger1991@gmail.com> Fabio Berger <fabioberger1991@gmail.com>
FaceHo <facehoshi@gmail.com> FaceHo <facehoshi@gmail.com>
Felipe Strozberg <48066928+FelStroz@users.noreply.github.com>
Felix Lange <fjl@twurst.com> Felix Lange <fjl@twurst.com>
Ferenc Szabo <frncmx@gmail.com> Ferenc Szabo <frncmx@gmail.com>
ferhat elmas <elmas.ferhat@gmail.com> ferhat elmas <elmas.ferhat@gmail.com>
Ferran Borreguero <ferranbt@protonmail.com>
Fiisio <liangcszzu@163.com> Fiisio <liangcszzu@163.com>
Fire Man <55934298+basdevelop@users.noreply.github.com>
flowerofdream <775654398@qq.com>
fomotrader <82184770+fomotrader@users.noreply.github.com>
ForLina <471133417@qq.com>
Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com> Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com>
Frank Wang <eternnoir@gmail.com> Frank Wang <eternnoir@gmail.com>
Franklin <mr_franklin@126.com> Franklin <mr_franklin@126.com>
Furkan KAMACI <furkankamaci@gmail.com> Furkan KAMACI <furkankamaci@gmail.com>
Fuyang Deng <dengfuyang@outlook.com>
GagziW <leon.stanko@rwth-aachen.de> GagziW <leon.stanko@rwth-aachen.de>
Gary Rong <garyrong0905@gmail.com> Gary Rong <garyrong0905@gmail.com>
Gautam Botrel <gautam.botrel@gmail.com>
George Ornbo <george@shapeshed.com> George Ornbo <george@shapeshed.com>
Giuseppe Bertone <bertone.giuseppe@gmail.com>
Greg Colvin <greg@colvin.org>
Gregg Dourgarian <greggd@tempworks.com> Gregg Dourgarian <greggd@tempworks.com>
Gregory Markou <16929357+GregTheGreek@users.noreply.github.com>
Guifel <toowik@gmail.com>
Guilherme Salgado <gsalgado@gmail.com> Guilherme Salgado <gsalgado@gmail.com>
Guillaume Ballet <gballet@gmail.com> Guillaume Ballet <gballet@gmail.com>
Guillaume Nicolas <guin56@gmail.com> Guillaume Nicolas <guin56@gmail.com>
GuiltyMorishita <morilliantblue@gmail.com> GuiltyMorishita <morilliantblue@gmail.com>
Guruprasad Kamath <48196632+gurukamath@users.noreply.github.com>
Gus <yo@soygus.com> Gus <yo@soygus.com>
Gustav Simonsson <gustav.simonsson@gmail.com> Gustav Simonsson <gustav.simonsson@gmail.com>
Gísli Kristjánsson <gislik@hamstur.is> Gísli Kristjánsson <gislik@hamstur.is>
Ha ĐANG <dvietha@gmail.com> Ha ĐANG <dvietha@gmail.com>
HackyMiner <hackyminer@gmail.com> HackyMiner <hackyminer@gmail.com>
hadv <dvietha@gmail.com> hadv <dvietha@gmail.com>
Hanjiang Yu <delacroix.yu@gmail.com>
Hao Bryan Cheng <haobcheng@gmail.com> Hao Bryan Cheng <haobcheng@gmail.com>
Hao Duan <duanhao0814@gmail.com>
HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Harry Dutton <me@bytejedi.com>
haryu703 <34744512+haryu703@users.noreply.github.com>
Hendrik Hofstadt <hendrik@nexantic.com>
Henning Diedrich <hd@eonblast.com> Henning Diedrich <hd@eonblast.com>
henopied <13500516+henopied@users.noreply.github.com>
hero5512 <lvshuaino@gmail.com>
holisticode <holistic.computing@gmail.com> holisticode <holistic.computing@gmail.com>
Hongbin Mao <hello2mao@gmail.com> Hongbin Mao <hello2mao@gmail.com>
Hsien-Tang Kao <htkao@pm.me> Hsien-Tang Kao <htkao@pm.me>
hsyodyssey <47173566+hsyodyssey@users.noreply.github.com>
Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com> Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com>
Hwanjo Heo <34005989+hwanjo@users.noreply.github.com>
hydai <z54981220@gmail.com> hydai <z54981220@gmail.com>
Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com> Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com>
Håvard Anda Estensen <haavard.ae@gmail.com>
Ian Macalinao <me@ian.pw> Ian Macalinao <me@ian.pw>
Ian Norden <iannordenn@gmail.com> Ian Norden <iannordenn@gmail.com>
icodezjb <icodezjb@163.com>
Ikko Ashimine <eltociear@gmail.com>
Ilan Gitter <8359193+gitteri@users.noreply.github.com>
ImanSharaf <78227895+ImanSharaf@users.noreply.github.com>
Isidoro Ghezzi <isidoro.ghezzi@icloud.com> Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
Iskander (Alex) Sharipov <quasilyte@gmail.com> Iskander (Alex) Sharipov <quasilyte@gmail.com>
Ivan Bogatyy <bogatyi@gmail.com>
Ivan Daniluk <ivan.daniluk@gmail.com> Ivan Daniluk <ivan.daniluk@gmail.com>
Ivo Georgiev <ivo@strem.io> Ivo Georgiev <ivo@strem.io>
jacksoom <lifengliu1994@gmail.com>
Jae Kwon <jkwon.work@gmail.com> Jae Kwon <jkwon.work@gmail.com>
James Prestwich <10149425+prestwich@users.noreply.github.com>
Jamie Pitts <james.pitts@gmail.com> Jamie Pitts <james.pitts@gmail.com>
Janos Guljas <janos@resenje.org> Janoš Guljaš <janos@resenje.org>
Janoš Guljaš <janos@users.noreply.github.com> Jared Wasinger <j-wasinger@hotmail.com>
Jason Carver <jacarver@linkedin.com> Jason Carver <jacarver@linkedin.com>
Javier Peletier <jm@epiclabs.io> Javier Peletier <jm@epiclabs.io>
Javier Peletier <jpeletier@users.noreply.github.com>
Javier Sagredo <jasataco@gmail.com> Javier Sagredo <jasataco@gmail.com>
Jay <codeholic.arena@gmail.com> Jay <codeholic.arena@gmail.com>
Jay Guo <guojiannan1101@gmail.com> Jay Guo <guojiannan1101@gmail.com>
Jaynti Kanani <jdkanani@gmail.com> Jaynti Kanani <jdkanani@gmail.com>
Jeff Prestes <jeffprestes@gmail.com> Jeff Prestes <jeffprestes@gmail.com>
Jeff R. Allen <jra@nella.org> Jeff R. Allen <jra@nella.org>
Jeff Wentworth <jeff@curvegrid.com>
Jeffery Robert Walsh <rlxrlps@gmail.com> Jeffery Robert Walsh <rlxrlps@gmail.com>
Jeffrey Wilcke <jeffrey@ethereum.org> Jeffrey Wilcke <jeffrey@ethereum.org>
Jens Agerberg <github@agerberg.me> Jens Agerberg <github@agerberg.me>
Jeremy McNevin <jeremy.mcnevin@optum.com> Jeremy McNevin <jeremy.mcnevin@optum.com>
Jeremy Schlatter <jeremy.schlatter@gmail.com> Jeremy Schlatter <jeremy.schlatter@gmail.com>
Jerzy Lasyk <jerzylasyk@gmail.com> Jerzy Lasyk <jerzylasyk@gmail.com>
Jesse Tane <jesse.tane@gmail.com>
Jia Chenhui <jiachenhui1989@gmail.com> Jia Chenhui <jiachenhui1989@gmail.com>
Jim McDonald <Jim@mcdee.net> Jim McDonald <Jim@mcdee.net>
jk-jeongkyun <45347815+jeongkyun-oh@users.noreply.github.com>
jkcomment <jkcomment@gmail.com> jkcomment <jkcomment@gmail.com>
JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
Joel Burget <joelburget@gmail.com> Joel Burget <joelburget@gmail.com>
John C. Vernaleo <john@netpurgatory.com> John C. Vernaleo <john@netpurgatory.com>
John Difool <johndifoolpi@gmail.com>
Johns Beharry <johns@peakshift.com> Johns Beharry <johns@peakshift.com>
Jonas <felberj@users.noreply.github.com> Jonas <felberj@users.noreply.github.com>
Jonathan Brown <jbrown@bluedroplet.com> Jonathan Brown <jbrown@bluedroplet.com>
Jonathan Chappelow <chappjc@users.noreply.github.com>
Jonathan Gimeno <jgimeno@gmail.com>
JoranHonig <JoranHonig@users.noreply.github.com> JoranHonig <JoranHonig@users.noreply.github.com>
Jordan Krage <jmank88@gmail.com> Jordan Krage <jmank88@gmail.com>
Jorropo <jorropo.pgm@gmail.com>
Joseph Chow <ethereum@outlook.com> Joseph Chow <ethereum@outlook.com>
Joshua Colvin <jcolvin@offchainlabs.com>
Joshua Gutow <jbgutow@gmail.com>
jovijovi <mageyul@hotmail.com>
jtakalai <juuso.takalainen@streamr.com> jtakalai <juuso.takalainen@streamr.com>
JU HYEONG PARK <dkdkajej@gmail.com> JU HYEONG PARK <dkdkajej@gmail.com>
Julian Y <jyap808@users.noreply.github.com>
Justin Clark-Casey <justincc@justincc.org> Justin Clark-Casey <justincc@justincc.org>
Justin Drake <drakefjustin@gmail.com> Justin Drake <drakefjustin@gmail.com>
jwasinger <j-wasinger@hotmail.com> Justus <jus@gtsbr.org>
Kawashima <91420903+sscodereth@users.noreply.github.com>
ken10100147 <sunhongping@kanjian.com> ken10100147 <sunhongping@kanjian.com>
Kenji Siu <kenji@isuntv.com> Kenji Siu <kenji@isuntv.com>
Kenso Trabing <kenso.trabing@bloomwebsite.com>
Kenso Trabing <ktrabing@acm.org> Kenso Trabing <ktrabing@acm.org>
Kevin <denk.kevin@web.de> Kevin <denk.kevin@web.de>
kevin.xu <cming.xu@gmail.com> kevin.xu <cming.xu@gmail.com>
KibGzr <kibgzr@gmail.com>
kiel barry <kiel.j.barry@gmail.com> kiel barry <kiel.j.barry@gmail.com>
kilic <onurkilic1004@gmail.com>
kimmylin <30611210+kimmylin@users.noreply.github.com> kimmylin <30611210+kimmylin@users.noreply.github.com>
Kitten King <53072918+kittenking@users.noreply.github.com> Kitten King <53072918+kittenking@users.noreply.github.com>
knarfeh <hejun1874@gmail.com> knarfeh <hejun1874@gmail.com>
Kobi Gurkan <kobigurk@gmail.com> Kobi Gurkan <kobigurk@gmail.com>
komika <komika@komika.org>
Konrad Feldmeier <konrad@brainbot.com> Konrad Feldmeier <konrad@brainbot.com>
Kris Shinn <raggamuffin.music@gmail.com> Kris Shinn <raggamuffin.music@gmail.com>
Kristofer Peterson <svenski123@users.noreply.github.com>
Kumar Anirudha <mail@anirudha.dev>
Kurkó Mihály <kurkomisi@users.noreply.github.com> Kurkó Mihály <kurkomisi@users.noreply.github.com>
Kushagra Sharma <ksharm01@gmail.com> Kushagra Sharma <ksharm01@gmail.com>
Kwuaint <34888408+kwuaint@users.noreply.github.com> Kwuaint <34888408+kwuaint@users.noreply.github.com>
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com> Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
ledgerwatch <akhounov@gmail.com> Lee Bousfield <ljbousfield@gmail.com>
Lefteris Karapetsas <lefteris@refu.co> Lefteris Karapetsas <lefteris@refu.co>
Leif Jurvetson <leijurv@gmail.com> Leif Jurvetson <leijurv@gmail.com>
Leo Shklovskii <leo@thermopylae.net> Leo Shklovskii <leo@thermopylae.net>
LeoLiao <leofantast@gmail.com> LeoLiao <leofantast@gmail.com>
Lewis Marshall <lewis@lmars.net> Lewis Marshall <lewis@lmars.net>
lhendre <lhendre2@gmail.com> lhendre <lhendre2@gmail.com>
Liang Ma <liangma.ul@gmail.com> Li Dongwei <lidw1988@126.com>
Liang Ma <liangma@liangbit.com> Liang Ma <liangma@liangbit.com>
Liang ZOU <liang.d.zou@gmail.com> Liang ZOU <liang.d.zou@gmail.com>
libby kent <viskovitzzz@gmail.com>
libotony <liboliqi@gmail.com> libotony <liboliqi@gmail.com>
LieutenantRoger <dijsky_2015@hotmail.com>
ligi <ligi@ligi.de> ligi <ligi@ligi.de>
Lio李欧 <lionello@users.noreply.github.com> Lio李欧 <lionello@users.noreply.github.com>
lmittmann <lmittmann@users.noreply.github.com>
Lorenzo Manacorda <lorenzo@kinvolk.io> Lorenzo Manacorda <lorenzo@kinvolk.io>
Louis Holbrook <dev@holbrook.no> Louis Holbrook <dev@holbrook.no>
Luca Zeug <luclu@users.noreply.github.com> Luca Zeug <luclu@users.noreply.github.com>
Lucas Hendren <lhendre2@gmail.com>
lzhfromustc <43191155+lzhfromustc@users.noreply.github.com>
Magicking <s@6120.eu> Magicking <s@6120.eu>
manlio <manlio.poltronieri@gmail.com> manlio <manlio.poltronieri@gmail.com>
Maran Hidskes <maran.hidskes@gmail.com> Maran Hidskes <maran.hidskes@gmail.com>
Marek Kotewicz <marek.kotewicz@gmail.com> Marek Kotewicz <marek.kotewicz@gmail.com>
Mariano Cortesi <mcortesi@gmail.com>
Marius van der Wijden <m.vanderwijden@live.de> Marius van der Wijden <m.vanderwijden@live.de>
Mark <markya0616@gmail.com> Mark <markya0616@gmail.com>
Mark Rushakoff <mark.rushakoff@gmail.com> Mark Rushakoff <mark.rushakoff@gmail.com>
@ -218,108 +324,193 @@ mark.lin <mark@maicoin.com>
Martin Alex Philip Dawson <u1356770@gmail.com> Martin Alex Philip Dawson <u1356770@gmail.com>
Martin Holst Swende <martin@swende.se> Martin Holst Swende <martin@swende.se>
Martin Klepsch <martinklepsch@googlemail.com> Martin Klepsch <martinklepsch@googlemail.com>
Martin Lundfall <martin.lundfall@protonmail.com>
Martin Michlmayr <tbm@cyrius.com>
Martin Redmond <21436+reds@users.noreply.github.com>
Mason Fischer <mason@kissr.co>
Mateusz Morusiewicz <11313015+Ruteri@users.noreply.github.com>
Mats Julian Olsen <mats@plysjbyen.net> Mats Julian Olsen <mats@plysjbyen.net>
Matt Garnett <14004106+lightclient@users.noreply.github.com>
Matt K <1036969+mkrump@users.noreply.github.com> Matt K <1036969+mkrump@users.noreply.github.com>
Matthew Di Ferrante <mattdf@users.noreply.github.com> Matthew Di Ferrante <mattdf@users.noreply.github.com>
Matthew Halpern <matthalp@gmail.com> Matthew Halpern <matthalp@gmail.com>
Matthew Halpern <matthalp@google.com>
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com> Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
Max Sistemich <mafrasi2@googlemail.com> Max Sistemich <mafrasi2@googlemail.com>
Maxim Zhiburt <zhiburt@gmail.com>
Maximilian Meister <mmeister@suse.de> Maximilian Meister <mmeister@suse.de>
me020523 <me020523@gmail.com>
Melvin Junhee Woo <melvin.woo@groundx.xyz>
meowsbits <b5c6@protonmail.com>
Micah Zoltu <micah@zoltu.net> Micah Zoltu <micah@zoltu.net>
Michael Forney <mforney@mforney.org>
Michael Riabzev <michael@starkware.co>
Michael Ruminer <michael.ruminer+github@gmail.com> Michael Ruminer <michael.ruminer+github@gmail.com>
michael1011 <me@michael1011.at>
Miguel Mota <miguelmota2@gmail.com> Miguel Mota <miguelmota2@gmail.com>
Mike Burr <mburr@nightmare.com>
Mikhail Mikheev <mmvsha73@gmail.com>
milesvant <milesvant@gmail.com>
Miro <mirokuratczyk@users.noreply.github.com>
Miya Chen <miyatlchen@gmail.com> Miya Chen <miyatlchen@gmail.com>
Mohanson <mohanson@outlook.com> Mohanson <mohanson@outlook.com>
mr_franklin <mr_franklin@126.com> mr_franklin <mr_franklin@126.com>
Mudit Gupta <guptamudit@ymail.com>
Mymskmkt <1847234666@qq.com> Mymskmkt <1847234666@qq.com>
Nalin Bhardwaj <nalinbhardwaj@nibnalin.me> Nalin Bhardwaj <nalinbhardwaj@nibnalin.me>
Natsu Kagami <natsukagami@gmail.com>
Nchinda Nchinda <nchinda2@gmail.com> Nchinda Nchinda <nchinda2@gmail.com>
nebojsa94 <nebojsa94@users.noreply.github.com>
necaremus <necaremus@gmail.com> necaremus <necaremus@gmail.com>
nedifi <103940716+nedifi@users.noreply.github.com>
needkane <604476380@qq.com> needkane <604476380@qq.com>
Nguyen Kien Trung <trung.n.k@gmail.com> Nguyen Kien Trung <trung.n.k@gmail.com>
Nguyen Sy Thanh Son <thanhson1085@gmail.com> Nguyen Sy Thanh Son <thanhson1085@gmail.com>
Nic Jansma <nic@nicj.net>
Nick Dodson <silentcicero@outlook.com> Nick Dodson <silentcicero@outlook.com>
Nick Johnson <arachnid@notdot.net> Nick Johnson <arachnid@notdot.net>
Nicolas Feignon <nfeignon@gmail.com>
Nicolas Guillaume <gunicolas@sqli.com> Nicolas Guillaume <gunicolas@sqli.com>
Nikita Kozhemyakin <enginegl.ec@gmail.com>
Nikola Madjarevic <nikola.madjarevic@gmail.com>
Nilesh Trivedi <nilesh@hypertrack.io> Nilesh Trivedi <nilesh@hypertrack.io>
Nimrod Gutman <nimrod.gutman@gmail.com> Nimrod Gutman <nimrod.gutman@gmail.com>
Nishant Das <nishdas93@gmail.com>
njupt-moon <1015041018@njupt.edu.cn> njupt-moon <1015041018@njupt.edu.cn>
nkbai <nkbai@163.com> nkbai <nkbai@163.com>
noam-alchemy <76969113+noam-alchemy@users.noreply.github.com>
nobody <ddean2009@163.com> nobody <ddean2009@163.com>
Noman <noman@noman.land> Noman <noman@noman.land>
nujabes403 <nujabes403@gmail.com>
Nye Liu <nyet@nyet.org>
Oleg Kovalov <iamolegkovalov@gmail.com> Oleg Kovalov <iamolegkovalov@gmail.com>
Oli Bye <olibye@users.noreply.github.com> Oli Bye <olibye@users.noreply.github.com>
Oliver Tale-Yazdi <oliver@perun.network>
Olivier Hervieu <olivier.hervieu@gmail.com>
Or Neeman <oneeman@gmail.com>
Osoro Bironga <fanosoro@gmail.com>
Osuke <arget-fee.free.dgm@hotmail.co.jp> Osuke <arget-fee.free.dgm@hotmail.co.jp>
Pantelis Peslis <pespantelis@gmail.com>
Pascal Dierich <pascal@merkleplant.xyz>
Patrick O'Grady <prohb125@gmail.com>
Pau <pau@dabax.net>
Paul Berg <hello@paulrberg.com> Paul Berg <hello@paulrberg.com>
Paul Litvak <litvakpol@012.net.il> Paul Litvak <litvakpol@012.net.il>
Paul-Armand Verhaegen <paularmand.verhaegen@gmail.com>
Paulo L F Casaretto <pcasaretto@gmail.com> Paulo L F Casaretto <pcasaretto@gmail.com>
Paweł Bylica <chfast@gmail.com> Paweł Bylica <chfast@gmail.com>
Pedro Gomes <otherview@gmail.com>
Pedro Pombeiro <PombeirP@users.noreply.github.com> Pedro Pombeiro <PombeirP@users.noreply.github.com>
Peter Broadhurst <peter@themumbles.net> Peter Broadhurst <peter@themumbles.net>
peter cresswell <pcresswell@gmail.com>
Peter Pratscher <pratscher@gmail.com> Peter Pratscher <pratscher@gmail.com>
Peter Simard <petesimard56@gmail.com>
Petr Mikusek <petr@mikusek.info> Petr Mikusek <petr@mikusek.info>
Philip Schlump <pschlump@gmail.com> Philip Schlump <pschlump@gmail.com>
Pierre Neter <pierreneter@gmail.com> Pierre Neter <pierreneter@gmail.com>
Pierre R <p.rousset@gmail.com>
piersy <pierspowlesland@gmail.com>
PilkyuJung <anothel1@naver.com> PilkyuJung <anothel1@naver.com>
protolambda <proto@protolambda.com> Piotr Dyraga <piotr.dyraga@keep.network>
ploui <64719999+ploui@users.noreply.github.com>
Preston Van Loon <preston@prysmaticlabs.com>
Prince Sinha <sinhaprince013@gmail.com>
Péter Szilágyi <peterke@gmail.com> Péter Szilágyi <peterke@gmail.com>
qd-ethan <31876119+qdgogogo@users.noreply.github.com> qd-ethan <31876119+qdgogogo@users.noreply.github.com>
Qian Bin <cola.tin.com@gmail.com>
Quest Henkart <qhenkart@gmail.com>
Rachel Franks <nfranks@protonmail.com>
Rafael Matias <rafael@skyle.net>
Raghav Sood <raghavsood@gmail.com> Raghav Sood <raghavsood@gmail.com>
Ralph Caraveo <deckarep@gmail.com> Ralph Caraveo <deckarep@gmail.com>
Ralph Caraveo III <deckarep@gmail.com>
Ramesh Nair <ram@hiddentao.com> Ramesh Nair <ram@hiddentao.com>
rangzen <public@l-homme.com>
reinerRubin <tolstov.georgij@gmail.com> reinerRubin <tolstov.georgij@gmail.com>
Rene Lubov <41963722+renaynay@users.noreply.github.com>
rhaps107 <dod-source@yandex.ru> rhaps107 <dod-source@yandex.ru>
Ricardo Catalinas Jiménez <r@untroubled.be> Ricardo Catalinas Jiménez <r@untroubled.be>
Ricardo Domingos <ricardohsd@gmail.com> Ricardo Domingos <ricardohsd@gmail.com>
Richard Hart <richardhart92@gmail.com> Richard Hart <richardhart92@gmail.com>
Rick <rick.no@groundx.xyz>
RJ Catalano <catalanor0220@gmail.com> RJ Catalano <catalanor0220@gmail.com>
Rob <robert@rojotek.com> Rob <robert@rojotek.com>
Rob Mulholand <rmulholand@8thlight.com> Rob Mulholand <rmulholand@8thlight.com>
Robert Zaremba <robert.zaremba@scale-it.pl> Robert Zaremba <robert@zaremba.ch>
Roc Yu <rociiu0112@gmail.com> Roc Yu <rociiu0112@gmail.com>
Roman Mazalov <83914728+gopherxyz@users.noreply.github.com>
Ross <9055337+Chadsr@users.noreply.github.com>
Runchao Han <elvisage941102@gmail.com> Runchao Han <elvisage941102@gmail.com>
Russ Cox <rsc@golang.org> Russ Cox <rsc@golang.org>
Ryan Schneider <ryanleeschneider@gmail.com> Ryan Schneider <ryanleeschneider@gmail.com>
ryanc414 <ryan@tokencard.io>
Rémy Roy <remyroy@remyroy.com> Rémy Roy <remyroy@remyroy.com>
S. Matthew English <s-matthew-english@users.noreply.github.com> S. Matthew English <s-matthew-english@users.noreply.github.com>
salanfe <salanfe@users.noreply.github.com> salanfe <salanfe@users.noreply.github.com>
Sam <39165351+Xia-Sam@users.noreply.github.com>
Sammy Libre <7374093+sammy007@users.noreply.github.com>
Samuel Marks <samuelmarks@gmail.com> Samuel Marks <samuelmarks@gmail.com>
sanskarkhare <sanskarkhare47@gmail.com>
Sarlor <kinsleer@outlook.com> Sarlor <kinsleer@outlook.com>
Sasuke1964 <neilperry1964@gmail.com> Sasuke1964 <neilperry1964@gmail.com>
Satpal <28562234+SatpalSandhu61@users.noreply.github.com>
Saulius Grigaitis <saulius@necolt.com> Saulius Grigaitis <saulius@necolt.com>
Sean <darcys22@gmail.com> Sean <darcys22@gmail.com>
Sheldon <11510383@mail.sustc.edu.cn> Serhat Şevki Dinçer <jfcgauss@gmail.com>
Sheldon <374662347@qq.com> Shane Bammel <sjb933@gmail.com>
shawn <36943337+lxex@users.noreply.github.com>
shigeyuki azuchi <azuchi@chaintope.com>
Shihao Xia <charlesxsh@hotmail.com>
Shiming <codingmylife@gmail.com>
Shintaro Kaneko <kaneshin0120@gmail.com> Shintaro Kaneko <kaneshin0120@gmail.com>
shiqinfeng1 <150627601@qq.com>
Shuai Qi <qishuai231@gmail.com> Shuai Qi <qishuai231@gmail.com>
Shude Li <islishude@gmail.com>
Shunsuke Watanabe <ww.shunsuke@gmail.com> Shunsuke Watanabe <ww.shunsuke@gmail.com>
silence <wangsai.silence@qq.com> silence <wangsai.silence@qq.com>
Simon Jentzsch <simon@slock.it> Simon Jentzsch <simon@slock.it>
Sina Mahmoodi <1591639+s1na@users.noreply.github.com>
sixdays <lj491685571@126.com>
SjonHortensius <SjonHortensius@users.noreply.github.com>
Slava Karpenko <slavikus@gmail.com>
slumber1122 <slumber1122@gmail.com> slumber1122 <slumber1122@gmail.com>
Smilenator <yurivanenko@yandex.ru> Smilenator <yurivanenko@yandex.ru>
soc1c <soc1c@users.noreply.github.com>
Sorin Neacsu <sorin.neacsu@gmail.com> Sorin Neacsu <sorin.neacsu@gmail.com>
Sparty <vignesh.crysis@gmail.com>
Stein Dekker <dekker.stein@gmail.com> Stein Dekker <dekker.stein@gmail.com>
Steve Gattuso <steve@stevegattuso.me> Steve Gattuso <steve@stevegattuso.me>
Steve Ruckdashel <steve.ruckdashel@gmail.com> Steve Ruckdashel <steve.ruckdashel@gmail.com>
Steve Waldman <swaldman@mchange.com> Steve Waldman <swaldman@mchange.com>
Steven E. Harris <seh@panix.com>
Steven Roose <stevenroose@gmail.com> Steven Roose <stevenroose@gmail.com>
stompesi <stompesi@gmail.com> stompesi <stompesi@gmail.com>
stormpang <jialinpeng@vip.qq.com> stormpang <jialinpeng@vip.qq.com>
sunxiaojun2014 <sunxiaojun-xy@360.cn> sunxiaojun2014 <sunxiaojun-xy@360.cn>
Suriyaa Sundararuban <isc.suriyaa@gmail.com>
Sylvain Laurent <s@6120.eu>
Taeik Lim <sibera21@gmail.com>
tamirms <tamir@trello.com> tamirms <tamir@trello.com>
Tangui Clairet <tangui.clairet@gmail.com>
Tatsuya Shimoda <tacoo@users.noreply.github.com>
Taylor Gerring <taylor.gerring@gmail.com> Taylor Gerring <taylor.gerring@gmail.com>
TColl <38299499+TColl@users.noreply.github.com> TColl <38299499+TColl@users.noreply.github.com>
terasum <terasum@163.com> terasum <terasum@163.com>
tgyKomgo <52910426+tgyKomgo@users.noreply.github.com>
Thad Guidry <thadguidry@gmail.com>
Thomas Bocek <tom@tomp2p.net> Thomas Bocek <tom@tomp2p.net>
thomasmodeneis <thomas.modeneis@gmail.com> thomasmodeneis <thomas.modeneis@gmail.com>
thumb8432 <thumb8432@gmail.com> thumb8432 <thumb8432@gmail.com>
Ti Zhou <tizhou1986@gmail.com> Ti Zhou <tizhou1986@gmail.com>
tia-99 <67107070+tia-99@users.noreply.github.com>
Tim Cooijmans <timcooijmans@gmail.com>
Tobias Hildebrandt <79341166+tobias-hildebrandt@users.noreply.github.com>
Tosh Camille <tochecamille@gmail.com> Tosh Camille <tochecamille@gmail.com>
tsarpaul <Litvakpol@012.net.il> tsarpaul <Litvakpol@012.net.il>
Tyler Chambers <2775339+tylerchambers@users.noreply.github.com>
tzapu <alex@tzapu.com> tzapu <alex@tzapu.com>
ucwong <ucwong@126.com>
uji <49834542+uji@users.noreply.github.com>
ult-bobonovski <alex@ultiledger.io> ult-bobonovski <alex@ultiledger.io>
Valentin Trinqué <ValentinTrinque@users.noreply.github.com>
Valentin Wüstholz <wuestholz@gmail.com> Valentin Wüstholz <wuestholz@gmail.com>
Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com> Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com>
Victor Farazdagi <simple.square@gmail.com> Victor Farazdagi <simple.square@gmail.com>
@ -330,40 +521,71 @@ Ville Sundell <github@solarius.fi>
vim88 <vim88vim88@gmail.com> vim88 <vim88vim88@gmail.com>
Vincent G <caktux@gmail.com> Vincent G <caktux@gmail.com>
Vincent Serpoul <vincent@serpoul.com> Vincent Serpoul <vincent@serpoul.com>
Vinod Damle <vdamle@users.noreply.github.com>
Vitalik Buterin <v@buterin.com> Vitalik Buterin <v@buterin.com>
Vitaly Bogdanov <vsbogd@gmail.com> Vitaly Bogdanov <vsbogd@gmail.com>
Vitaly V <vvelikodny@gmail.com> Vitaly V <vvelikodny@gmail.com>
Vivek Anand <vivekanand1101@users.noreply.github.com> Vivek Anand <vivekanand1101@users.noreply.github.com>
Vlad <gluk256@gmail.com>
Vlad Bokov <razum2um@mail.ru> Vlad Bokov <razum2um@mail.ru>
Vlad Gluhovsky <gluk256@users.noreply.github.com> Vlad Gluhovsky <gluk256@gmail.com>
Ward Bradt <wardbradt5@gmail.com>
Water <44689567+codeoneline@users.noreply.github.com>
wbt <wbt@users.noreply.github.com>
weimumu <934657014@qq.com> weimumu <934657014@qq.com>
Wenbiao Zheng <delweng@gmail.com> Wenbiao Zheng <delweng@gmail.com>
Wenshao Zhong <wzhong20@uic.edu>
Will Villanueva <hello@willvillanueva.com>
William Morriss <wjmelements@gmail.com>
William Setzer <bootstrapsetzer@gmail.com> William Setzer <bootstrapsetzer@gmail.com>
williambannas <wrschwartz@wpi.edu> williambannas <wrschwartz@wpi.edu>
wuff1996 <33193253+wuff1996@users.noreply.github.com>
Wuxiang <wuxiangzhou2010@gmail.com> Wuxiang <wuxiangzhou2010@gmail.com>
Xiaobing Jiang <s7v7nislands@gmail.com>
xiekeyang <xiekeyang@users.noreply.github.com> xiekeyang <xiekeyang@users.noreply.github.com>
xincaosu <xincaosu@126.com> xincaosu <xincaosu@126.com>
xinluyin <31590468+xinluyin@users.noreply.github.com>
Xudong Liu <33193253+r1cs@users.noreply.github.com>
xwjack <XWJACK@users.noreply.github.com>
yahtoo <yahtoo.ma@gmail.com> yahtoo <yahtoo.ma@gmail.com>
Yang Hau <vulxj0j8j8@gmail.com>
YaoZengzeng <yaozengzeng@zju.edu.cn> YaoZengzeng <yaozengzeng@zju.edu.cn>
YH-Zhou <yanhong.zhou05@gmail.com> YH-Zhou <yanhong.zhou05@gmail.com>
Yihau Chen <a122092487@gmail.com>
Yohann Léon <sybiload@gmail.com> Yohann Léon <sybiload@gmail.com>
Yoichi Hirai <i@yoichihirai.com> Yoichi Hirai <i@yoichihirai.com>
Yole <007yuyue@gmail.com>
Yondon Fu <yondon.fu@gmail.com> Yondon Fu <yondon.fu@gmail.com>
YOSHIDA Masanori <masanori.yoshida@gmail.com> YOSHIDA Masanori <masanori.yoshida@gmail.com>
yoza <yoza.is12s@gmail.com> yoza <yoza.is12s@gmail.com>
yumiel yoomee1313 <yumiel.ko@groundx.xyz>
Yusup <awklsgrep@gmail.com> Yusup <awklsgrep@gmail.com>
yutianwu <wzxingbupt@gmail.com>
ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
zaccoding <zaccoding725@gmail.com>
Zach <zach.ramsay@gmail.com> Zach <zach.ramsay@gmail.com>
Zachinquarantine <Zachinquarantine@protonmail.com>
zah <zahary@gmail.com> zah <zahary@gmail.com>
Zahoor Mohamed <zahoor@zahoor.in> Zahoor Mohamed <zahoor@zahoor.in>
Zak Cole <zak@beattiecole.com> Zak Cole <zak@beattiecole.com>
zcheng9 <zcheng9@hawk.iit.edu>
zer0to0ne <36526113+zer0to0ne@users.noreply.github.com> zer0to0ne <36526113+zer0to0ne@users.noreply.github.com>
zgfzgf <48779939+zgfzgf@users.noreply.github.com>
Zhang Zhuo <mycinbrin@gmail.com>
zhangsoledad <787953403@qq.com>
zhaochonghe <41711151+zhaochonghe@users.noreply.github.com>
Zhenguo Niu <Niu.ZGlinux@gmail.com> Zhenguo Niu <Niu.ZGlinux@gmail.com>
zhiqiangxu <652732310@qq.com>
Zhou Zhiyao <ZHOU0250@e.ntu.edu.sg>
Ziyuan Zhong <zzy.albert@163.com>
Zoe Nolan <github@zoenolan.org> Zoe Nolan <github@zoenolan.org>
Zou Guangxian <zouguangxian@gmail.com>
Zsolt Felföldi <zsfelfoldi@gmail.com> Zsolt Felföldi <zsfelfoldi@gmail.com>
Łukasz Kurowski <crackcomm@users.noreply.github.com> Łukasz Kurowski <crackcomm@users.noreply.github.com>
Łukasz Zimnoch <lukaszzimnoch1994@gmail.com>
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com> ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
Максим Чусовлянов <mchusovlianov@gmail.com> Максим Чусовлянов <mchusovlianov@gmail.com>
大彬 <hz_stb@163.com> 大彬 <hz_stb@163.com>
沉风 <myself659@users.noreply.github.com>
贺鹏飞 <hpf@hackerful.cn> 贺鹏飞 <hpf@hackerful.cn>
陈佳 <chenjiablog@gmail.com>
유용환 <33824408+eric-yoo@users.noreply.github.com> 유용환 <33824408+eric-yoo@users.noreply.github.com>

View File

@ -1,10 +1,20 @@
# 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.20-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git
# Get dependencies - will also be cached if we won't change go.mod/go.sum
COPY go.mod /go-ethereum/
COPY go.sum /go-ethereum/
RUN cd /go-ethereum && go mod download
ADD . /go-ethereum ADD . /go-ethereum
RUN cd /go-ethereum && make geth RUN cd /go-ethereum && go run build/ci.go install -static ./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 +24,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"

View File

@ -1,10 +1,20 @@
# 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.20-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git
# Get dependencies - will also be cached if we won't change go.mod/go.sum
COPY go.mod /go-ethereum/
COPY go.sum /go-ethereum/
RUN cd /go-ethereum && go mod download
ADD . /go-ethereum ADD . /go-ethereum
RUN cd /go-ethereum && make all RUN cd /go-ethereum && go run build/ci.go install -static
# 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 +23,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"

7
Dockerfile.amd64 Normal file
View File

@ -0,0 +1,7 @@
# Build Geth in a stock Go builder container
FROM golang:1.15.5 as builder
#RUN apk add --no-cache make gcc musl-dev linux-headers git
ADD . /go-ethereum
RUN cd /go-ethereum && make geth

50
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,50 @@
pipeline {
agent any
stages {
stage('Build') {
steps {
script{
docker.withRegistry('https://git.vdb.to'){
echo 'Building geth image...'
//def geth_image = docker.build("cerc-io/go-ethereum:jenkinscicd")
echo 'built geth image'
}
}
}
}
stage('Test') {
agent {
docker {
image 'cerc-io/foundation:jenkinscicd'
//image 'cerc-io/foundation_alpine:jenkinscicd'
}
}
environment {
GO111MODULE = "on"
CGO_ENABLED = 1
//GOPATH = "${JENKINS_HOME}/jobs/${JOB_NAME}/builds/${BUILD_ID}"
//GOPATH = "/go"
GOPATH = "/tmp/go"
//GOMODCACHE = "/go/pkg/mod"
GOCACHE = "${WORKSPACE}/.cache/go-build"
GOENV = "${WORKSPACE}/.config/go/env"
GOMODCACHE = "/tmp/go/pkg/mod"
GOWORK=""
//GOFLAGS=""
}
steps {
echo 'Testing ...'
//sh '/usr/local/go/bin/go test -p 1 -v ./...'
sh 'make test'
}
}
stage('Packaging') {
steps {
echo 'Packaging ...'
}
}
}
}

138
Makefile
View File

@ -2,16 +2,33 @@
# with Go source code. If you know what GOPATH is then you probably # with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make. # don't need to bother with make.
.PHONY: geth android ios geth-cross evm all test clean .PHONY: geth android ios evm all test clean
.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64 BIN = $(GOPATH)/bin
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
.PHONY: geth-windows geth-windows-386 geth-windows-amd64 ## Migration tool
GOOSE = $(BIN)/goose
$(BIN)/goose:
go get -u github.com/pressly/goose/cmd/goose
GOBIN = ./build/bin GOBIN = ./build/bin
GO ?= latest GO ?= latest
GORUN = env GO111MODULE=on go run GORUN = env GO111MODULE=on go run
#Database
HOST_NAME = localhost
PORT = 5432
USER = vdbm
PASSWORD = password
# Set env variable
# `PGPASSWORD` is used by `createdb` and `dropdb`
export PGPASSWORD=$(PASSWORD)
#Test
TEST_DB = cerc_testing
TEST_CONNECT_STRING = postgresql://$(USER):$(PASSWORD)@$(HOST_NAME):$(PORT)/$(TEST_DB)?sslmode=disable
geth: geth:
$(GORUN) build/ci.go install ./cmd/geth $(GORUN) build/ci.go install ./cmd/geth
@echo "Done building." @echo "Done building."
@ -20,18 +37,6 @@ geth:
all: all:
$(GORUN) build/ci.go install $(GORUN) build/ci.go install
android:
$(GORUN) build/ci.go aar --local
@echo "Done building."
@echo "Import \"$(GOBIN)/geth.aar\" to use the library."
@echo "Import \"$(GOBIN)/geth-sources.jar\" to add javadocs"
@echo "For more info see https://stackoverflow.com/questions/20994336/android-studio-how-to-attach-javadoc"
ios:
$(GORUN) build/ci.go xcode --local
@echo "Done building."
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
test: all test: all
$(GORUN) build/ci.go test $(GORUN) build/ci.go test
@ -47,101 +52,18 @@ clean:
devtools: devtools:
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
env GOBIN= go install github.com/kevinburke/go-bindata/go-bindata@latest
env GOBIN= go install github.com/fjl/gencodec@latest env GOBIN= go install github.com/fjl/gencodec@latest
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
env GOBIN= go install ./cmd/abigen env GOBIN= go install ./cmd/abigen
@type "solc" 2> /dev/null || echo 'Please install solc' @type "solc" 2> /dev/null || echo 'Please install solc'
@type "protoc" 2> /dev/null || echo 'Please install protoc' @type "protoc" 2> /dev/null || echo 'Please install protoc'
# Cross Compilation Targets (xgo) .PHONY: statedifftest
statedifftest: | $(GOOSE)
GO111MODULE=on go get github.com/stretchr/testify/assert@v1.7.0
GO111MODULE=on MODE=statediff go test -p 1 ./statediff/... -v
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios .PHONY: statediff_filewriting_test
@echo "Full cross compilation done:" statediff_filetest: | $(GOOSE)
@ls -ld $(GOBIN)/geth-* GO111MODULE=on go get github.com/stretchr/testify/assert@v1.7.0
GO111MODULE=on MODE=statediff STATEDIFF_DB=file go test -p 1 ./statediff/... -v
geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 geth-linux-mips64le
@echo "Linux cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-*
geth-linux-386:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/geth
@echo "Linux 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep 386
geth-linux-amd64:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/geth
@echo "Linux amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
@echo "Linux ARM cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm
geth-linux-arm-5:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth
@echo "Linux ARMv5 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
geth-linux-arm-6:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/geth
@echo "Linux ARMv6 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
geth-linux-arm-7:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth
@echo "Linux ARMv7 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
geth-linux-arm64:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth
@echo "Linux ARM64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
geth-linux-mips:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPS cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips
geth-linux-mipsle:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPSle cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mipsle
geth-linux-mips64:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPS64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
geth-linux-mips64le:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPS64le cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
geth-darwin: geth-darwin-386 geth-darwin-amd64
@echo "Darwin cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-*
geth-darwin-386:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth
@echo "Darwin 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
geth-darwin-amd64:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth
@echo "Darwin amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
geth-windows: geth-windows-386 geth-windows-amd64
@echo "Windows cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-*
geth-windows-386:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth
@echo "Windows 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep 386
geth-windows-amd64:
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth
@echo "Windows amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep amd64

168
README.md
View File

@ -1,10 +1,8 @@
## Go Ethereum ## Go Ethereum
Official Golang implementation of the Ethereum protocol. Official Golang execution layer implementation of the Ethereum protocol.
[![API Reference]( [![API Reference](https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667)](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc)
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc)
[![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum) [![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
[![Travis](https://travis-ci.com/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.com/ethereum/go-ethereum) [![Travis](https://travis-ci.com/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.com/ethereum/go-ethereum)
[![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv) [![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv)
@ -12,11 +10,26 @@ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/6874
Automated builds are available for stable releases and the unstable master branch. Binary Automated builds are available for stable releases and the unstable master branch. Binary
archives are published at https://geth.ethereum.org/downloads/. archives are published at https://geth.ethereum.org/downloads/.
## Vulcanize Specific
This section captures components specific to vulcanize.
### Branching Structure
We currently follow the following branching structure.
1. Create a branch: `v1.10.18-statediff-vX` --> feature/some-feature`
2. Create a PR upstream: `feature/some-feature` --> `v1.10.18-statediff-vX`
3. When a release is ready, create a release branch: `v1.10.18-statediff-vX` --> `v1.10.18-statediff-X.Y.Z`
4. When `v1.10.18-statediff-vX` is stable, merge it to `statediff`.
This process is subject to change.
## Building the source ## Building the source
For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/install-and-build/installing-geth). For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth).
Building `geth` requires both a Go (version 1.14 or later) and a C compiler. You can install Building `geth` requires both a Go (version 1.19 or later) and a C compiler. You can install
them using your favourite package manager. Once the dependencies are installed, run them using your favourite package manager. Once the dependencies are installed, run
```shell ```shell
@ -34,44 +47,59 @@ make all
The go-ethereum project comes with several wrappers/executables found in the `cmd` The go-ethereum project comes with several wrappers/executables found in the `cmd`
directory. directory.
| Command | Description | | Command | Description |
| :-----------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | :--------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/interface/command-line-options) for command line options. | | **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/fundamentals/command-line-options) for command line options. |
| `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. | | `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. |
| `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. | | `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/dapp/native-bindings) page for details. | | `abigen` | Source code generator to convert Ethereum contract definitions into easy-to-use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings) page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | | `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | | `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://eth.wiki/en/fundamentals/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | | `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
| `puppeth` | a CLI wizard that aids in creating a new Ethereum network. |
## Running `geth` ## Running `geth`
Going through all the possible command line flags is out of scope here (please consult our Going through all the possible command line flags is out of scope here (please consult our
[CLI Wiki page](https://geth.ethereum.org/docs/interface/command-line-options)), [CLI Wiki page](https://geth.ethereum.org/docs/fundamentals/command-line-options)),
but we've enumerated a few common parameter combos to get you up to speed quickly but we've enumerated a few common parameter combos to get you up to speed quickly
on how you can run your own `geth` instance. on how you can run your own `geth` instance.
### Hardware Requirements
Minimum:
- CPU with 2+ cores
- 4GB RAM
- 1TB free storage space to sync the Mainnet
- 8 MBit/sec download Internet service
Recommended:
* Fast CPU with 4+ cores
* 16GB+ RAM
* High-performance SSD with at least 1TB of free space
* 25+ MBit/sec download Internet service
### Full node on the main Ethereum network ### Full node on the main Ethereum network
By far the most common scenario is people wanting to simply interact with the Ethereum By far the most common scenario is people wanting to simply interact with the Ethereum
network: create accounts; transfer funds; deploy and interact with contracts. For this network: create accounts; transfer funds; deploy and interact with contracts. For this
particular use-case the user doesn't care about years-old historical data, so we can particular use case, the user doesn't care about years-old historical data, so we can
fast-sync quickly to the current state of the network. To do so: sync quickly to the current state of the network. To do so:
```shell ```shell
$ geth console $ geth console
``` ```
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 the built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interacting-with-geth/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://github.com/ChainSafe/web3.js/blob/0.20.7/DOCUMENTATION.md)
(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/interacting-with-geth/rpc).
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 it to an already running
`geth` instance with `geth attach`. `geth` instance with `geth attach`.
### A Full node on the Görli test network ### A Full node on the Görli test network
@ -86,12 +114,12 @@ the main network, but with play-Ether only.
$ geth --goerli console $ geth --goerli console
``` ```
The `console` subcommand has the exact same meaning as above and they are equally The `console` subcommand has the same meaning as above and is equally
useful on the testnet too. Please, see above for their explanations if you've skipped here. useful on the testnet too.
Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit: Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit:
* Instead of connecting the main Ethereum network, the client will connect to the Görli * Instead of connecting to the main Ethereum network, the client will connect to the Görli
test network, which uses different P2P bootnodes, different network IDs and genesis test network, which uses different P2P bootnodes, different network IDs and genesis
states. states.
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth` * Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
@ -102,34 +130,21 @@ Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a
`geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by `geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by
this. this.
*Note: Although there are some internal protective measures to prevent transactions from *Note: Although some internal protective measures prevent transactions from
crossing over between the main network and test network, you should make sure to always crossing over between the main network and test network, you should always
use separate accounts for play-money and real-money. Unless you manually move use separate accounts for play and real money. Unless you manually move
accounts, `geth` will by default correctly separate the two networks and will not make any accounts, `geth` will by default correctly separate the two networks and will not make any
accounts available between them.* accounts available between them._
### Full node on the Rinkeby test network ### Full node on the Rinkeby test network
Go Ethereum also supports connecting to the older proof-of-authority based test network Go Ethereum also supports connecting to the older proof-of-authority based test network
called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community. called [_Rinkeby_](https://www.rinkeby.io) which is operated by members of the community.
```shell ```shell
$ geth --rinkeby console $ geth --rinkeby console
``` ```
### Full node on the Ropsten test network
In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The
Ropsten test network is based on the Ethash proof-of-work consensus algorithm. As such,
it has certain extra overhead and is more susceptible to reorganization attacks due to the
network's low difficulty/security.
```shell
$ geth --ropsten console
```
*Note: Older Geth configurations store the Ropsten database in the `testnet` subdirectory.*
### Configuration ### Configuration
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a As an alternative to passing the numerous flags to the `geth` binary, you can also pass a
@ -139,14 +154,14 @@ configuration file via:
$ geth --config /path/to/your_config.toml $ geth --config /path/to/your_config.toml
``` ```
To get an idea how the file should look like you can use the `dumpconfig` subcommand to To get an idea of how the file should look like you can use the `dumpconfig` subcommand to
export your existing configuration: export your existing configuration:
```shell ```shell
$ geth --your-favourite-flags dumpconfig $ geth --your-favourite-flags dumpconfig
``` ```
*Note: This works only with `geth` v1.6.0 and above.* _Note: This works only with `geth` v1.6.0 and above._
#### Docker quick start #### Docker quick start
@ -159,21 +174,21 @@ docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
ethereum/client-go ethereum/client-go
``` ```
This will start `geth` in fast-sync mode with a DB memory allowance of 1GB just as the This will start `geth` in snap-sync mode with a DB memory allowance of 1GB, as the
above command does. It will also create a persistent volume in your home directory for above command does. It will also create a persistent volume in your home directory for
saving your blockchain as well as map the default ports. There is also an `alpine` tag saving your blockchain as well as map the default ports. There is also an `alpine` tag
available for a slim version of the image. available for a slim version of the image.
Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers
and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not and/or hosts. By default, `geth` binds to the local interface and RPC endpoints are not
accessible from the outside. accessible from the outside.
### Programmatically interfacing `geth` nodes ### Programmatically interfacing `geth` nodes
As a developer, sooner rather than later you'll want to start interacting with `geth` and the As a developer, sooner rather than later you'll want to start interacting with `geth` and the
Ethereum network via your own programs and not manually through the console. To aid Ethereum network via your own programs and not manually through the console. To aid
this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://eth.wiki/json-rpc/API) this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://ethereum.github.io/execution-apis/api-documentation/)
and [`geth` specific APIs](https://geth.ethereum.org/docs/rpc/server)). and [`geth` specific APIs](https://geth.ethereum.org/docs/interacting-with-geth/rpc)).
These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based
platforms, and named pipes on Windows). platforms, and named pipes on Windows).
@ -193,9 +208,9 @@ HTTP based JSON-RPC API options:
* `--ws.addr` WS-RPC server listening interface (default: `localhost`) * `--ws.addr` WS-RPC server listening interface (default: `localhost`)
* `--ws.port` WS-RPC server listening port (default: `8546`) * `--ws.port` WS-RPC server listening port (default: `8546`)
* `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`) * `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`)
* `--ws.origins` Origins from which to accept websockets requests * `--ws.origins` Origins from which to accept WebSocket requests
* `--ipcdisable` Disable the IPC-RPC server * `--ipcdisable` Disable the IPC-RPC server
* `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,shh,txpool,web3`) * `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,txpool,web3`)
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it) * `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to You'll need to use your own programming environments' capabilities (libraries, tools, etc) to
@ -231,7 +246,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",
@ -280,13 +296,13 @@ $ bootnode --genkey=boot.key
$ bootnode --nodekey=boot.key $ bootnode --nodekey=boot.key
``` ```
With the bootnode online, it will display an [`enode` URL](https://eth.wiki/en/fundamentals/enode-url-format) With the bootnode online, it will display an [`enode` URL](https://ethereum.org/en/developers/docs/networking-layer/network-addresses/#enode)
that other nodes can use to connect to it and exchange peer information. Make sure to that other nodes can use to connect to it and exchange peer information. Make sure to
replace the displayed IP address information (most probably `[::]`) with your externally replace the displayed IP address information (most probably `[::]`) with your externally
accessible IP to get the actual `enode` URL. accessible IP to get the actual `enode` URL.
*Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less _Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less
recommended way.* recommended way._
#### Starting up your member nodes #### Starting up your member nodes
@ -300,17 +316,13 @@ do also specify a custom `--datadir` flag.
$ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above> $ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above>
``` ```
*Note: Since your network will be completely cut off from the main and test networks, you'll _Note: Since your network will be completely cut off from the main and test networks, you'll
also need to configure a miner to process transactions and create new blocks for you.* also need to configure a miner to process transactions and create new blocks for you._
#### Running a private miner #### Running a private miner
Mining on the public Ethereum network is a complex task as it's only feasible using GPUs,
requiring an OpenCL or CUDA enabled `ethminer` instance. For information on such a
setup, please consult the [EtherMining subreddit](https://www.reddit.com/r/EtherMining/)
and the [ethminer](https://github.com/ethereum-mining/ethminer) repository.
In a private network setting, however a single CPU miner instance is more than enough for In a private network setting a single CPU miner instance is more than enough for
practical purposes as it can produce a stable stream of blocks at the correct intervals practical purposes as it can produce a stable stream of blocks at the correct intervals
without needing heavy resources (consider running on a single thread, no need for multiple without needing heavy resources (consider running on a single thread, no need for multiple
ones either). To start a `geth` instance for mining, run it with all your usual flags, extended ones either). To start a `geth` instance for mining, run it with all your usual flags, extended
@ -327,36 +339,42 @@ transactions are accepted at (`--miner.gasprice`).
## Contribution ## Contribution
Thank you for considering to help out with the source code! We welcome contributions Thank you for considering helping out with the source code! We welcome contributions
from anyone on the internet, and are grateful for even the smallest of fixes! 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.
Please make sure your contributions adhere to our coding guidelines: Please make sure your contributions adhere to our coding guidelines:
* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) - Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting)
guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) - Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary)
guidelines. guidelines.
* Pull requests need to be based on and opened against the `master` branch. - Pull requests need to be based on and opened against the `master` branch.
* Commit messages should be prefixed with the package(s) they modify. - Commit messages should be prefixed with the package(s) they modify.
* E.g. "eth, rpc: make trace configs optional" - E.g. "eth, rpc: make trace configs optional"
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide) Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/geth-developer/dev-guide)
for more details on configuring your environment, managing project dependencies, and for more details on configuring your environment, managing project dependencies, and
testing procedures. testing procedures.
### Contributing to geth.ethereum.org
For contributions to the [go-ethereum website](https://geth.ethereum.org), please checkout and raise pull requests against the `website` branch.
For more detailed instructions please see the `website` branch [README](https://github.com/ethereum/go-ethereum/tree/website#readme) or the
[contributing](https://geth.ethereum.org/docs/developers/geth-developer/contributing) page of the website.
## License ## License
The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html), [GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
also included in our repository in the `COPYING.LESSER` file. also included in our repository in the `COPYING.LESSER` file.
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the The go-ethereum binaries (i.e. all code inside of the `cmd` directory) are licensed under the
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
included in our repository in the `COPYING` file. included in our repository in the `COPYING` file.

View File

@ -12,12 +12,14 @@ 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
**Please do not file a public ticket** mentioning the vulnerability. **Please do not file a public ticket** mentioning the vulnerability.
To find out how to disclose a vulnerability in Ethereum visit [https://bounty.ethereum.org](https://bounty.ethereum.org) or email bounty@ethereum.org. Please read the [disclosure page](https://github.com/ethereum/go-ethereum/security/advisories?state=published) for more information about publically disclosed security vulnerabilities. To find out how to disclose a vulnerability in Ethereum visit [https://bounty.ethereum.org](https://bounty.ethereum.org) or email bounty@ethereum.org. Please read the [disclosure page](https://github.com/ethereum/go-ethereum/security/advisories?state=published) for more information about publicly disclosed security vulnerabilities.
Use the built-in `geth version-check` feature to check whether the software is affected by any known vulnerability. This command will fetch the latest [`vulnerabilities.json`](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json) file which contains known security vulnerabilities concerning `geth`, and cross-check the data against its own version number. Use the built-in `geth version-check` feature to check whether the software is affected by any known vulnerability. This command will fetch the latest [`vulnerabilities.json`](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json) file which contains known security vulnerabilities concerning `geth`, and cross-check the data against its own version number.
@ -27,92 +29,147 @@ Fingerprint: `AE96 ED96 9E47 9B00 84F3 E17F E88D 3334 FA5F 6A0A`
``` ```
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1 Version: SKS 1.1.6
Comment: Hostname: pgp.mit.edu
mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaY mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaYneAk3Bp1
neAk3Bp182GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9 82GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9L8c8yiqry1ZTCmYM
L8c8yiqry1ZTCmYMqCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUi qCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUim+y7buJDtoNf7YILlhDQXN8q
m+y7buJDtoNf7YILlhDQXN8qlHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0b lHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0bfUo9pexOn7LS4SojoJmsm/5dp6AoKlac
fUo9pexOn7LS4SojoJmsm/5dp6AoKlac48cZU5zwR9AYcq/nvkrfmf2WkObg/xRd 48cZU5zwR9AYcq/nvkrfmf2WkObg/xRdEvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/y
EvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/yPFE335k+ujjZCPOu7OwjzDk7 PFE335k+ujjZCPOu7OwjzDk7M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXCho
M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXChoyI8vbfp4dGvCvYqv yI8vbfp4dGvCvYqvQAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+F
QAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+FnQOUgg2H nQOUgg2Hh8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c
h8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c 2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZEZCjMXxB
2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZ 8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQABtDRFdGhlcmV1bSBG
EZCjMXxB8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQAB b3VuZGF0aW9uIEJ1ZyBCb3VudHkgPGJvdW50eUBldGhlcmV1bS5vcmc+iQIcBBEBCAAGBQJa
tDlFdGhlcmV1bSBGb3VuZGF0aW9uIFNlY3VyaXR5IFRlYW0gPHNlY3VyaXR5QGV0 FCY6AAoJEHoMA3Q0/nfveH8P+gJBPo9BXZL8isUfbUWjwLi81Yi70hZqIJUnz64SWTqBzg5b
aGVyZXVtLm9yZz6JAj4EEwECACgCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheA mCZ69Ji5637THsxQetS2ARabz0DybQ779FhD/IWnqV9T3KuBM/9RzJtuhLzKCyMrAINPMo28
BQJaCWH6BQkFo2BYAAoJEOiNMzT6X2oK+DEP/3H6dxkm0hvHZKoHLVuuxcu3EHYo rKWdunHHarpuR4m3tL2zWJkle5QVYb+vkZXJJE98PJw+N4IYeKKeCs2ubeqZu636GA0sMzzB
k5sd3MMWPrZSN8qzZnY7ayEDMxnarWOizc+2jfOxfJlzX/g8lR1/fsHdWPFPhPoV Jn3m/dRRA2va+/zzbr6F6b51ynzbMxWKTsJnstjC8gs8EeI+Zcd6otSyelLtCUkk3h5sTvpV
Qk8ygrHn1H8U8+rpw/U03BqmqHpYCDzJ+CIis9UWROniqXw1nuqu/FtWOsdWxNKh Wv67BNSU0BYsMkxyFi9PUyy07Wixgeas89K5jG1oOtDva/FkpRHrTE/WA5OXDRcLrHJM+SwD
jUo6k/0EsaXsxRPzgJv7fEUcVcQ7as/C3x9sy3muc2gvgA4/BKoGPb1/U0GuA8lV CwqcLQqJd09NxwUW1iKeBmPptTiOGu1Gv2o7aEyoaWrHRBO7JuYrQrj6q2B3H1Je0zjAd2qt
fDIDshAggmnSUAg+TuYSAAdoFQ1sKwFMPigcLJF2eyKuK3iUyixJrec/c4LSf3wA 09ni2bLwLn4LA+VDpprNTO+eZDprv09s2oFSU6NwziHybovu0y7X4pADGkK2evOM7c86PohX
cGghbeuqI8INP0Y2zvXDQN2cByxsFAuoZG+m0cyKGaDH2MVUvOKKYqn/03qvrf15 QRQ1M1T16xLj6wP8/Ykwl6v/LUk7iDPXP3GPILnh4YOkwBR3DsCOPn8098xy7FxEELmupRzt
AWAsW0l0yQwOTCo3FbsNzemClm5Bj/xH0E4XuwXwChcMCMOWJrFoxyvCEI+keoQc Cj9oC7YAoweeShgUjBPzb+nGY1m6OcFfbUPBgFyMMfwF6joHbiVIO+39+Ut2g2ysZa7KF+yp
c08/a8/MtS7vBAABXwOziSmm6CNqmzpWrh/fDrjlJlba9U3MxzvqU3IFlTdMratv XqVDqyEkYXsOLb25OC7brt8IJEPgBPwcHK5GNag6RfLxnQV+iVZ9KNH1yQgSiQI+BBMBAgAo
6V+SgX+L25lCzW4NxxUavoB8fAlvo8lxpHKo24FP+RcLQ8XqkU3RiUsgRjQRFOqQ AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAUCWglh+gUJBaNgWAAKCRDojTM0+l9qCgQ2
TaJcsp8mimmiYyf24mNu6b48pi+a5c/eQR9w59emeEUZqsJU+nqv8BWIIp7o4Agh D/4udJpV4zGIZW1yNaVvtd3vfKsTLi7GIRJLUBqVb2Yx/uhnN8jTl/tAhCVosCQ1pzvi9kMl
NYnKjkhPlY5e1fLVfAHIADZFynWwRPkPMJSrBiP5EtcOFxQGHGjRxU/KjXkvE0hV s8qO1vu2kw5EWFFkwK96roI8pTql3VIjwhRVQrCkR7oAk/eUd1U/nt2q6J4UTYeVgqbq4dsI
xYb1PB8pWMTu/beeiQI+BBMBAgAoBQJYJd7YAhsDBQkB4TOABgsJCAcDAgYVCAIJ ZZTRyPJMD667YpuAIcaah+w9j/E5xksYQdMeprnDrQkkBCb4FIMqfDzBPKvEa8DcQr949K85
CgsEFgIDAQIeAQIXgAAKCRDojTM0+l9qCplDD/9IZ2i+m1cnqQKtiyHbyFGx32oL kxhr6LDq9i5l4Egxt2JdH8DaR4GLca6+oHy0MyPs/bZOsfmZUObfM2oZgPpqYM96JanhzO1j
fzqPylX2bOG5DPsSTorSUdJMGVfT04oVxXc4S/2DVnNvi7RAbSiLapCWSplgtBOj dpnItyBii2pc+kNx5nMOf4eikE/MBv+WUJ0TttWzApGGmFUzDhtuEvRH9NBjtJ/pMrYspIGu
j1xlblOoXxT3m7s1XHGCX5tENxI9fVSSPVKJn+fQaWpPB2MhBA+1lUI6GJ+11T7K O/QNY5KKOKQTvVIlwGcm8dTsSkqtBDSUwZyWbfKfKOI1/RhM9dC3gj5/BOY57DYYV4rdTK01
J8LrP/fiw1/nOb7rW61HW44Gtyox23sA/d1+DsFVaF8hxJlNj5coPKr8xWzQ8pQl ZtYjuhdfs2bhuP1uF/cgnSSZlv8azvf7Egh7tHPnYxvLjfq1bJAhCIX0hNg0a81/ndPAEFky
juzdjHDukjevuw4rRmRq9vozvj9keEU9XJ5dldyEVXFmdDk7KT0p0Rla9nxYhzf/ fSko+JPKvdSvsUcSi2QQ4U2HX//jNBjXRfG4F0utgbJnhXzEckz6gqt7wSDZH2oddVuO8Ssc
r/Bv8Bzy0HCWRb2D31BjXXGG05oVnYmNGxGFxYja4MwgrMmne3ilEVjfUJsapsqi T7sK+CdXthSKnRyuI+sGUpG+6glpKWIfYkWFKNZWuQ+YUatY3QEDHXTIioycSmV8p4d/g/0S
w41BAyQgIdfREulYN7ahsF5PrjVAqBd9IGtE8ULelF2SQxEBQBngEkP0ahP6tRAL V6TegidLxY8bXMkbqz+3n6FArRffv5MH7qt3cYkCPgQTAQIAKAUCWCXhOwIbAwUJAeEzgAYL
i7/CBjPKOyKijtqVny7qrGOnU2ygcA88/WDibexDhrjz0Gx8WmErU7rIWZiZ5u4Y CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6I0zNPpfagrN/w/+Igp3vtYdNunikw3yHnYf
vJYVRo0+6rBCXRPeSJfiP5h1p17Anr2l42boAYslfcrzquB8MHtrNcyn650OLtHG Jkm0MmaMDUM9mtsaXVN6xb9n25N3Xa3GWCpmdsbYZ8334tI/oQ4/NHq/bEI5WFH5F1aFkMkm
nbxgIdniKrpuzGN6Opw+O2id2JhD1/1p4SOemwAmthplr1MIyOHNP3q93rEj2J7h 5AJVLuUkipCtmCZ5NkbRPJA9l0uNUUE6uuFXBhf4ddu7jb0jMetRF/kifJHVCCo5fISUNhLp
5zPS/AJuKkMDFUpslPNLQjCOwPXtdzL7/kUZGBSyez1T3TaW1uY6l9XaJJRaSn+v 7bwcWq9qgDQNZNYMOo4s9WX5Tl+5x4gTZdd2/cAYt49h/wnkw+huM+Jm0GojpLqIQ1jZiffm
1zPgfp4GJ3lPs4AlAbQ0RXRoZXJldW0gRm91bmRhdGlvbiBCdWcgQm91bnR5IDxi otf5rF4L+JhIIdW0W4IIh1v9BhHVllXw+z9oj0PALstT5h8/DuKoIiirFJ4DejU85GR1KKAS
b3VudHlAZXRoZXJldW0ub3JnPokCPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC DeO19G/lSpWj1rSgFv2N2gAOxq0X+BbQTua2jdcY6JpHR4H1JJ2wzfHsHPgDQcgY1rGlmjVF
AwECHgECF4AFAloJYfoFCQWjYFgACgkQ6I0zNPpfagoENg/+LnSaVeMxiGVtcjWl aqU73WV4/hzXc/HshK/k4Zd8uD4zypv6rFsZ3UemK0aL2zXLVpV8SPWQ61nS03x675SmDlYr
b7Xd73yrEy4uxiESS1AalW9mMf7oZzfI05f7QIQlaLAkNac74vZDJbPKjtb7tpMO A80ENfdqvsn00JQuBVIv4Tv0Ub7NfDraDGJCst8rObjBT/0vnBWTBCebb2EsnS2iStIFkWdz
RFhRZMCveq6CPKU6pd1SI8IUVUKwpEe6AJP3lHdVP57dquieFE2HlYKm6uHbCGWU /WXs4L4Yzre1iJwqRjiuqahZR5jHsjAUf2a0O29HVHE7zlFtCFmLPClml2lGQfQOpm5klGZF
0cjyTA+uu2KbgCHGmofsPY/xOcZLGEHTHqa5w60JJAQm+BSDKnw8wTyrxGvA3EK/ rmvus+qZ9rt35UgWHPZezykkwtWrFOwspwuCWaPDto6tgbRJZ4ftitpdYYM3dKW9IGJXBwrt
ePSvOZMYa+iw6vYuZeBIMbdiXR/A2keBi3GuvqB8tDMj7P22TrH5mVDm3zNqGYD6 BQrMsu+lp0vDF+yJAlUEEwEIAD8CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAFiEErpbt
amDPeiWp4cztY3aZyLcgYotqXPpDceZzDn+HopBPzAb/llCdE7bVswKRhphVMw4b lp5HmwCE8+F/6I0zNPpfagoFAmEAEJwFCQycmLgACgkQ6I0zNPpfagpWoBAAhOcbMAUw6Zt0
bhL0R/TQY7Sf6TK2LKSBrjv0DWOSijikE71SJcBnJvHU7EpKrQQ0lMGclm3ynyji GYzT3sR5/c0iatezPzXEXJf9ebzR8M5uPElXcxcnMx1dvXZmGPXPJKCPa99WCu1NZYy8F+Wj
Nf0YTPXQt4I+fwTmOew2GFeK3UytNWbWI7oXX7Nm4bj9bhf3IJ0kmZb/Gs73+xII GTOY9tfIkvSxhys1p/giPAmvid6uQmD+bz7ivktnyzCkDWfMA+l8lsCSEqVlaq6y5T+a6SWB
e7Rz52Mby436tWyQIQiF9ITYNGvNf53TwBBZMn0pKPiTyr3Ur7FHEotkEOFNh1// 6TzC2S0MPb/RrC/7DpwyrNYWumvyVJh09adm1Mw/UGgst/sZ8eMaRYEd3X0yyT1CBpX4zp2E
4zQY10XxuBdLrYGyZ4V8xHJM+oKre8Eg2R9qHXVbjvErHE+7CvgnV7YUip0criPr qQj9IEOTizvzv1x2jkHe5ZUeU3+nTBNlhSA+WFHUi0pfBdo2qog3Mv2EC1P2qMKoSdD5tPbA
BlKRvuoJaSliH2JFhSjWVrkPmFGrWN0BAx10yIqMnEplfKeHf4P9Elek3oInS8WP zql1yKoHHnXOMsqdftGwbiv2sYXWvrYvmaCd3Ys/viOyt3HOy9uV2ZEtBd9Yqo9x/NZj8QMA
G1zJG6s/t5+hQK0X37+TB+6rd3GJAj4EEwECACgFAlgl4TsCGwMFCQHhM4AGCwkI nY5k8jjrIXbUC89MqrJsQ6xxWQIg5ikMT7DvY0Ln89ev4oJyVvwIQAwCm4jUzFNm9bZLYDOP
BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOiNMzT6X2oKzf8P/iIKd77WHTbp4pMN 5lGJCV7tF5NYVU7NxNM8vescKc40mVNK/pygS5mxhK9QYOUjZsIv8gddrl1TkqrFMuxFnTyN
8h52HyZJtDJmjA1DPZrbGl1TesW/Z9uTd12txlgqZnbG2GfN9+LSP6EOPzR6v2xC WvzE29wFu/n4N1DkF+ZBqS70SlRvB+Hjz5LrDgEzF1Wf1eA/wq1dZbvMjjDVIc2VGlYp8Cp2
OVhR+RdWhZDJJuQCVS7lJIqQrZgmeTZG0TyQPZdLjVFBOrrhVwYX+HXbu429IzHr 8ob23c1seTtYXTNYgSR5go4EpH+xi+bIWv01bQQ9xGwBbT5sm4WUeWOcmX4QewzLZ3T/wK9+
URf5InyR1QgqOXyElDYS6e28HFqvaoA0DWTWDDqOLPVl+U5fuceIE2XXdv3AGLeP N4Ye/hmU9O34FwWJOY58EIe0OUV0aGVyZXVtIEZvdW5kYXRpb24gU2VjdXJpdHkgVGVhbSA8
Yf8J5MPobjPiZtBqI6S6iENY2Yn35qLX+axeC/iYSCHVtFuCCIdb/QYR1ZZV8Ps/ c2VjdXJpdHlAZXRoZXJldW0ub3JnPokCHAQRAQgABgUCWhQmOgAKCRB6DAN0NP5372LSEACT
aI9DwC7LU+YfPw7iqCIoqxSeA3o1PORkdSigEg3jtfRv5UqVo9a0oBb9jdoADsat wZk1TASWZj5QF7rmkIM1GEyBxLE+PundNcMgM9Ktj1315ED8SmiukNI4knVS1MY99OIgXhQl
F/gW0E7mto3XGOiaR0eB9SSdsM3x7Bz4A0HIGNaxpZo1RWqlO91leP4c13Px7ISv D1foF2GKdTomrwwC4012zTNyUYCY60LnPZ6Z511HG+rZgZtZrbkz0IiUpwAlhGQND77lBqem
5OGXfLg+M8qb+qxbGd1HpitGi9s1y1aVfEj1kOtZ0tN8eu+Upg5WKwPNBDX3ar7J J3K+CFX2XpDA/ojui/kqrY4cwMT5P8xPJkwgpRgw/jgdcZyJTsXdHblV9IGU4H1Vd1SgcfAf
9NCULgVSL+E79FG+zXw62gxiQrLfKzm4wU/9L5wVkwQnm29hLJ0tokrSBZFnc/1l Db3YxDUlBtzlp0NkZqxen8irLIXUQvsfuIfRUbUSkWoK/n3U/gOCajAe8ZNF07iX4OWjH4Sw
7OC+GM63tYicKkY4rqmoWUeYx7IwFH9mtDtvR1RxO85RbQhZizwpZpdpRkH0DqZu NDA841WhFWcGE+d8+pfMVfPASU3UPKH72uw86b2VgR46Av6voyMFd1pj+yCA+YAhJuOpV4yL
ZJRmRa5r7rPqmfa7d+VIFhz2Xs8pJMLVqxTsLKcLglmjw7aOrYG0SWeH7YraXWGD QaGg2Z0kVOjuNWK/kBzp1F58DWGh4YBatbhE/UyQOqAAtR7lNf0M3QF9AdrHTxX8oZeqVW3V
N3SlvSBiVwcK7QUKzLLvpadLwxfsuQINBFgl3tgBEACbgq6HTN5gEBi0lkD/MafI Fmi2mk0NwCIUv8SSrZr1dTchp04OtyXe5gZBXSfzncCSRQIUDC8OgNWaOzAaUmK299v4bvye
nmNi+59U5gRGYqk46WlfRjhHudXjDpgD0lolGb4hYontkMaKRlCg2Rvgjvk3Zve0 uSCxOysxC7Q1hZtjzFPKdljS81mRlYeUL4fHlJU9R57bg8mriSXLmn7eKrSEDm/EG5T8nRx7
PKWjKw7gr8YBa9fMFY8BhAXI32OdyI9rFhxEZFfWAfwKVmT19BdeAQRFvcfd+8w8 TgX2MqJs8sWFxD2+bboVEu75yuFmZ//nmCBApAit9Hr2/sCshGIEpa9MQ6xJCYUxyqeJH+Cc
f1XVc+zddULMJFBTr+xKDlIRWwTkdLPQeWbjo0eHl/g4tuLiLrTxVbnj26bf+2+1 Aja0UfXhnK2uvPClpJLIl4RE3gm4OXeE1IkCPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC
DbM/w5VavzPrkviHqvKe/QP/gay4QDViWvFgLb90idfAHIdsPgflp0VDS5rVHFL6 AwECHgECF4AFAloJYfoFCQWjYFgACgkQ6I0zNPpfagr4MQ//cfp3GSbSG8dkqgctW67Fy7cQ
D73rSRdIRo3I8c8mYoNjSR4XDuvgOkAKW9LR3pvouFHHjp6Fr0GesRbrbb2EG66i diiTmx3cwxY+tlI3yrNmdjtrIQMzGdqtY6LNz7aN87F8mXNf+DyVHX9+wd1Y8U+E+hVCTzKC
PsR99MQ7FqIL9VMHPm2mtR+XvbnKkH2rYyEqaMbSdk29jGapkAWle4sIhSKk749A sefUfxTz6unD9TTcGqaoelgIPMn4IiKz1RZE6eKpfDWe6q78W1Y6x1bE0qGNSjqT/QSxpezF
4tGkHl08KZ2N9o6GrfUehP/V2eJLaph2DioFL1HxRryrKy80QQKLMJRekxigq8gr E/OAm/t8RRxVxDtqz8LfH2zLea5zaC+ADj8EqgY9vX9TQa4DyVV8MgOyECCCadJQCD5O5hIA
eW8xB4zuf9Mkuou+RHNmo8PebHjFstLigiD6/zP2e+4tUmrT0/JTGOShoGMl8Rt0 B2gVDWwrAUw+KBwskXZ7Iq4reJTKLEmt5z9zgtJ/fABwaCFt66ojwg0/RjbO9cNA3ZwHLGwU
VRxdPImKun+4LOXbfOxArOSkY6i35+gsgkkSy1gTJE0BY3S9auT6+YrglY/TWPQ9 C6hkb6bRzIoZoMfYxVS84opiqf/Teq+t/XkBYCxbSXTJDA5MKjcVuw3N6YKWbkGP/EfQThe7
IJxWVOKlT+3WIp5wJu2bBKQ420VLqDYzkoWytel/bM1ACUtipMiIVeUs2uFiRjpz BfAKFwwIw5YmsWjHK8IQj6R6hBxzTz9rz8y1Lu8EAAFfA7OJKaboI2qbOlauH98OuOUmVtr1
A1Wy0QHKPTdSuGlJPRrfcQARAQABiQIlBBgBAgAPAhsMBQJaCWIIBQkFo2BYAAoJ TczHO+pTcgWVN0ytq2/pX5KBf4vbmULNbg3HFRq+gHx8CW+jyXGkcqjbgU/5FwtDxeqRTdGJ
EOiNMzT6X2oKgSwQAKKs7BGF8TyZeIEO2EUK7R2bdQDCdSGZY06tqLFg3IHMGxDM SyBGNBEU6pBNolyynyaKaaJjJ/biY27pvjymL5rlz95BH3Dn16Z4RRmqwlT6eq/wFYginujg
b/7FVoa2AEsFgv6xpoebxBB5zkhUk7lslgxvKiSLYjxfNjTBltfiFJ+eQnf+OTs8 CCE1icqOSE+Vjl7V8tV8AcgANkXKdbBE+Q8wlKsGI/kS1w4XFAYcaNHFT8qNeS8TSFXFhvU8
KeR51lLa66rvIH2qUzkNDCCTF45H4wIDpV05AXhBjKYkrDCrtey1rQyFp5fxI+0I HylYxO79t56JAj4EEwECACgFAlgl3tgCGwMFCQHhM4AGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
Q1UKKXvzZK4GdxhxDbOUSd38MYy93nqcmclGSGK/gF8XiyuVjeifDCM6+T1NQTX0 AheAAAoJEOiNMzT6X2oKmUMP/0hnaL6bVyepAq2LIdvIUbHfagt/Oo/KVfZs4bkM+xJOitJR
K9lneidcqtBDvlggJTLJtQPO33o5EHzXSiud+dKth1uUhZOFEaYRZoye1YE3yB0T 0kwZV9PTihXFdzhL/YNWc2+LtEBtKItqkJZKmWC0E6OPXGVuU6hfFPebuzVccYJfm0Q3Ej19
NOOE8fXlvu8iuIAMBSDL9ep6sEIaXYwoD60I2gHdWD0lkP0DOjGQpi4ouXM3Edsd VJI9Uomf59Bpak8HYyEED7WVQjoYn7XVPsonwus/9+LDX+c5vutbrUdbjga3KjHbewD93X4O
5MTi0MDRNTij431kn8T/D0LCgmoUmYYMBgbwFhXr67axPZlKjrqR0z3F/Elv0ZPP wVVoXyHEmU2Plyg8qvzFbNDylCWO7N2McO6SN6+7DitGZGr2+jO+P2R4RT1cnl2V3IRVcWZ0
cVg1tNznsALYQ9Ovl6b5M3cJ5GapbbvNWC7yEE1qScl9HiMxjt/H6aPastH63/7w OTspPSnRGVr2fFiHN/+v8G/wHPLQcJZFvYPfUGNdcYbTmhWdiY0bEYXFiNrgzCCsyad7eKUR
cN0TslW+zRBy05VNJvpWGStQXcngsSUeJtI1Gd992YNjUJq4/Lih6Z1TlwcFVap+ WN9QmxqmyqLDjUEDJCAh19ES6Vg3tqGwXk+uNUCoF30ga0TxQt6UXZJDEQFAGeASQ/RqE/q1
cTcDptoUvXYGg/9mRNNPZwErSfIJ0Ibnx9wPVuRN6NiCLOt2mtKp2F1pM6AOQPpZ EAuLv8IGM8o7IqKO2pWfLuqsY6dTbKBwDzz9YOJt7EOGuPPQbHxaYStTushZmJnm7hi8lhVG
85vEh6I8i6OaO0w/Z0UHBwvpY6jDUliaROsWUQsqz78Z34CVj4cy6vPW2EF4 jT7qsEJdE95Il+I/mHWnXsCevaXjZugBiyV9yvOq4Hwwe2s1zKfrnQ4u0cadvGAh2eIqum7M
=r6KK Y3o6nD47aJ3YmEPX/WnhI56bACa2GmWvUwjI4c0/er3esSPYnuHnM9L8Am4qQwMVSmyU80tC
-----END PGP PUBLIC KEY BLOCK----- MI7A9e13Mvv+RRkYFLJ7PVPdNpbW5jqX1doklFpKf6/XM+B+ngYneU+zgCUBiQJVBBMBCAA/
AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgBYhBK6W7ZaeR5sAhPPhf+iNMzT6X2oKBQJh
ABCQBQkMnJi4AAoJEOiNMzT6X2oKAv0P+gJ3twBp5efNWyVLcIg4h4cOo9uD0NPvz8/fm2gX
FoOJL3MeigtPuSVfE9kuTaTuRbArzuFtdvH6G/kcRQvOlO4zyiIRHCk1gDHoIvvtn6RbRhVm
/Xo4uGIsFHst7n4A7BjicwEK5Op6Ih5Hoq19xz83YSBgBVk2fYEJIRyJiKFbyPjH0eSYe8v+
Ra5/F85ugLx1P6mMVkW+WPzULns89riW7BGTnZmXFHZp8nO2pkUlcI7F3KRG7l4kmlC50ox6
DiG/6AJCVulbAClky9C68TmJ/R1RazQxU/9IqVywsydq66tbJQbm5Z7GEti0C5jjbSRJL2oT
1xC7Rilr85PMREkPL3vegJdgj5PKlffZ/MocD/0EohiQ7wFpejFD4iTljeh0exRUwCRb6655
9ib34JSQgU8Hl4JJu+mEgd9v0ZHD0/1mMD6fnAR84zca+O3cdASbnQmzTOKcGzLIrkE8TEnU
+2UZ8Ol7SAAqmBgzY1gKOilUho6dkyCAwNL+QDpvrITDPLEFPsjyB/M2KudZSVEn+Rletju1
qkMW31qFMNlsbwzMZw+0USeGcs31Cs0B2/WQsro99CExlhS9auUFkmoVjJmYVTIYOM0zuPa4
OyGspqPhRu5hEsmMDPDWD7Aad5k4GTqogQNnuKyRliZjXXrDZqFD5nfsJSL8Ky/sJGEMuQIN
BFgl3tgBEACbgq6HTN5gEBi0lkD/MafInmNi+59U5gRGYqk46WlfRjhHudXjDpgD0lolGb4h
YontkMaKRlCg2Rvgjvk3Zve0PKWjKw7gr8YBa9fMFY8BhAXI32OdyI9rFhxEZFfWAfwKVmT1
9BdeAQRFvcfd+8w8f1XVc+zddULMJFBTr+xKDlIRWwTkdLPQeWbjo0eHl/g4tuLiLrTxVbnj
26bf+2+1DbM/w5VavzPrkviHqvKe/QP/gay4QDViWvFgLb90idfAHIdsPgflp0VDS5rVHFL6
D73rSRdIRo3I8c8mYoNjSR4XDuvgOkAKW9LR3pvouFHHjp6Fr0GesRbrbb2EG66iPsR99MQ7
FqIL9VMHPm2mtR+XvbnKkH2rYyEqaMbSdk29jGapkAWle4sIhSKk749A4tGkHl08KZ2N9o6G
rfUehP/V2eJLaph2DioFL1HxRryrKy80QQKLMJRekxigq8greW8xB4zuf9Mkuou+RHNmo8Pe
bHjFstLigiD6/zP2e+4tUmrT0/JTGOShoGMl8Rt0VRxdPImKun+4LOXbfOxArOSkY6i35+gs
gkkSy1gTJE0BY3S9auT6+YrglY/TWPQ9IJxWVOKlT+3WIp5wJu2bBKQ420VLqDYzkoWytel/
bM1ACUtipMiIVeUs2uFiRjpzA1Wy0QHKPTdSuGlJPRrfcQARAQABiQIlBBgBAgAPAhsMBQJa
CWIIBQkFo2BYAAoJEOiNMzT6X2oKgSwQAKKs7BGF8TyZeIEO2EUK7R2bdQDCdSGZY06tqLFg
3IHMGxDMb/7FVoa2AEsFgv6xpoebxBB5zkhUk7lslgxvKiSLYjxfNjTBltfiFJ+eQnf+OTs8
KeR51lLa66rvIH2qUzkNDCCTF45H4wIDpV05AXhBjKYkrDCrtey1rQyFp5fxI+0IQ1UKKXvz
ZK4GdxhxDbOUSd38MYy93nqcmclGSGK/gF8XiyuVjeifDCM6+T1NQTX0K9lneidcqtBDvlgg
JTLJtQPO33o5EHzXSiud+dKth1uUhZOFEaYRZoye1YE3yB0TNOOE8fXlvu8iuIAMBSDL9ep6
sEIaXYwoD60I2gHdWD0lkP0DOjGQpi4ouXM3Edsd5MTi0MDRNTij431kn8T/D0LCgmoUmYYM
BgbwFhXr67axPZlKjrqR0z3F/Elv0ZPPcVg1tNznsALYQ9Ovl6b5M3cJ5GapbbvNWC7yEE1q
Scl9HiMxjt/H6aPastH63/7wcN0TslW+zRBy05VNJvpWGStQXcngsSUeJtI1Gd992YNjUJq4
/Lih6Z1TlwcFVap+cTcDptoUvXYGg/9mRNNPZwErSfIJ0Ibnx9wPVuRN6NiCLOt2mtKp2F1p
M6AOQPpZ85vEh6I8i6OaO0w/Z0UHBwvpY6jDUliaROsWUQsqz78Z34CVj4cy6vPW2EF4iQIl
BBgBAgAPBQJYJd7YAhsMBQkB4TOAAAoJEOiNMzT6X2oKTjgP/1ojCVyGyvHMLUgnX0zwrR5Q
1M5RKFz6kHwKjODVLR3Isp8I935oTQt3DY7yFDI4t0GqbYRQMtxcNEb7maianhK2trCXfhPs
6/L04igjDf5iTcmzamXN6xnh5xkz06hZJJCMuu4MvKxC9MQHCVKAwjswl/9H9JqIBXAY3E2l
LpX5P+5jDZuPxS86p3+k4Rrdp9KTGXjiuEleM3zGlz5BLWydqovOck7C2aKh27ETFpDYY0z3
yQ5AsPJyk1rAr0wrH6+ywmwWlzuQewavnrLnJ2M8iMFXpIhyHeEIU/f7o8f+dQk72rZ9CGzd
cqig2za/BS3zawZWgbv2vB2elNsIllYLdir45jxBOxx2yvJvEuu4glz78y4oJTCTAYAbMlle
5gVdPkVcGyvvVS9tinnSaiIzuvWrYHKWll1uYPm2Q1CDs06P5I7bUGAXpgQLUh/XQguy/0sX
GWqW3FS5JzP+XgcR/7UASvwBdHylubKbeqEpB7G1s+m+8C67qOrc7EQv3Jmy1YDOkhEyNig1
rmjplLuir3tC1X+D7dHpn7NJe7nMwFx2b2MpMkLA9jPPAGPp/ekcu5sxCe+E0J/4UF++K+CR
XIxgtzU2UJfp8p9x+ygbx5qHinR0tVRdIzv3ZnGsXrfxnWfSOaB582cU3VRN9INzHHax8ETa
QVDnGO5uQa+FiQI8BBgBCAAmAhsMFiEErpbtlp5HmwCE8+F/6I0zNPpfagoFAmEAELYFCQyc
mN4ACgkQ6I0zNPpfagoqAQ/+MnDjBx8JWMd/XjeFoYKx/Oo0ntkInV+ME61JTBls4PdVk+TB
8PWZdPQHw9SnTvRmykFeznXIRzuxkowjrZYXdPXBxY2b1WyD5V3Ati1TM9vqpaR4osyPs2xy
I4dzDssh9YvUsIRL99O04/65lGiYeBNuACq+yK/7nD/ErzBkDYJHhMCdadbVWUACxvVIDvro
yQeVLKMsHqMCd8BTGD7VDs79NXskPnN77pAFnkzS4Z2b8SNzrlgTc5pUiuZHIXPIpEYmsYzh
ucTU6uI3dN1PbSFHK5tG2pHb4ZrPxY3L20Dgc2Tfu5/SDApZzwvvKTqjdO891MEJ++H+ssOz
i4O1UeWKs9owWttan9+PI47ozBSKOTxmMqLSQ0f56Np9FJsV0ilGxRKfjhzJ4KniOMUBA7mP
+m+TmXfVtthJred4sHlJMTJNpt+sCcT6wLMmyc3keIEAu33gsJj3LTpkEA2q+V+ZiP6Q8HRB
402ITklABSArrPSE/fQU9L8hZ5qmy0Z96z0iyILgVMLuRCCfQOMWhwl8yQWIIaf1yPI07xur
epy6lH7HmxjjOR7eo0DaSxQGQpThAtFGwkWkFh8yki8j3E42kkrxvEyyYZDXn2YcI3bpqhJx
PtwCMZUJ3kc/skOrs6bOI19iBNaEoNX5Dllm7UHjOgWNDQkcCuOCxucKano=
=arte
-----END PGP PUBLIC KEY BLOCK------
``` ```

View File

@ -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
@ -86,7 +87,7 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
var args Arguments var args Arguments
if method, ok := abi.Methods[name]; ok { if method, ok := abi.Methods[name]; ok {
if len(data)%32 != 0 { if len(data)%32 != 0 {
return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data) return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
} }
args = method.Outputs args = method.Outputs
} }
@ -94,7 +95,7 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
args = event.Inputs args = event.Inputs
} }
if args == nil { if args == nil {
return nil, errors.New("abi: could not locate named method or event") return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
} }
return args, nil return args, nil
} }
@ -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 := ResolveNameConflict(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,12 @@ 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 := ResolveNameConflict(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":
// Errors cannot be overloaded or overridden but are inherited,
// no need to resolve the name conflict here.
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 +197,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) {
@ -270,7 +246,10 @@ func UnpackRevert(data []byte) (string, error) {
if !bytes.Equal(data[:4], revertSelector) { if !bytes.Equal(data[:4], revertSelector) {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
typ, _ := NewType("string", "", nil) typ, err := NewType("string", "", nil)
if err != nil {
return "", err
}
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:]) unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
if err != nil { if err != nil {
return "", err return "", err

View File

@ -165,8 +165,9 @@ func TestInvalidABI(t *testing.T) {
// TestConstructor tests a constructor function. // TestConstructor tests a constructor function.
// The test is based on the following contract: // The test is based on the following contract:
// contract TestConstructor { //
// constructor(uint256 a, uint256 b) public{} // contract TestConstructor {
// constructor(uint256 a, uint256 b) public{}
// } // }
func TestConstructor(t *testing.T) { func TestConstructor(t *testing.T) {
json := `[{ "inputs": [{"internalType": "uint256","name": "a","type": "uint256" },{ "internalType": "uint256","name": "b","type": "uint256"}],"stateMutability": "nonpayable","type": "constructor"}]` json := `[{ "inputs": [{"internalType": "uint256","name": "a","type": "uint256" },{ "internalType": "uint256","name": "b","type": "uint256"}],"stateMutability": "nonpayable","type": "constructor"}]`
@ -295,6 +296,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 {
@ -710,16 +725,19 @@ func TestBareEvents(t *testing.T) {
} }
// TestUnpackEvent is based on this contract: // TestUnpackEvent is based on this contract:
// contract T { //
// event received(address sender, uint amount, bytes memo); // contract T {
// event receivedAddr(address sender); // event received(address sender, uint amount, bytes memo);
// function receive(bytes memo) external payable { // event receivedAddr(address sender);
// received(msg.sender, msg.value, memo); // function receive(bytes memo) external payable {
// receivedAddr(msg.sender); // received(msg.sender, msg.value, memo);
// } // receivedAddr(msg.sender);
// } // }
// }
//
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt: // When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} //
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestUnpackEvent(t *testing.T) { func TestUnpackEvent(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON)) abi, err := JSON(strings.NewReader(abiJSON))
@ -1024,9 +1042,7 @@ func TestABI_EventById(t *testing.T) {
} }
if event == nil { if event == nil {
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum) t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
} } else if event.ID != topicID {
if event.ID != topicID {
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID.Hex(), topicID.Hex(), testnum) t.Errorf("Event id %s does not match topic %s, test #%d", event.ID.Hex(), topicID.Hex(), testnum)
} }
@ -1066,8 +1082,9 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
// TestDoubleDuplicateEventNames checks that if send0 already exists, there won't be a name // TestDoubleDuplicateEventNames checks that if send0 already exists, there won't be a name
// conflict and that the second send event will be renamed send1. // conflict and that the second send event will be renamed send1.
// The test runs the abi of the following contract. // The test runs the abi of the following contract.
// contract DuplicateEvent { //
// event send(uint256 a); // contract DuplicateEvent {
// event send(uint256 a);
// event send0(); // event send0();
// event send(); // event send();
// } // }
@ -1094,7 +1111,8 @@ func TestDoubleDuplicateEventNames(t *testing.T) {
// TestUnnamedEventParam checks that an event with unnamed parameters is // TestUnnamedEventParam checks that an event with unnamed parameters is
// correctly handled. // correctly handled.
// The test runs the abi of the following contract. // The test runs the abi of the following contract.
// contract TestEvent { //
// contract TestEvent {
// event send(uint256, uint256); // event send(uint256, uint256);
// } // }
func TestUnnamedEventParam(t *testing.T) { func TestUnnamedEventParam(t *testing.T) {

View File

@ -18,6 +18,7 @@ package abi
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@ -78,16 +79,10 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format. // Unpack performs the operation hexdata -> Go format.
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
if len(data) == 0 { if len(data) == 0 {
if len(arguments) != 0 { if len(arguments.NonIndexed()) != 0 {
return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
} }
// Nothing to unmarshal, return default variables return make([]interface{}, 0), nil
nonIndexedArgs := arguments.NonIndexed()
defaultVars := make([]interface{}, len(nonIndexedArgs))
for index, arg := range nonIndexedArgs {
defaultVars[index] = reflect.New(arg.Type.GetType())
}
return defaultVars, nil
} }
return arguments.UnpackValues(data) return arguments.UnpackValues(data)
} }
@ -96,11 +91,11 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error { func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
// Make sure map is not nil // Make sure map is not nil
if v == nil { if v == nil {
return fmt.Errorf("abi: cannot unpack into a nil map") return errors.New("abi: cannot unpack into a nil map")
} }
if len(data) == 0 { if len(data) == 0 {
if len(arguments) != 0 { if len(arguments.NonIndexed()) != 0 {
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
} }
return nil // Nothing to unmarshal, return return nil // Nothing to unmarshal, return
} }
@ -121,8 +116,8 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v) return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
} }
if len(values) == 0 { if len(values) == 0 {
if len(arguments) != 0 { if len(arguments.NonIndexed()) != 0 {
return fmt.Errorf("abi: attempting to copy no values while %d arguments are expected", len(arguments)) return errors.New("abi: attempting to copy no values while arguments are expected")
} }
return nil // Nothing to copy, return return nil // Nothing to copy, return
} }
@ -137,7 +132,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)
@ -192,6 +187,9 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
virtualArgs := 0 virtualArgs := 0
for index, arg := range nonIndexedArgs { for index, arg := range nonIndexedArgs {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data) marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if err != nil {
return nil, err
}
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) { if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
@ -209,9 +207,6 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
// coded as just like uint256,bool,uint256 // coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(arg.Type)/32 - 1 virtualArgs += getTypeSize(arg.Type)/32 - 1
} }
if err != nil {
return nil, err
}
retval = append(retval, marshalledValue) retval = append(retval, marshalledValue)
} }
return retval, nil return retval, nil

View File

@ -17,10 +17,10 @@
package bind package bind
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
"errors" "errors"
"io" "io"
"io/ioutil"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -44,7 +44,7 @@ var ErrNotAuthorized = errors.New("not authorized to sign this account")
// Deprecated: Use NewTransactorWithChainID instead. // Deprecated: Use NewTransactorWithChainID instead.
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
json, err := ioutil.ReadAll(keyin) json, err := io.ReadAll(keyin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -74,6 +74,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,13 +98,14 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
} }
return tx.WithSignature(signer, signature) return tx.WithSignature(signer, signature)
}, },
Context: context.Background(),
} }
} }
// NewTransactorWithChainID is a utility method to easily create a transaction signer from // NewTransactorWithChainID is a utility method to easily create a transaction signer from
// an encrypted json key stream and the associated passphrase. // an encrypted json key stream and the associated passphrase.
func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
json, err := ioutil.ReadAll(keyin) json, err := io.ReadAll(keyin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -133,6 +135,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 +159,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 +174,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(),
} }
} }

View File

@ -32,12 +32,12 @@ var (
// have any code associated with it (i.e. suicided). // have any code associated with it (i.e. suicided).
ErrNoCode = errors.New("no contract code at given address") ErrNoCode = errors.New("no contract code at given address")
// This error is raised when attempting to perform a pending state action // ErrNoPendingState is raised when attempting to perform a pending state action
// on a backend that doesn't implement PendingContractCaller. // on a backend that doesn't implement PendingContractCaller.
ErrNoPendingState = errors.New("backend does not support pending state") ErrNoPendingState = errors.New("backend does not support pending state")
// This error is returned by WaitDeployed if contract creation leaves an // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
// empty contract behind. // an empty contract behind.
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
) )
@ -47,7 +47,8 @@ type ContractCaller interface {
// CodeAt returns the code of the given account. This is needed to differentiate // CodeAt returns the code of the given account. This is needed to differentiate
// between contract internal errors and the local chain being out of sync. // between contract internal errors and the local chain being out of sync.
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
// ContractCall executes an Ethereum contract call with the specified data as the
// CallContract executes an Ethereum contract call with the specified data as the
// input. // input.
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
} }
@ -58,6 +59,7 @@ type ContractCaller interface {
type PendingContractCaller interface { type PendingContractCaller interface {
// PendingCodeAt returns the code of the given account in the pending state. // PendingCodeAt returns the code of the given account in the pending state.
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
// PendingCallContract executes an Ethereum contract call against the pending state. // PendingCallContract executes an Ethereum contract call against the pending state.
PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
} }
@ -67,19 +69,31 @@ type PendingContractCaller interface {
// used when the user does not provide some needed values, but rather leaves it up // used when the user does not provide some needed values, but rather leaves it up
// to the transactor to decide. // to the transactor to decide.
type ContractTransactor interface { type ContractTransactor interface {
// HeaderByNumber returns a block header from the current canonical chain. If
// number is nil, the latest known header is returned.
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
// PendingCodeAt returns the code of the given account in the pending state. // PendingCodeAt returns the code of the given account in the pending state.
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
// PendingNonceAt retrieves the current pending nonce associated with an account. // PendingNonceAt retrieves the current pending nonce associated with an account.
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction. // execution of a transaction.
SuggestGasPrice(ctx context.Context) (*big.Int, error) SuggestGasPrice(ctx context.Context) (*big.Int, error)
// SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
// a timely execution of a transaction.
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
// EstimateGas tries to estimate the gas needed to execute a specific // EstimateGas tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain. // transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as other // There is no guarantee that this is the true gas limit requirement as other
// transactions may be added or removed by miners, but it should provide a basis // transactions may be added or removed by miners, but it should provide a basis
// for setting a reasonable default. // for setting a reasonable default.
EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
// SendTransaction injects the transaction into the pending pool for execution. // SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(ctx context.Context, tx *types.Transaction) error SendTransaction(ctx context.Context, tx *types.Transaction) error
} }

View File

@ -63,11 +63,13 @@ type SimulatedBackend struct {
database ethdb.Database // In memory database to store our testing data database ethdb.Database // In memory database to store our testing data
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
mu sync.Mutex mu sync.Mutex
pendingBlock *types.Block // Currently pending block that will be imported on request pendingBlock *types.Block // Currently pending block that will be imported on request
pendingState *state.StateDB // Currently pending state that will be the active on request pendingState *state.StateDB // Currently pending state that will be the active on request
pendingReceipts types.Receipts // Currently receipts for the pending block
events *filters.EventSystem // Event system for filtering log events live events *filters.EventSystem // for filtering log events live
filterSystem *filters.FilterSystem // for filtering database logs
config *params.ChainConfig config *params.ChainConfig
} }
@ -76,17 +78,27 @@ type SimulatedBackend struct {
// and uses a simulated blockchain for testing purposes. // and uses a simulated blockchain for testing purposes.
// A simulated backend always uses chainID 1337. // A simulated backend always uses chainID 1337.
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} genesis := core.Genesis{
genesis.MustCommit(database) Config: params.AllEthashProtocolChanges,
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil) GasLimit: gasLimit,
Alloc: alloc,
}
blockchain, _ := core.NewBlockChain(database, nil, &genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
backend := &SimulatedBackend{ backend := &SimulatedBackend{
database: database, database: database,
blockchain: blockchain, blockchain: blockchain,
config: genesis.Config, config: genesis.Config,
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
} }
backend.rollback()
filterBackend := &filterBackend{database, blockchain, backend}
backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{})
backend.events = filters.NewEventSystem(backend.filterSystem, false)
header := backend.blockchain.CurrentBlock()
block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
backend.rollback(block)
return backend return backend
} }
@ -105,14 +117,20 @@ func (b *SimulatedBackend) Close() error {
// Commit imports all the pending transactions as a single block and starts a // Commit imports all the pending transactions as a single block and starts a
// fresh new state. // fresh new state.
func (b *SimulatedBackend) Commit() { func (b *SimulatedBackend) Commit() common.Hash {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil { if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
panic(err) // This cannot happen unless the simulator is wrong, fail in that case panic(err) // This cannot happen unless the simulator is wrong, fail in that case
} }
b.rollback() blockHash := b.pendingBlock.Hash()
// Using the last inserted block here makes it possible to build on a side
// chain after a fork.
b.rollback(b.pendingBlock)
return blockHash
} }
// Rollback aborts all pending transactions, reverting to the last committed state. // Rollback aborts all pending transactions, reverting to the last committed state.
@ -120,22 +138,52 @@ func (b *SimulatedBackend) Rollback() {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
b.rollback() header := b.blockchain.CurrentBlock()
block := b.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
b.rollback(block)
} }
func (b *SimulatedBackend) rollback() { func (b *SimulatedBackend) rollback(parent *types.Block) {
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil) b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
} }
// Fork creates a side-chain that can be used to simulate reorgs.
//
// This function should be called with the ancestor block where the new side
// chain should be started. Transactions (old and new) can then be applied on
// top and Commit-ed.
//
// Note, the side-chain will only become canonical (and trigger the events) when
// it becomes longer. Until then CallContract will still operate on the current
// canonical chain.
//
// There is a % chance that the side chain becomes canonical at the same length
// to simulate live network behavior.
func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("pending block dirty")
}
block, err := b.blockByHash(ctx, parent)
if err != nil {
return err
}
b.rollback(block)
return nil
}
// stateByBlockNumber retrieves a state by a given blocknumber. // stateByBlockNumber retrieves a state by a given blocknumber.
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) { func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 { if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 {
return b.blockchain.State() return b.blockchain.State()
} }
block, err := b.blockByNumberNoLock(ctx, blockNumber) block, err := b.blockByNumber(ctx, blockNumber)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -201,6 +249,9 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
defer b.mu.Unlock() defer b.mu.Unlock()
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config) receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
if receipt == nil {
return nil, ethereum.NotFound
}
return receipt, nil return receipt, nil
} }
@ -228,6 +279,11 @@ func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
return b.blockByHash(ctx, hash)
}
// blockByHash retrieves a block based on the block hash without Locking.
func (b *SimulatedBackend) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
if hash == b.pendingBlock.Hash() { if hash == b.pendingBlock.Hash() {
return b.pendingBlock, nil return b.pendingBlock, nil
} }
@ -246,14 +302,14 @@ func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
return b.blockByNumberNoLock(ctx, number) return b.blockByNumber(ctx, number)
} }
// blockByNumberNoLock retrieves a block from the database by number, caching it // blockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found without Lock. // (associated with its hash) if found without Lock.
func (b *SimulatedBackend) blockByNumberNoLock(ctx context.Context, number *big.Int) (*types.Block, error) { func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 { if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
return b.blockchain.CurrentBlock(), nil return b.blockByHash(ctx, b.blockchain.CurrentBlock().Hash())
} }
block := b.blockchain.GetBlockByNumber(uint64(number.Int64())) block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
@ -381,7 +437,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
return nil, errBlockNumberUnsupported return nil, errBlockNumberUnsupported
} }
stateDB, err := b.blockchain.State() stateDB, err := b.blockchain.State()
@ -405,7 +461,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
defer b.mu.Unlock() defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -428,6 +484,18 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doesn't have miners, we just return a gas price of 1 for any call. // chain doesn't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
b.mu.Lock()
defer b.mu.Unlock()
if b.pendingBlock.Header().BaseFee != nil {
return b.pendingBlock.Header().BaseFee, nil
}
return big.NewInt(1), nil
}
// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
// chain doesn't have miners, we just return a gas tip of 1 for any call.
func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil return big.NewInt(1), nil
} }
@ -448,24 +516,35 @@ 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 {
if call.Value.Cmp(available) >= 0 { if call.Value.Cmp(available) >= 0 {
return 0, errors.New("insufficient funds for transfer") return 0, core.ErrInsufficientFundsForTransfer
} }
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()
} }
} }
@ -476,7 +555,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
call.Gas = gas call.Gas = gas
snapshot := b.pendingState.Snapshot() snapshot := b.pendingState.Snapshot()
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
b.pendingState.RevertToSnapshot(snapshot) b.pendingState.RevertToSnapshot(snapshot)
if err != nil { if err != nil {
@ -526,53 +605,96 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
// callContract implements common code between normal and pending contract calls. // callContract implements common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary. // state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) { func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, header *types.Header, stateDB *state.StateDB) (*core.ExecutionResult, error) {
// Ensure message is initialized properly. // Gas prices post 1559 need to be initialized
if call.GasPrice == nil { if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
call.GasPrice = big.NewInt(1) return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} }
head := b.blockchain.CurrentHeader()
if !b.blockchain.Config().IsLondon(head.Number) {
// If there's no basefee, then it must be a non-1559 execution
if call.GasPrice == nil {
call.GasPrice = new(big.Int)
}
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
} else {
// A basefee is provided, necessitating 1559-type execution
if call.GasPrice != nil {
// User specified the legacy gas field, convert to 1559 gas typing
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
} else {
// User specified 1559 gas fields (or none), use those
if call.GasFeeCap == nil {
call.GasFeeCap = new(big.Int)
}
if call.GasTipCap == nil {
call.GasTipCap = new(big.Int)
}
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
call.GasPrice = new(big.Int)
if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap)
}
}
}
// Ensure message is initialized properly.
if call.Gas == 0 { if call.Gas == 0 {
call.Gas = 50000000 call.Gas = 50000000
} }
if call.Value == nil { if call.Value == nil {
call.Value = new(big.Int) call.Value = new(big.Int)
} }
// Set infinite balance to the fake caller account. // Set infinite balance to the fake caller account.
from := stateDB.GetOrNewStateObject(call.From) from := stateDB.GetOrNewStateObject(call.From)
from.SetBalance(math.MaxBig256) from.SetBalance(math.MaxBig256)
// Execute the call.
msg := callMsg{call}
txContext := core.NewEVMTxContext(msg) // Execute the call.
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) msg := &core.Message{
From: call.From,
To: call.To,
Value: call.Value,
GasLimit: call.Gas,
GasPrice: call.GasPrice,
GasFeeCap: call.GasFeeCap,
GasTipCap: call.GasTipCap,
Data: call.Data,
AccessList: call.AccessList,
SkipAccountChecks: true,
}
// Create a new environment which holds all relevant information // Create a new environment which holds all relevant information
// about the transaction and calling mechanisms. // about the transaction and calling mechanisms.
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{}) txContext := core.NewEVMTxContext(msg)
evmContext := core.NewEVMBlockContext(header, b.blockchain, nil)
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
gasPool := new(core.GasPool).AddGas(math.MaxUint64) gasPool := new(core.GasPool).AddGas(math.MaxUint64)
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb() return core.ApplyMessage(vmEnv, msg, gasPool)
} }
// SendTransaction updates the pending block to include the given transaction. // SendTransaction updates the pending block to include the given transaction.
// It panics if the transaction is invalid.
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
// Check transaction validity. // Get the last block
block := b.blockchain.CurrentBlock() block, err := b.blockByHash(ctx, b.pendingBlock.ParentHash())
if err != nil {
return fmt.Errorf("could not fetch parent")
}
// Check transaction validity
signer := types.MakeSigner(b.blockchain.Config(), block.Number()) signer := types.MakeSigner(b.blockchain.Config(), block.Number())
sender, err := types.Sender(signer, tx) sender, err := types.Sender(signer, tx)
if err != nil { if err != nil {
panic(fmt.Errorf("invalid transaction: %v", err)) return fmt.Errorf("invalid transaction: %v", err)
} }
nonce := b.pendingState.GetNonce(sender) nonce := b.pendingState.GetNonce(sender)
if tx.Nonce() != nonce { if tx.Nonce() != nonce {
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
} }
// Include tx in chain
// Include tx in chain. blocks, receipts := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() { for _, tx := range b.pendingBlock.Transactions() {
block.AddTxWithChain(b.blockchain, tx) block.AddTxWithChain(b.blockchain, tx)
} }
@ -582,6 +704,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil) b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
b.pendingReceipts = receipts[0]
return nil return nil
} }
@ -593,7 +716,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
var filter *filters.Filter var filter *filters.Filter
if query.BlockHash != nil { if query.BlockHash != nil {
// Block filter requested, construct a single-shot filter // Block filter requested, construct a single-shot filter
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics) filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics)
} else { } else {
// Initialize unset filter boundaries to run from genesis to chain head // Initialize unset filter boundaries to run from genesis to chain head
from := int64(0) from := int64(0)
@ -605,7 +728,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
to = query.ToBlock.Int64() to = query.ToBlock.Int64()
} }
// Construct the range filter // Construct the range filter
filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics) filter = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics)
} }
// Run the filter and return all the logs // Run the filter and return all the logs
logs, err := filter.Logs(ctx) logs, err := filter.Logs(ctx)
@ -689,8 +812,13 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
if len(b.pendingBlock.Transactions()) != 0 { if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("Could not adjust time on non-empty block") return errors.New("Could not adjust time on non-empty block")
} }
// Get the last block
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
if block == nil {
return fmt.Errorf("could not find parent")
}
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
block.OffsetTime(int64(adjustment.Seconds())) block.OffsetTime(int64(adjustment.Seconds()))
}) })
stateDB, _ := b.blockchain.State() stateDB, _ := b.blockchain.State()
@ -706,42 +834,51 @@ func (b *SimulatedBackend) Blockchain() *core.BlockChain {
return b.blockchain return b.blockchain
} }
// callMsg implements core.Message to allow passing it as a transaction simulator.
type callMsg struct {
ethereum.CallMsg
}
func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
// filterBackend implements filters.Backend to support filtering for logs without // filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account. // taking bloom-bits acceleration structures into account.
type filterBackend struct { type filterBackend struct {
db ethdb.Database db ethdb.Database
bc *core.BlockChain bc *core.BlockChain
backend *SimulatedBackend
} }
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) { func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
if block == rpc.LatestBlockNumber { switch number {
case rpc.PendingBlockNumber:
if block := fb.backend.pendingBlock; block != nil {
return block.Header(), nil
}
return nil, nil
case rpc.LatestBlockNumber:
return fb.bc.CurrentHeader(), nil return fb.bc.CurrentHeader(), nil
case rpc.FinalizedBlockNumber:
return fb.bc.CurrentFinalBlock(), nil
case rpc.SafeBlockNumber:
return fb.bc.CurrentSafeBlock(), nil
default:
return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil
} }
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
} }
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
return fb.bc.GetHeaderByHash(hash), nil return fb.bc.GetHeaderByHash(hash), nil
} }
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
if body := fb.bc.GetBody(hash); body != nil {
return body, nil
}
return nil, errors.New("block body not found")
}
func (fb *filterBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
return fb.backend.pendingBlock, fb.backend.pendingReceipts
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
number := rawdb.ReadHeaderNumber(fb.db, hash) number := rawdb.ReadHeaderNumber(fb.db, hash)
if number == nil { if number == nil {
@ -750,19 +887,8 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
} }
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) { func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
number := rawdb.ReadHeaderNumber(fb.db, hash) logs := rawdb.ReadLogs(fb.db, hash, number, fb.bc.Config())
if number == nil {
return nil, nil
}
receipts := rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config())
if receipts == nil {
return nil, nil
}
logs := make([][]*types.Log, len(receipts))
for i, receipt := range receipts {
logs[i] = receipt.Logs
}
return logs, nil return logs, nil
} }
@ -792,6 +918,14 @@ func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.Matche
panic("not supported") panic("not supported")
} }
func (fb *filterBackend) ChainConfig() *params.ChainConfig {
panic("not supported")
}
func (fb *filterBackend) CurrentHeader() *types.Header {
panic("not supported")
}
func nullSubscription() event.Subscription { func nullSubscription() event.Subscription {
return event.NewSubscription(func(quit <-chan struct{}) error { return event.NewSubscription(func(quit <-chan struct{}) error {
<-quit <-quit

View File

@ -21,6 +21,7 @@ import (
"context" "context"
"errors" "errors"
"math/big" "math/big"
"math/rand"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -58,9 +59,12 @@ func TestSimulatedBackend(t *testing.T) {
} }
// generate a transaction and confirm you can retrieve it // generate a transaction and confirm you can retrieve it
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
code := `6060604052600a8060106000396000f360606040526008565b00` code := `6060604052600a8060106000396000f360606040526008565b00`
var gas uint64 = 3000000 var gas uint64 = 3000000
tx := types.NewContractCreation(0, big.NewInt(0), gas, big.NewInt(1), common.FromHex(code)) tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
err = sim.SendTransaction(context.Background(), tx) err = sim.SendTransaction(context.Background(), tx)
@ -89,17 +93,18 @@ func TestSimulatedBackend(t *testing.T) {
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// the following is based on this contract: // the following is based on this contract:
// contract T {
// event received(address sender, uint amount, bytes memo);
// event receivedAddr(address sender);
// //
// function receive(bytes calldata memo) external payable returns (string memory res) { // contract T {
// emit received(msg.sender, msg.value, memo); // event received(address sender, uint amount, bytes memo);
// emit receivedAddr(msg.sender); // event receivedAddr(address sender);
// return "hello world"; //
// } // function receive(bytes calldata memo) external payable returns (string memory res) {
// } // emit received(msg.sender, msg.value, memo);
// emit receivedAddr(msg.sender);
// return "hello world";
// }
// }
const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]` const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]`
const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
@ -110,14 +115,14 @@ var expectedReturn = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
func simTestBackend(testAddr common.Address) *SimulatedBackend { func simTestBackend(testAddr common.Address) *SimulatedBackend {
return NewSimulatedBackend( return NewSimulatedBackend(
core.GenesisAlloc{ core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)}, testAddr: {Balance: big.NewInt(10000000000000000)},
}, 10000000, }, 10000000,
) )
} }
func TestNewSimulatedBackend(t *testing.T) { func TestNewSimulatedBackend(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000) expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
@ -136,7 +141,7 @@ func TestNewSimulatedBackend(t *testing.T) {
} }
} }
func TestSimulatedBackend_AdjustTime(t *testing.T) { func TestAdjustTime(t *testing.T) {
sim := NewSimulatedBackend( sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000, core.GenesisAlloc{}, 10000000,
) )
@ -153,11 +158,15 @@ func TestSimulatedBackend_AdjustTime(t *testing.T) {
} }
} }
func TestNewSimulatedBackend_AdjustTimeFail(t *testing.T) { func TestNewAdjustTimeFail(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
// Create tx and send // Create tx and send
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -178,7 +187,7 @@ func TestNewSimulatedBackend_AdjustTimeFail(t *testing.T) {
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime) t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
} }
// Put a transaction after adjusting time // Put a transaction after adjusting time
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey) signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -191,9 +200,9 @@ func TestNewSimulatedBackend_AdjustTimeFail(t *testing.T) {
} }
} }
func TestSimulatedBackend_BalanceAt(t *testing.T) { func TestBalanceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000) expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
@ -208,7 +217,7 @@ func TestSimulatedBackend_BalanceAt(t *testing.T) {
} }
} }
func TestSimulatedBackend_BlockByHash(t *testing.T) { func TestBlockByHash(t *testing.T) {
sim := NewSimulatedBackend( sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000, core.GenesisAlloc{}, 10000000,
) )
@ -229,7 +238,7 @@ func TestSimulatedBackend_BlockByHash(t *testing.T) {
} }
} }
func TestSimulatedBackend_BlockByNumber(t *testing.T) { func TestBlockByNumber(t *testing.T) {
sim := NewSimulatedBackend( sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000, core.GenesisAlloc{}, 10000000,
) )
@ -264,7 +273,7 @@ func TestSimulatedBackend_BlockByNumber(t *testing.T) {
} }
} }
func TestSimulatedBackend_NonceAt(t *testing.T) { func TestNonceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -281,7 +290,10 @@ func TestSimulatedBackend_NonceAt(t *testing.T) {
} }
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -314,7 +326,7 @@ func TestSimulatedBackend_NonceAt(t *testing.T) {
} }
} }
func TestSimulatedBackend_SendTransaction(t *testing.T) { func TestSendTransaction(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -322,7 +334,10 @@ func TestSimulatedBackend_SendTransaction(t *testing.T) {
bgCtx := context.Background() bgCtx := context.Background()
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -345,19 +360,22 @@ func TestSimulatedBackend_SendTransaction(t *testing.T) {
} }
} }
func TestSimulatedBackend_TransactionByHash(t *testing.T) { func TestTransactionByHash(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend( sim := NewSimulatedBackend(
core.GenesisAlloc{ core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)}, testAddr: {Balance: big.NewInt(10000000000000000)},
}, 10000000, }, 10000000,
) )
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -396,16 +414,17 @@ func TestSimulatedBackend_TransactionByHash(t *testing.T) {
} }
} }
func TestSimulatedBackend_EstimateGas(t *testing.T) { func TestEstimateGas(t *testing.T) {
/* /*
pragma solidity ^0.6.4; pragma solidity ^0.6.4;
contract GasEstimation { contract GasEstimation {
function PureRevert() public { revert(); } function PureRevert() public { revert(); }
function Revert() public { revert("revert reason");} function Revert() public { revert("revert reason");}
function OOG() public { for (uint i = 0; ; i++) {}} function OOG() public { for (uint i = 0; ; i++) {}}
function Assert() public { assert(false);} function Assert() public { assert(false);}
function Valid() public {} function Valid() public {}
}*/ }
*/
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033" const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
@ -479,7 +498,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) {
GasPrice: big.NewInt(0), GasPrice: big.NewInt(0),
Value: nil, Value: nil,
Data: common.Hex2Bytes("b9b046f9"), Data: common.Hex2Bytes("b9b046f9"),
}, 0, errors.New("invalid opcode: opcode 0xfe not defined"), nil}, }, 0, errors.New("invalid opcode: INVALID"), nil},
{"Valid", ethereum.CallMsg{ {"Valid", ethereum.CallMsg{
From: addr, From: addr,
@ -514,7 +533,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) {
} }
} }
func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) { func TestEstimateGasWithPrice(t *testing.T) {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
@ -533,7 +552,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
To: &recipient, To: &recipient,
Gas: 0, Gas: 0,
GasPrice: big.NewInt(0), GasPrice: big.NewInt(0),
Value: big.NewInt(1000), Value: big.NewInt(100000000000),
Data: nil, Data: nil,
}, 21000, nil}, }, 21000, nil},
@ -541,8 +560,8 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
From: addr, From: addr,
To: &recipient, To: &recipient,
Gas: 0, Gas: 0,
GasPrice: big.NewInt(1000), GasPrice: big.NewInt(100000000000),
Value: big.NewInt(1000), Value: big.NewInt(100000000000),
Data: nil, Data: nil,
}, 21000, nil}, }, 21000, nil},
@ -560,28 +579,51 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
To: &recipient, To: &recipient,
Gas: 0, Gas: 0,
GasPrice: big.NewInt(2e14), // gascost = 4.2ether GasPrice: big.NewInt(2e14), // gascost = 4.2ether
Value: big.NewInt(1000), 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 _, c := range cases { for i, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message) got, err := sim.EstimateGas(context.Background(), c.message)
if c.expectError != nil { if c.expectError != nil {
if err == nil { if err == nil {
t.Fatalf("Expect error, got nil") t.Fatalf("test %d: expect error, got nil", i)
} }
if c.expectError.Error() != err.Error() { if c.expectError.Error() != err.Error() {
t.Fatalf("Expect error, want %v, got %v", c.expectError, err) t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
} }
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("Gas estimation mismatch, want %d, got %d", c.expect, got) t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
} }
} }
} }
func TestSimulatedBackend_HeaderByHash(t *testing.T) { func TestHeaderByHash(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -602,7 +644,7 @@ func TestSimulatedBackend_HeaderByHash(t *testing.T) {
} }
} }
func TestSimulatedBackend_HeaderByNumber(t *testing.T) { func TestHeaderByNumber(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -615,8 +657,7 @@ func TestSimulatedBackend_HeaderByNumber(t *testing.T) {
} }
if latestBlockHeader == nil { if latestBlockHeader == nil {
t.Errorf("received a nil block header") t.Errorf("received a nil block header")
} } else if latestBlockHeader.Number.Uint64() != uint64(0) {
if latestBlockHeader.Number.Uint64() != uint64(0) {
t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64()) t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
} }
@ -649,7 +690,7 @@ func TestSimulatedBackend_HeaderByNumber(t *testing.T) {
} }
} }
func TestSimulatedBackend_TransactionCount(t *testing.T) { func TestTransactionCount(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -668,9 +709,11 @@ func TestSimulatedBackend_TransactionCount(t *testing.T) {
if count != 0 { if count != 0 {
t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count) t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count)
} }
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -699,7 +742,7 @@ func TestSimulatedBackend_TransactionCount(t *testing.T) {
} }
} }
func TestSimulatedBackend_TransactionInBlock(t *testing.T) { func TestTransactionInBlock(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -723,9 +766,11 @@ func TestSimulatedBackend_TransactionInBlock(t *testing.T) {
if pendingNonce != uint64(0) { if pendingNonce != uint64(0) {
t.Errorf("expected pending nonce of 0 got %v", pendingNonce) t.Errorf("expected pending nonce of 0 got %v", pendingNonce)
} }
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -762,7 +807,7 @@ func TestSimulatedBackend_TransactionInBlock(t *testing.T) {
} }
} }
func TestSimulatedBackend_PendingNonceAt(t *testing.T) { func TestPendingNonceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -780,7 +825,10 @@ func TestSimulatedBackend_PendingNonceAt(t *testing.T) {
} }
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -803,7 +851,7 @@ func TestSimulatedBackend_PendingNonceAt(t *testing.T) {
} }
// make a new transaction with a nonce of 1 // make a new transaction with a nonce of 1
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -824,7 +872,7 @@ func TestSimulatedBackend_PendingNonceAt(t *testing.T) {
} }
} }
func TestSimulatedBackend_TransactionReceipt(t *testing.T) { func TestTransactionReceipt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
@ -832,7 +880,10 @@ func TestSimulatedBackend_TransactionReceipt(t *testing.T) {
bgCtx := context.Background() bgCtx := context.Background()
// create a signed transaction to send // create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -855,7 +906,7 @@ func TestSimulatedBackend_TransactionReceipt(t *testing.T) {
} }
} }
func TestSimulatedBackend_SuggestGasPrice(t *testing.T) { func TestSuggestGasPrice(t *testing.T) {
sim := NewSimulatedBackend( sim := NewSimulatedBackend(
core.GenesisAlloc{}, core.GenesisAlloc{},
10000000, 10000000,
@ -866,12 +917,12 @@ func TestSimulatedBackend_SuggestGasPrice(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get gas price: %v", err) t.Errorf("could not get gas price: %v", err)
} }
if gasPrice.Uint64() != uint64(1) { if gasPrice.Uint64() != sim.pendingBlock.Header().BaseFee.Uint64() {
t.Errorf("gas price was not expected value of 1. actual: %v", gasPrice.Uint64()) t.Errorf("gas price was not expected value of %v. actual: %v", sim.pendingBlock.Header().BaseFee.Uint64(), gasPrice.Uint64())
} }
} }
func TestSimulatedBackend_PendingCodeAt(t *testing.T) { func TestPendingCodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
@ -907,7 +958,7 @@ func TestSimulatedBackend_PendingCodeAt(t *testing.T) {
} }
} }
func TestSimulatedBackend_CodeAt(t *testing.T) { func TestCodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
@ -945,8 +996,9 @@ func TestSimulatedBackend_CodeAt(t *testing.T) {
} }
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt: // When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} //
func TestSimulatedBackend_PendingAndCallContract(t *testing.T) { // receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestPendingAndCallContract(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
@ -1008,29 +1060,29 @@ func TestSimulatedBackend_PendingAndCallContract(t *testing.T) {
// This test is based on the following contract: // This test is based on the following contract:
/* /*
contract Reverter { contract Reverter {
function revertString() public pure{ function revertString() public pure{
require(false, "some error"); require(false, "some error");
} }
function revertNoString() public pure { function revertNoString() public pure {
require(false, ""); require(false, "");
} }
function revertASM() public pure { function revertASM() public pure {
assembly { assembly {
revert(0x0, 0x0) revert(0x0, 0x0)
} }
} }
function noRevert() public pure { function noRevert() public pure {
assembly { assembly {
// Assembles something that looks like require(false, "some error") but is not reverted // Assembles something that looks like require(false, "some error") but is not reverted
mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(0x4, 0x0000000000000000000000000000000000000000000000000000000000000020) mstore(0x4, 0x0000000000000000000000000000000000000000000000000000000000000020)
mstore(0x24, 0x000000000000000000000000000000000000000000000000000000000000000a) mstore(0x24, 0x000000000000000000000000000000000000000000000000000000000000000a)
mstore(0x44, 0x736f6d65206572726f7200000000000000000000000000000000000000000000) mstore(0x44, 0x736f6d65206572726f7200000000000000000000000000000000000000000000)
return(0x0, 0x64) return(0x0, 0x64)
} }
} }
}*/ }*/
func TestSimulatedBackend_CallContractRevert(t *testing.T) { func TestCallContractRevert(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
@ -1114,3 +1166,234 @@ func TestSimulatedBackend_CallContractRevert(t *testing.T) {
sim.Commit() sim.Commit()
} }
} }
// TestFork check that the chain length after a reorg is correct.
// Steps:
// 1. Save the current block which will serve as parent for the fork.
// 2. Mine n blocks with n ∈ [0, 20].
// 3. Assert that the chain length is n.
// 4. Fork by using the parent block as ancestor.
// 5. Mine n+1 blocks which should trigger a reorg.
// 6. Assert that the chain length is n+1.
// Since Commit() was called 2n+1 times in total,
// having a chain length of just n+1 means that a reorg occurred.
func TestFork(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
// 1.
parent := sim.blockchain.CurrentBlock()
// 2.
n := int(rand.Int31n(21))
for i := 0; i < n; i++ {
sim.Commit()
}
// 3.
if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n) {
t.Error("wrong chain length")
}
// 4.
sim.Fork(context.Background(), parent.Hash())
// 5.
for i := 0; i < n+1; i++ {
sim.Commit()
}
// 6.
if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n+1) {
t.Error("wrong chain length")
}
}
/*
Example contract to test event emission:
pragma solidity >=0.7.0 <0.9.0;
contract Callable {
event Called();
function Call() public { emit Called(); }
}
*/
const callableAbi = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806334e2292114602d575b600080fd5b60336035565b005b7f81fab7a4a0aa961db47eefc81f143a5220e8c8495260dd65b1356f1d19d3c7b860405160405180910390a156fea2646970667358221220029436d24f3ac598ceca41d4d712e13ced6d70727f4cdc580667de66d2f51d8b64736f6c63430008010033"
// TestForkLogsReborn check that the simulated reorgs
// correctly remove and reborn logs.
// Steps:
// 1. Deploy the Callable contract.
// 2. Set up an event subscription.
// 3. Save the current block which will serve as parent for the fork.
// 4. Send a transaction.
// 5. Check that the event was included.
// 6. Fork by using the parent block as ancestor.
// 7. Mine two blocks to trigger a reorg.
// 8. Check that the event was removed.
// 9. Re-send the transaction and mine a block.
// 10. Check that the event was reborn.
func TestForkLogsReborn(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
// 1.
parsed, _ := abi.JSON(strings.NewReader(callableAbi))
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
_, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
if err != nil {
t.Errorf("deploying contract: %v", err)
}
sim.Commit()
// 2.
logs, sub, err := contract.WatchLogs(nil, "Called")
if err != nil {
t.Errorf("watching logs: %v", err)
}
defer sub.Unsubscribe()
// 3.
parent := sim.blockchain.CurrentBlock()
// 4.
tx, err := contract.Transact(auth, "Call")
if err != nil {
t.Errorf("transacting: %v", err)
}
sim.Commit()
// 5.
log := <-logs
if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash")
}
if log.Removed {
t.Error("Event should be included")
}
// 6.
if err := sim.Fork(context.Background(), parent.Hash()); err != nil {
t.Errorf("forking: %v", err)
}
// 7.
sim.Commit()
sim.Commit()
// 8.
log = <-logs
if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash")
}
if !log.Removed {
t.Error("Event should be removed")
}
// 9.
if err := sim.SendTransaction(context.Background(), tx); err != nil {
t.Errorf("sending transaction: %v", err)
}
sim.Commit()
// 10.
log = <-logs
if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash")
}
if log.Removed {
t.Error("Event should be included")
}
}
// TestForkResendTx checks that re-sending a TX after a fork
// is possible and does not cause a "nonce mismatch" panic.
// Steps:
// 1. Save the current block which will serve as parent for the fork.
// 2. Send a transaction.
// 3. Check that the TX is included in block 1.
// 4. Fork by using the parent block as ancestor.
// 5. Mine a block, Re-send the transaction and mine another one.
// 6. Check that the TX is now included in block 2.
func TestForkResendTx(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
// 1.
parent := sim.blockchain.CurrentBlock()
// 2.
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
_tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
tx, _ := types.SignTx(_tx, types.HomesteadSigner{}, testKey)
sim.SendTransaction(context.Background(), tx)
sim.Commit()
// 3.
receipt, _ := sim.TransactionReceipt(context.Background(), tx.Hash())
if h := receipt.BlockNumber.Uint64(); h != 1 {
t.Errorf("TX included in wrong block: %d", h)
}
// 4.
if err := sim.Fork(context.Background(), parent.Hash()); err != nil {
t.Errorf("forking: %v", err)
}
// 5.
sim.Commit()
if err := sim.SendTransaction(context.Background(), tx); err != nil {
t.Errorf("sending transaction: %v", err)
}
sim.Commit()
// 6.
receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())
if h := receipt.BlockNumber.Uint64(); h != 2 {
t.Errorf("TX included in wrong block: %d", h)
}
}
func TestCommitReturnValue(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
startBlockHeight := sim.blockchain.CurrentBlock().Number.Uint64()
// Test if Commit returns the correct block hash
h1 := sim.Commit()
if h1 != sim.blockchain.CurrentBlock().Hash() {
t.Error("Commit did not return the hash of the last block.")
}
// Create a block in the original chain (containing a transaction to force different block hashes)
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
_tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
tx, _ := types.SignTx(_tx, types.HomesteadSigner{}, testKey)
sim.SendTransaction(context.Background(), tx)
h2 := sim.Commit()
// Create another block in the original chain
sim.Commit()
// Fork at the first bock
if err := sim.Fork(context.Background(), h1); err != nil {
t.Errorf("forking: %v", err)
}
// Test if Commit returns the correct block hash after the reorg
h2fork := sim.Commit()
if h2 == h2fork {
t.Error("The block in the fork and the original block are the same block!")
}
if sim.blockchain.GetHeader(h2fork, startBlockHeight+2) == nil {
t.Error("Could not retrieve the just created block (side-chain)")
}
}
// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork
// block's parent rather than the canonical head's parent.
func TestAdjustTimeAfterFork(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
sim.Commit() // h1
h1 := sim.blockchain.CurrentHeader().Hash()
sim.Commit() // h2
sim.Fork(context.Background(), h1)
sim.AdjustTime(1 * time.Second)
sim.Commit()
head := sim.blockchain.CurrentHeader()
if head.Number == common.Big2 && head.ParentHash != h1 {
t.Errorf("failed to build block on fork")
}
}

View File

@ -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"
@ -30,6 +32,13 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
const basefeeWiggleMultiplier = 2
var (
errNoEventSignature = errors.New("no event signature")
errEventSignatureMismatch = errors.New("event signature mismatch")
)
// SignerFn is a signer function callback when a contract requires a method to // SignerFn is a signer function callback when a contract requires a method to
// sign the transaction before submission. // sign the transaction before submission.
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
@ -49,9 +58,11 @@ type TransactOpts struct {
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
Signer SignerFn // Method to use for signing the transaction (mandatory) Signer SignerFn // Method to use for signing the transaction (mandatory)
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds) Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
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)
@ -74,6 +85,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.
@ -144,7 +178,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
return ErrNoPendingState return ErrNoPendingState
} }
output, err = pb.PendingCallContract(ctx, msg) output, err = pb.PendingCallContract(ctx, msg)
if err == nil && len(output) == 0 { if err != nil {
return err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise. // Make sure we have a contract to operate on, and bail out otherwise.
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
return err return err
@ -204,57 +241,160 @@ 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 the gas allowance and gas price values // Estimate FeeCap
gasFeeCap := opts.GasFeeCap
if gasFeeCap == nil {
gasFeeCap = new(big.Int).Add(
gasTipCap,
new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
)
}
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 {
return nil, err
}
baseTx := &types.DynamicFeeTx{
To: contract,
Nonce: nonce,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
Gas: gasLimit,
Value: value,
Data: input,
}
return types.NewTx(baseTx), nil
}
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
}
// Normalize value
value := opts.Value
if value == nil {
value = new(big.Int)
}
// Estimate GasPrice
gasPrice := opts.GasPrice gasPrice := opts.GasPrice
if gasPrice == nil { if gasPrice == nil {
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to suggest gas price: %v", err) return nil, err
} }
gasPrice = price
} }
// Estimate GasLimit
gasLimit := opts.GasLimit gasLimit := opts.GasLimit
if gasLimit == 0 { if opts.GasLimit == 0 {
// Gas estimation cannot succeed without code for method invocations var err error
if contract != nil { gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
return nil, err
} else if len(code) == 0 {
return nil, ErrNoCode
}
}
// If the contract surely has code (or code is not needed), estimate the transaction
msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input}
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to estimate gas needed: %v", err) 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 contract == nil { if err != nil {
rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) return nil, err
} else {
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
} }
baseTx := &types.LegacyTx{
To: contract,
Nonce: nonce,
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
}
}
msg := ethereum.CallMsg{
From: opts.From,
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 {
return opts.Nonce.Uint64(), nil
}
}
// 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 if opts.GasFeeCap != nil && opts.GasTipCap != nil {
rawTx, err = c.createDynamicTx(opts, contract, input, nil)
} else {
// Only query for basefee if gasPrice not specified
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != 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")
} }
@ -353,6 +493,13 @@ 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 {
// Anonymous events are not supported.
if len(log.Topics) == 0 {
return errNoEventSignature
}
if log.Topics[0] != c.abi.Events[event].ID {
return errEventSignatureMismatch
}
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
@ -369,6 +516,13 @@ 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 {
// Anonymous events are not supported.
if len(log.Topics) == 0 {
return errNoEventSignature
}
if log.Topics[0] != c.abi.Events[event].ID {
return errEventSignatureMismatch
}
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
@ -387,7 +541,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
} }

View File

@ -18,6 +18,7 @@ package bind_test
import ( import (
"context" "context"
"errors"
"math/big" "math/big"
"reflect" "reflect"
"strings" "strings"
@ -31,37 +32,94 @@ 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
pendingCodeAtCalled bool callContractBytes []byte
pendingCallContractCalled bool callContractErr error
codeAtBytes []byte
codeAtErr error
} }
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
mc.codeAtBlockNumber = blockNumber mc.codeAtBlockNumber = blockNumber
return []byte{1, 2, 3}, nil return mc.codeAtBytes, mc.codeAtErr
} }
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
mc.callContractBlockNumber = blockNumber mc.callContractBlockNumber = blockNumber
return nil, nil return mc.callContractBytes, mc.callContractErr
} }
func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { type mockPendingCaller struct {
*mockCaller
pendingCodeAtBytes []byte
pendingCodeAtErr error
pendingCodeAtCalled bool
pendingCallContractCalled bool
pendingCallContractBytes []byte
pendingCallContractErr error
}
func (mc *mockPendingCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
mc.pendingCodeAtCalled = true mc.pendingCodeAtCalled = true
return nil, nil return mc.pendingCodeAtBytes, mc.pendingCodeAtErr
} }
func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
mc.pendingCallContractCalled = true mc.pendingCallContractCalled = true
return nil, nil return mc.pendingCallContractBytes, mc.pendingCallContractErr
} }
func TestPassingBlockNumber(t *testing.T) {
mc := &mockCaller{} func TestPassingBlockNumber(t *testing.T) {
mc := &mockPendingCaller{
mockCaller: &mockCaller{
codeAtBytes: []byte{1, 2, 3},
},
}
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
Methods: map[string]abi.Method{ Methods: map[string]abi.Method{
@ -110,7 +168,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"))
@ -128,6 +186,23 @@ func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
unpackAndCheck(t, bc, expectedReceivedMap, mockLog) unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
} }
func TestUnpackAnonymousLogIntoMap(t *testing.T) {
mockLog := newMockLog(nil, common.HexToHash("0x0"))
abiString := `[{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"received","type":"event"}]`
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
var received map[string]interface{}
err := bc.UnpackLogIntoMap(received, "received", mockLog)
if err == nil {
t.Error("unpacking anonymous event is not supported")
}
if err.Error() != "no event signature" {
t.Errorf("expected error 'no event signature', got '%s'", err)
}
}
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) { func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"}) sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
if err != nil { if err != nil {
@ -135,7 +210,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 +235,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 +262,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 +283,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 +301,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 {
@ -255,3 +375,140 @@ func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
Removed: false, Removed: false,
} }
} }
func TestCall(t *testing.T) {
var method, methodWithArg = "something", "somethingArrrrg"
tests := []struct {
name, method string
opts *bind.CallOpts
mc bind.ContractCaller
results *[]interface{}
wantErr bool
wantErrExact error
}{{
name: "ok not pending",
mc: &mockCaller{
codeAtBytes: []byte{0},
},
method: method,
}, {
name: "ok pending",
mc: &mockPendingCaller{
pendingCodeAtBytes: []byte{0},
},
opts: &bind.CallOpts{
Pending: true,
},
method: method,
}, {
name: "pack error, no method",
mc: new(mockCaller),
method: "else",
wantErr: true,
}, {
name: "interface error, pending but not a PendingContractCaller",
mc: new(mockCaller),
opts: &bind.CallOpts{
Pending: true,
},
method: method,
wantErrExact: bind.ErrNoPendingState,
}, {
name: "pending call canceled",
mc: &mockPendingCaller{
pendingCallContractErr: context.DeadlineExceeded,
},
opts: &bind.CallOpts{
Pending: true,
},
method: method,
wantErrExact: context.DeadlineExceeded,
}, {
name: "pending code at error",
mc: &mockPendingCaller{
pendingCodeAtErr: errors.New(""),
},
opts: &bind.CallOpts{
Pending: true,
},
method: method,
wantErr: true,
}, {
name: "no pending code at",
mc: new(mockPendingCaller),
opts: &bind.CallOpts{
Pending: true,
},
method: method,
wantErrExact: bind.ErrNoCode,
}, {
name: "call contract error",
mc: &mockCaller{
callContractErr: context.DeadlineExceeded,
},
method: method,
wantErrExact: context.DeadlineExceeded,
}, {
name: "code at error",
mc: &mockCaller{
codeAtErr: errors.New(""),
},
method: method,
wantErr: true,
}, {
name: "no code at",
mc: new(mockCaller),
method: method,
wantErrExact: bind.ErrNoCode,
}, {
name: "unpack error missing arg",
mc: &mockCaller{
codeAtBytes: []byte{0},
},
method: methodWithArg,
wantErr: true,
}, {
name: "interface unpack error",
mc: &mockCaller{
codeAtBytes: []byte{0},
},
method: method,
results: &[]interface{}{0},
wantErr: true,
}}
for _, test := range tests {
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
Methods: map[string]abi.Method{
method: {
Name: method,
Outputs: abi.Arguments{},
},
methodWithArg: {
Name: methodWithArg,
Outputs: abi.Arguments{abi.Argument{}},
},
},
}, test.mc, nil, nil)
err := bc.Call(test.opts, test.results, test.method)
if test.wantErr || test.wantErrExact != nil {
if err == nil {
t.Fatalf("%q expected error", test.name)
}
if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) {
t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err)
}
continue
}
if err != nil {
t.Fatalf("%q unexpected error: %v", test.name, err)
}
}
}
// TestCrashers contains some strings which previously caused the abi codec to crash.
func TestCrashers(t *testing.T) {
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`))
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`))
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`))
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`))
}

View File

@ -22,7 +22,6 @@ package bind
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"go/format" "go/format"
"regexp" "regexp"
@ -39,10 +38,45 @@ type Lang int
const ( const (
LangGo Lang = iota LangGo Lang = iota
LangJava
LangObjC
) )
func isKeyWord(arg string) bool {
switch arg {
case "break":
case "case":
case "chan":
case "const":
case "continue":
case "default":
case "defer":
case "else":
case "fallthrough":
case "for":
case "func":
case "go":
case "goto":
case "if":
case "import":
case "interface":
case "iota":
case "map":
case "make":
case "new":
case "package":
case "range":
case "return":
case "select":
case "struct":
case "switch":
case "type":
case "var":
default:
return false
}
return true
}
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
// to be used as is in client code, but rather as an intermediate struct which // to be used as is in client code, but rather as an intermediate struct which
// enforces compile time type safety and naming convention opposed to having to // enforces compile time type safety and naming convention opposed to having to
@ -88,10 +122,18 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
transactIdentifiers = make(map[string]bool) transactIdentifiers = make(map[string]bool)
eventIdentifiers = make(map[string]bool) eventIdentifiers = make(map[string]bool)
) )
for _, input := range evmABI.Constructor.Inputs {
if hasStruct(input.Type) {
bindStructType[lang](input.Type, structs)
}
}
for _, original := range evmABI.Methods { for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs // Normalize the method for capital cases and non-anonymous inputs/outputs
normalized := original normalized := original
normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
// Ensure there is no duplicated identifier // Ensure there is no duplicated identifier
var identifiers = callIdentifiers var identifiers = callIdentifiers
if !original.IsConstant() { if !original.IsConstant() {
@ -101,11 +143,12 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
} }
identifiers[normalizedName] = true identifiers[normalizedName] = true
normalized.Name = normalizedName normalized.Name = normalizedName
normalized.Inputs = make([]abi.Argument, len(original.Inputs)) normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs) copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs { for j, input := range normalized.Inputs {
if input.Name == "" { if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
} }
if hasStruct(input.Type) { if hasStruct(input.Type) {
@ -145,12 +188,22 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
eventIdentifiers[normalizedName] = true eventIdentifiers[normalizedName] = true
normalized.Name = normalizedName normalized.Name = normalizedName
used := make(map[string]bool)
normalized.Inputs = make([]abi.Argument, len(original.Inputs)) normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs) copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs { for j, input := range normalized.Inputs {
if input.Name == "" { if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
} }
// Event is a bit special, we need to define event struct in binding,
// ensure there is no camel-case-style name conflict.
for index := 0; ; index++ {
if !used[capitalise(normalized.Inputs[j].Name)] {
used[capitalise(normalized.Inputs[j].Name)] = true
break
}
normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
}
if hasStruct(input.Type) { if hasStruct(input.Type) {
bindStructType[lang](input.Type, structs) bindStructType[lang](input.Type, structs)
} }
@ -165,14 +218,9 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if evmABI.HasReceive() { if evmABI.HasReceive() {
receive = &tmplMethod{Original: evmABI.Receive} receive = &tmplMethod{Original: evmABI.Receive}
} }
// There is no easy way to pass arbitrary java objects to the Go side.
if len(structs) > 0 && lang == LangJava {
return "", errors.New("java binding for tuple arguments is not supported yet")
}
contracts[types[i]] = &tmplContract{ contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]), Type: capitalise(types[i]),
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
Constructor: evmABI.Constructor, Constructor: evmABI.Constructor,
Calls: calls, Calls: calls,
@ -242,8 +290,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
// bindType is a set of type binders that convert Solidity types to some supported // bindType is a set of type binders that convert Solidity types to some supported
// programming language types. // programming language types.
var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindTypeGo, LangGo: bindTypeGo,
LangJava: bindTypeJava,
} }
// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones. // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
@ -286,86 +333,10 @@ func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
} }
} }
// bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java ones.
func bindBasicTypeJava(kind abi.Type) string {
switch kind.T {
case abi.AddressTy:
return "Address"
case abi.IntTy, abi.UintTy:
// Note that uint and int (without digits) are also matched,
// these are size 256, and will translate to BigInt (the default).
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
if len(parts) != 3 {
return kind.String()
}
// All unsigned integers should be translated to BigInt since gomobile doesn't
// support them.
if parts[1] == "u" {
return "BigInt"
}
namedSize := map[string]string{
"8": "byte",
"16": "short",
"32": "int",
"64": "long",
}[parts[2]]
// default to BigInt
if namedSize == "" {
namedSize = "BigInt"
}
return namedSize
case abi.FixedBytesTy, abi.BytesTy:
return "byte[]"
case abi.BoolTy:
return "boolean"
case abi.StringTy:
return "String"
case abi.FunctionTy:
return "byte[24]"
default:
return kind.String()
}
}
// pluralizeJavaType explicitly converts multidimensional types to predefined
// types in go side.
func pluralizeJavaType(typ string) string {
switch typ {
case "boolean":
return "Bools"
case "String":
return "Strings"
case "Address":
return "Addresses"
case "byte[]":
return "Binaries"
case "BigInt":
return "BigInts"
}
return typ + "[]"
}
// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. BigDecimal).
func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T {
case abi.TupleTy:
return structs[kind.TupleRawName+kind.String()].Name
case abi.ArrayTy, abi.SliceTy:
return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
default:
return bindBasicTypeJava(kind)
}
}
// bindTopicType is a set of type binders that convert Solidity types to some // bindTopicType is a set of type binders that convert Solidity types to some
// supported programming language topic types. // supported programming language topic types.
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindTopicTypeGo, LangGo: bindTopicTypeGo,
LangJava: bindTopicTypeJava,
} }
// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
@ -385,28 +356,10 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
return bound return bound
} }
// bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
// functionality as for simple types, but dynamic types get converted to hashes.
func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
bound := bindTypeJava(kind, structs)
// todo(rjl493456442) according solidity documentation, indexed event
// parameters that are not value types i.e. arrays and structs are not
// stored directly but instead a keccak256-hash of an encoding is stored.
//
// We only convert strings and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct.
if bound == "String" || bound == "byte[]" {
bound = "Hash"
}
return bound
}
// bindStructType is a set of type binders that convert Solidity tuple types to some supported // bindStructType is a set of type binders that convert Solidity tuple types to some supported
// programming language struct definition. // programming language struct definition.
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindStructTypeGo, LangGo: bindStructTypeGo,
LangJava: bindStructTypeJava,
} }
// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
@ -425,15 +378,22 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
if s, exist := structs[id]; exist { if s, exist := structs[id]; exist {
return s.Name return s.Name
} }
var fields []*tmplField var (
names = make(map[string]bool)
fields []*tmplField
)
for i, elem := range kind.TupleElems { for i, elem := range kind.TupleElems {
field := bindStructTypeGo(*elem, structs) name := capitalise(kind.TupleRawNames[i])
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem}) name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
names[name] = true
fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
} }
name := kind.TupleRawName name := kind.TupleRawName
if name == "" { if name == "" {
name = fmt.Sprintf("Struct%d", len(structs)) name = fmt.Sprintf("Struct%d", len(structs))
} }
name = capitalise(name)
structs[id] = &tmplStruct{ structs[id] = &tmplStruct{
Name: name, Name: name,
Fields: fields, Fields: fields,
@ -448,74 +408,10 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
} }
} }
// bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
// in the given map.
// Notably, this function will resolve and record nested struct recursively.
func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T {
case abi.TupleTy:
// We compose a raw struct name and a canonical parameter expression
// together here. The reason is before solidity v0.5.11, kind.TupleRawName
// is empty, so we use canonical parameter expression to distinguish
// different struct definition. From the consideration of backward
// compatibility, we concat these two together so that if kind.TupleRawName
// is not empty, it can have unique id.
id := kind.TupleRawName + kind.String()
if s, exist := structs[id]; exist {
return s.Name
}
var fields []*tmplField
for i, elem := range kind.TupleElems {
field := bindStructTypeJava(*elem, structs)
fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
}
name := kind.TupleRawName
if name == "" {
name = fmt.Sprintf("Class%d", len(structs))
}
structs[id] = &tmplStruct{
Name: name,
Fields: fields,
}
return name
case abi.ArrayTy, abi.SliceTy:
return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
default:
return bindBasicTypeJava(kind)
}
}
// namedType is a set of functions that transform language specific types to // namedType is a set of functions that transform language specific types to
// named versions that may be used inside method names. // named versions that may be used inside method names.
var namedType = map[Lang]func(string, abi.Type) string{ var namedType = map[Lang]func(string, abi.Type) string{
LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
LangJava: namedTypeJava,
}
// namedTypeJava converts some primitive data types to named variants that can
// be used as parts of method names.
func namedTypeJava(javaKind string, solKind abi.Type) string {
switch javaKind {
case "byte[]":
return "Binary"
case "boolean":
return "Bool"
default:
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
if len(parts) != 4 {
return javaKind
}
switch parts[2] {
case "8", "16", "32", "64":
if parts[3] == "" {
return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
}
return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
default:
return javaKind
}
}
} }
// alias returns an alias of the given string based on the aliasing rules // alias returns an alias of the given string based on the aliasing rules
@ -530,8 +426,7 @@ func alias(aliases map[string]string, n string) string {
// methodNormalizer is a name transformer that modifies Solidity method names to // methodNormalizer is a name transformer that modifies Solidity method names to
// conform to target language naming conventions. // conform to target language naming conventions.
var methodNormalizer = map[Lang]func(string) string{ var methodNormalizer = map[Lang]func(string) string{
LangGo: abi.ToCamelCase, LangGo: abi.ToCamelCase,
LangJava: decapitalise,
} }
// capitalise makes a camel-case string which starts with an upper case character. // capitalise makes a camel-case string which starts with an upper case character.

File diff suppressed because one or more lines are too long

View File

@ -75,8 +75,7 @@ type tmplStruct struct {
// tmplSource is language to template mapping containing all the supported // tmplSource is language to template mapping containing all the supported
// programming languages the package can generate to. // programming languages the package can generate to.
var tmplSource = map[Lang]string{ var tmplSource = map[Lang]string{
LangGo: tmplSourceGo, LangGo: tmplSourceGo,
LangJava: tmplSourceJava,
} }
// tmplSourceGo is the Go source template that the generated Go contract binding // tmplSourceGo is the Go source template that the generated Go contract binding
@ -90,6 +89,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 +101,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
@ -108,6 +109,7 @@ var (
_ = common.Big1 _ = common.Big1
_ = types.BloomLookup _ = types.BloomLookup
_ = event.NewSubscription _ = event.NewSubscription
_ = abi.ConvertType
) )
{{$structs := .Structs}} {{$structs := .Structs}}
@ -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.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
{{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
} }
@ -250,11 +268,11 @@ var (
// bind{{.Type}} binds a generic wrapper to an already deployed contract. // bind{{.Type}} binds a generic wrapper to an already deployed contract.
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI)) parsed, err := {{.Type}}MetaData.GetAbi()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
} }
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and
@ -551,140 +569,3 @@ var (
{{end}} {{end}}
{{end}} {{end}}
` `
// tmplSourceJava is the Java source template that the generated Java contract binding
// is based on.
const tmplSourceJava = `
// This file is an automatically generated Java binding. Do not modify as any
// change will likely be lost upon the next re-generation!
package {{.Package}};
import org.ethereum.geth.*;
import java.util.*;
{{$structs := .Structs}}
{{range $contract := .Contracts}}
{{if not .Library}}public {{end}}class {{.Type}} {
// ABI is the input ABI used to generate the binding from.
public final static String ABI = "{{.InputABI}}";
{{if $contract.FuncSigs}}
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
public final static Map<String, String> {{.Type}}FuncSigs;
static {
Hashtable<String, String> temp = new Hashtable<String, String>();
{{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
{{end}}
{{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
}
{{end}}
{{if .InputBin}}
// BYTECODE is the compiled bytecode used for deploying new contracts.
public final static String BYTECODE = "0x{{.InputBin}}";
// deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
String bytecode = BYTECODE;
{{if .Libraries}}
// "link" contract to dependent libraries by deploying them first.
{{range $pattern, $name := .Libraries}}
{{capitalise $name}} {{decapitalise $name}}Inst = {{capitalise $name}}.deploy(auth, client);
bytecode = bytecode.replace("__${{$pattern}}$__", {{decapitalise $name}}Inst.Address.getHex().substring(2));
{{end}}
{{end}}
{{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
{{end}}
return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
}
// Internal constructor used by contract deployment.
private {{.Type}}(BoundContract deployment) {
this.Address = deployment.getAddress();
this.Deployer = deployment.getDeployer();
this.Contract = deployment;
}
{{end}}
// Ethereum address where this contract is located at.
public final Address Address;
// Ethereum transaction in which this contract was deployed (if known!).
public final Transaction Deployer;
// Contract instance bound to a blockchain address.
private final BoundContract Contract;
// Creates a new instance of {{.Type}}, bound to a specific deployed contract.
public {{.Type}}(Address address, EthereumClient client) throws Exception {
this(Geth.bindContract(address, ABI, client));
}
{{range .Calls}}
{{if gt (len .Normalized.Outputs) 1}}
// {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
public class {{capitalise .Normalized.Name}}Results {
{{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type $structs}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
{{end}}
}
{{end}}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else if eq (len .Normalized.Outputs) 0}}void{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
{{end}}
Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
{{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type $structs) .Type}}(); results.set({{$index}}, result{{$index}});
{{end}}
if (opts == null) {
opts = Geth.newCallOpts();
}
this.Contract.call(opts, results, "{{.Original.Name}}", args);
{{if gt (len .Normalized.Outputs) 1}}
{{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
{{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type $structs) .Type}}();
{{end}}
return result;
{{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type $structs) .Type}}();{{end}}
{{end}}
}
{{end}}
{{range .Transacts}}
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
{{end}}
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
}
{{end}}
{{if .Fallback}}
// Fallback is a paid mutator transaction binding the contract fallback function.
//
// Solidity: {{.Fallback.Original.String}}
public Transaction Fallback(TransactOpts opts, byte[] calldata) throws Exception {
return this.Contract.rawTransact(opts, calldata);
}
{{end}}
{{if .Receive}}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: {{.Receive.Original.String}}
public Transaction Receive(TransactOpts opts) throws Exception {
return this.Contract.rawTransact(opts, null);
}
{{end}}
}
{{end}}
`

View File

@ -21,6 +21,7 @@ import (
"errors" "errors"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "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/log" "github.com/ethereum/go-ethereum/log"
@ -35,14 +36,16 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
logger := log.New("hash", tx.Hash()) logger := log.New("hash", tx.Hash())
for { for {
receipt, err := b.TransactionReceipt(ctx, tx.Hash()) receipt, err := b.TransactionReceipt(ctx, tx.Hash())
if receipt != nil { if err == nil {
return receipt, nil return receipt, nil
} }
if err != nil {
logger.Trace("Receipt retrieval failed", "err", err) if errors.Is(err, ethereum.NotFound) {
} else {
logger.Trace("Transaction not yet mined") logger.Trace("Transaction not yet mined")
} else {
logger.Trace("Receipt retrieval failed", "err", err)
} }
// Wait for the next round. // Wait for the next round.
select { select {
case <-ctx.Done(): case <-ctx.Done():

View File

@ -56,14 +56,17 @@ func TestWaitDeployed(t *testing.T) {
for name, test := range waitDeployedTests { for name, test := range waitDeployedTests {
backend := backends.NewSimulatedBackend( backend := backends.NewSimulatedBackend(
core.GenesisAlloc{ core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)}, crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
}, },
10000000, 10000000,
) )
defer backend.Close() defer backend.Close()
// Create the transaction. // Create the transaction
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code)) head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
// Wait for it to get mined in the background. // Wait for it to get mined in the background.
@ -99,15 +102,18 @@ func TestWaitDeployed(t *testing.T) {
func TestWaitDeployedCornerCases(t *testing.T) { func TestWaitDeployedCornerCases(t *testing.T) {
backend := backends.NewSimulatedBackend( backend := backends.NewSimulatedBackend(
core.GenesisAlloc{ core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)}, crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
}, },
10000000, 10000000,
) )
defer backend.Close() defer backend.Close()
head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
// Create a transaction to an account. // Create a transaction to an account.
code := "6060604052600a8060106000396000f360606040526008565b00" code := "6060604052600a8060106000396000f360606040526008565b00"
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, big.NewInt(1), common.FromHex(code)) tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -119,7 +125,7 @@ func TestWaitDeployedCornerCases(t *testing.T) {
} }
// Create a transaction that is not mined. // Create a transaction that is not mined.
tx = types.NewContractCreation(1, big.NewInt(0), 3000000, big.NewInt(1), common.FromHex(code)) tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
go func() { go func() {

View File

@ -17,66 +17,77 @@
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. error 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 error's signature used by the
return fmt.Sprintf("[%d]%v", sliceSize, kind) // abi definition to identify event names and types.
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:])
} }

View File

@ -0,0 +1,89 @@
// 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")
errBadUint8 = errors.New("abi: improperly encoded uint8 value")
errBadUint16 = errors.New("abi: improperly encoded uint16 value")
errBadUint32 = errors.New("abi: improperly encoded uint32 value")
errBadUint64 = errors.New("abi: improperly encoded uint64 value")
errBadInt8 = errors.New("abi: improperly encoded int8 value")
errBadInt16 = errors.New("abi: improperly encoded int16 value")
errBadInt32 = errors.New("abi: improperly encoded int32 value")
errBadInt64 = errors.New("abi: improperly encoded int64 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)
}

View File

@ -29,24 +29,27 @@ import (
// don't get the signature canonical representation as the first LOG topic. // don't get the signature canonical representation as the first LOG topic.
type Event struct { type Event struct {
// Name is the event name used for internal representation. It's derived from // Name is the event name used for internal representation. It's derived from
// the raw name and a suffix will be added in the case of a event overload. // the raw name and a suffix will be added in the case of event overloading.
// //
// e.g. // e.g.
// These are two events that have the same name: // These are two events that have the same name:
// * foo(int,int) // * foo(int,int)
// * foo(uint,uint) // * foo(uint,uint)
// The event name of the first one wll be resolved as foo while the second one // The event name of the first one will be resolved as foo while the second one
// will be resolved as foo0. // will be resolved as foo0.
Name string Name string
// RawName is the raw event name parsed from ABI. // RawName is the raw event name parsed from ABI.
RawName string RawName string
Anonymous bool Anonymous bool
Inputs Arguments Inputs Arguments
str string str string
// Sig contains the string signature according to the ABI spec. // Sig contains the string signature according to the ABI spec.
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)" // e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
// Please note that "int" is substitute for its canonical representation "int256" // Please note that "int" is substitute for its canonical representation "int256"
Sig string Sig string
// ID returns the canonical representation of the event's signature used by the // ID returns the canonical representation of the event's signature used by the
// abi definition to identify event names and types. // abi definition to identify event names and types.
ID common.Hash ID common.Hash

View File

@ -161,7 +161,6 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
} }
func TestEventTupleUnpack(t *testing.T) { func TestEventTupleUnpack(t *testing.T) {
type EventTransfer struct { type EventTransfer struct {
Value *big.Int Value *big.Int
} }

View File

@ -25,16 +25,19 @@ import (
) )
// ConvertType converts an interface of a runtime type into a interface of the // ConvertType converts an interface of a runtime type into a interface of the
// given type // given type, e.g. turn this code:
// e.g. turn //
// var fields []reflect.StructField // var fields []reflect.StructField
// fields = append(fields, reflect.StructField{ //
// Name: "X", // fields = append(fields, reflect.StructField{
// Type: reflect.TypeOf(new(big.Int)), // Name: "X",
// Tag: reflect.StructTag("json:\"" + "x" + "\""), // Type: reflect.TypeOf(new(big.Int)),
// } // Tag: reflect.StructTag("json:\"" + "x" + "\""),
// into // }
// type TupleT struct { X *big.Int } //
// into:
//
// type TupleT struct { X *big.Int }
func ConvertType(in interface{}, proto interface{}) interface{} { func ConvertType(in interface{}, proto interface{}) interface{} {
protoType := reflect.TypeOf(proto) protoType := reflect.TypeOf(proto)
if reflect.TypeOf(in).ConvertibleTo(protoType) { if reflect.TypeOf(in).ConvertibleTo(protoType) {
@ -99,7 +102,7 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
func set(dst, src reflect.Value) error { func set(dst, src reflect.Value) error {
dstType, srcType := dst.Type(), src.Type() dstType, srcType := dst.Type(), src.Type()
switch { switch {
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid(): case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
return set(dst.Elem(), src) return set(dst.Elem(), src)
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}): case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
return set(dst.Elem(), src) return set(dst.Elem(), src)
@ -123,15 +126,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() {
@ -177,11 +173,13 @@ func setStruct(dst, src reflect.Value) error {
} }
// mapArgNamesToStructFields maps a slice of argument names to struct fields. // mapArgNamesToStructFields maps a slice of argument names to struct fields.
// first round: for each Exportable field that contains a `abi:""` tag //
// and this field name exists in the given argument name list, pair them together. // first round: for each Exportable field that contains a `abi:""` tag and this field name
// second round: for each argument name that has not been already linked, // exists in the given argument name list, pair them together.
// find what variable is expected to be mapped into, if it exists and has not been //
// used, pair them. // second round: for each argument name that has not been already linked, find what
// variable is expected to be mapped into, if it exists and has not been used, pair them.
//
// Note this function assumes the given value is a struct value. // Note this function assumes the given value is a struct value.
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) { func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
typ := value.Type() typ := value.Type()
@ -227,7 +225,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
// second round ~~~ // second round ~~~
for _, argName := range argNames { for _, argName := range argNames {
structFieldName := ToCamelCase(argName) structFieldName := ToCamelCase(argName)
if structFieldName == "" { if structFieldName == "" {

View File

@ -32,7 +32,7 @@ type reflectTest struct {
var reflectTests = []reflectTest{ var reflectTests = []reflectTest{
{ {
name: "OneToOneCorrespondance", name: "OneToOneCorrespondence",
args: []string{"fieldA"}, args: []string{"fieldA"},
struc: struct { struc: struct {
FieldA int `abi:"fieldA"` FieldA int `abi:"fieldA"`

View File

@ -0,0 +1,176 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"fmt"
)
type SelectorMarshaling struct {
Name string `json:"name"`
Type string `json:"type"`
Inputs []ArgumentMarshaling `json:"inputs"`
}
func isDigit(c byte) bool {
return c >= '0' && c <= '9'
}
func isAlpha(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
func isIdentifierSymbol(c byte) bool {
return c == '$' || c == '_'
}
func parseToken(unescapedSelector string, isIdent bool) (string, string, error) {
if len(unescapedSelector) == 0 {
return "", "", fmt.Errorf("empty token")
}
firstChar := unescapedSelector[0]
position := 1
if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
return "", "", fmt.Errorf("invalid token start: %c", firstChar)
}
for position < len(unescapedSelector) {
char := unescapedSelector[position]
if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
break
}
position++
}
return unescapedSelector[:position], unescapedSelector[position:], nil
}
func parseIdentifier(unescapedSelector string) (string, string, error) {
return parseToken(unescapedSelector, true)
}
func parseElementaryType(unescapedSelector string) (string, string, error) {
parsedType, rest, err := parseToken(unescapedSelector, false)
if err != nil {
return "", "", fmt.Errorf("failed to parse elementary type: %v", err)
}
// handle arrays
for len(rest) > 0 && rest[0] == '[' {
parsedType = parsedType + string(rest[0])
rest = rest[1:]
for len(rest) > 0 && isDigit(rest[0]) {
parsedType = parsedType + string(rest[0])
rest = rest[1:]
}
if len(rest) == 0 || rest[0] != ']' {
return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
}
parsedType = parsedType + string(rest[0])
rest = rest[1:]
}
return parsedType, rest, nil
}
func parseCompositeType(unescapedSelector string) ([]interface{}, string, error) {
if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
}
parsedType, rest, err := parseType(unescapedSelector[1:])
if err != nil {
return nil, "", fmt.Errorf("failed to parse type: %v", err)
}
result := []interface{}{parsedType}
for len(rest) > 0 && rest[0] != ')' {
parsedType, rest, err = parseType(rest[1:])
if err != nil {
return nil, "", fmt.Errorf("failed to parse type: %v", err)
}
result = append(result, parsedType)
}
if len(rest) == 0 || rest[0] != ')' {
return nil, "", fmt.Errorf("expected ')', got '%s'", rest)
}
if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' {
return append(result, "[]"), rest[3:], nil
}
return result, rest[1:], nil
}
func parseType(unescapedSelector string) (interface{}, string, error) {
if len(unescapedSelector) == 0 {
return nil, "", fmt.Errorf("empty type")
}
if unescapedSelector[0] == '(' {
return parseCompositeType(unescapedSelector)
} else {
return parseElementaryType(unescapedSelector)
}
}
func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
arguments := make([]ArgumentMarshaling, 0)
for i, arg := range args {
// generate dummy name to avoid unmarshal issues
name := fmt.Sprintf("name%d", i)
if s, ok := arg.(string); ok {
arguments = append(arguments, ArgumentMarshaling{name, s, s, nil, false})
} else if components, ok := arg.([]interface{}); ok {
subArgs, err := assembleArgs(components)
if err != nil {
return nil, fmt.Errorf("failed to assemble components: %v", err)
}
tupleType := "tuple"
if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" {
subArgs = subArgs[:len(subArgs)-1]
tupleType = "tuple[]"
}
arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false})
} else {
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
}
}
return arguments, nil
}
// ParseSelector converts a method selector into a struct that can be JSON encoded
// and consumed by other functions in this package.
// Note, although uppercase letters are not part of the ABI spec, this function
// still accepts it as the general format is valid.
func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
name, rest, err := parseIdentifier(unescapedSelector)
if err != nil {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
}
args := []interface{}{}
if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
rest = rest[2:]
} else {
args, rest, err = parseCompositeType(rest)
if err != nil {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
}
}
if len(rest) > 0 {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
}
// Reassemble the fake ABI and construct the JSON
fakeArgs, err := assembleArgs(args)
if err != nil {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector: %v", err)
}
return SelectorMarshaling{name, "function", fakeArgs}, nil
}

View File

@ -0,0 +1,79 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"fmt"
"log"
"reflect"
"testing"
)
func TestParseSelector(t *testing.T) {
mkType := func(types ...interface{}) []ArgumentMarshaling {
var result []ArgumentMarshaling
for i, typeOrComponents := range types {
name := fmt.Sprintf("name%d", i)
if typeName, ok := typeOrComponents.(string); ok {
result = append(result, ArgumentMarshaling{name, typeName, typeName, nil, false})
} else if components, ok := typeOrComponents.([]ArgumentMarshaling); ok {
result = append(result, ArgumentMarshaling{name, "tuple", "tuple", components, false})
} else if components, ok := typeOrComponents.([][]ArgumentMarshaling); ok {
result = append(result, ArgumentMarshaling{name, "tuple[]", "tuple[]", components[0], false})
} else {
log.Fatalf("unexpected type %T", typeOrComponents)
}
}
return result
}
tests := []struct {
input string
name string
args []ArgumentMarshaling
}{
{"noargs()", "noargs", []ArgumentMarshaling{}},
{"simple(uint256,uint256,uint256)", "simple", mkType("uint256", "uint256", "uint256")},
{"other(uint256,address)", "other", mkType("uint256", "address")},
{"withArray(uint256[],address[2],uint8[4][][5])", "withArray", mkType("uint256[]", "address[2]", "uint8[4][][5]")},
{"singleNest(bytes32,uint8,(uint256,uint256),address)", "singleNest", mkType("bytes32", "uint8", mkType("uint256", "uint256"), "address")},
{"multiNest(address,(uint256[],uint256),((address,bytes32),uint256))", "multiNest",
mkType("address", mkType("uint256[]", "uint256"), mkType(mkType("address", "bytes32"), "uint256"))},
{"arrayNest((uint256,uint256)[],bytes32)", "arrayNest", mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, "bytes32")},
{"multiArrayNest((uint256,uint256)[],(uint256,uint256)[])", "multiArrayNest",
mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, [][]ArgumentMarshaling{mkType("uint256", "uint256")})},
{"singleArrayNestAndArray((uint256,uint256)[],bytes32[])", "singleArrayNestAndArray",
mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, "bytes32[]")},
{"singleArrayNestWithArrayAndArray((uint256[],address[2],uint8[4][][5])[],bytes32[])", "singleArrayNestWithArrayAndArray",
mkType([][]ArgumentMarshaling{mkType("uint256[]", "address[2]", "uint8[4][][5]")}, "bytes32[]")},
}
for i, tt := range tests {
selector, err := ParseSelector(tt.input)
if err != nil {
t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err)
}
if selector.Name != tt.name {
t.Errorf("test %d: unexpected function name: '%s' != '%s'", i, selector.Name, tt.name)
}
if selector.Type != "function" {
t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function")
}
if !reflect.DeepEqual(selector.Inputs, tt.args) {
t.Errorf("test %d: unexpected args: '%v' != '%v'", i, selector.Inputs, tt.args)
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2019 The go-ethereum Authors // Copyright 2020 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

View File

@ -23,6 +23,8 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"unicode"
"unicode/utf8"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -152,6 +154,9 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
if varSize == 0 { if varSize == 0 {
typ.T = BytesTy typ.T = BytesTy
} else { } else {
if varSize > 32 {
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
typ.T = FixedBytesTy typ.T = FixedBytesTy
typ.Size = varSize typ.Size = varSize
} }
@ -161,19 +166,26 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
elems []*Type elems []*Type
names []string names []string
expression string // canonical parameter expression expression string // canonical parameter expression
used = make(map[string]bool)
) )
expression += "(" expression += "("
overloadedNames := make(map[string]string)
for idx, c := range components { for idx, c := range components {
cType, err := NewType(c.Type, c.InternalType, c.Components) cType, err := NewType(c.Type, c.InternalType, c.Components)
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
fieldName, err := overloadedArgName(c.Name, overloadedNames) name := ToCamelCase(c.Name)
if name == "" {
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
}
fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
overloadedNames[fieldName] = fieldName used[fieldName] = true
if !isValidFieldName(fieldName) {
return Type{}, fmt.Errorf("field %d has invalid name", idx)
}
fields = append(fields, reflect.StructField{ fields = append(fields, reflect.StructField{
Name: fieldName, // reflect.StructOf will panic for any exported field. Name: fieldName, // reflect.StructOf will panic for any exported field.
Type: cType.GetType(), Type: cType.GetType(),
@ -201,7 +213,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
if internalType != "" && strings.HasPrefix(internalType, structPrefix) { if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
// Foo.Bar type definition is not allowed in golang, // Foo.Bar type definition is not allowed in golang,
// convert the format to FooBar // convert the format to FooBar
typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1) typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
} }
case "function": case "function":
@ -250,20 +262,6 @@ func (t Type) GetType() reflect.Type {
} }
} }
func overloadedArgName(rawName string, names map[string]string) (string, error) {
fieldName := ToCamelCase(rawName)
if fieldName == "" {
return "", errors.New("abi: purely anonymous or underscored field is not supported")
}
// Handle overloaded fieldNames
_, ok := names[fieldName]
for idx := 0; ok; idx++ {
fieldName = fmt.Sprintf("%s%d", ToCamelCase(rawName), idx)
_, ok = names[fieldName]
}
return fieldName, nil
}
// String implements Stringer. // String implements Stringer.
func (t Type) String() (out string) { func (t Type) String() (out string) {
return t.stringKind return t.stringKind
@ -399,3 +397,30 @@ func getTypeSize(t Type) int {
} }
return 32 return 32
} }
// isLetter reports whether a given 'rune' is classified as a Letter.
// This method is copied from reflect/type.go
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
// isValidFieldName checks if a string is a valid (struct) field name or not.
//
// According to the language spec, a field name should be an identifier.
//
// identifier = letter { letter | unicode_digit } .
// letter = unicode_letter | "_" .
// This method is copied from reflect/type.go
func isValidFieldName(fieldName string) bool {
for i, c := range fieldName {
if i == 0 && !isLetter(c) {
return false
}
if !(isLetter(c) || unicode.IsDigit(c)) {
return false
}
}
return len(fieldName) > 0
}

View File

@ -366,3 +366,10 @@ func TestGetTypeSize(t *testing.T) {
} }
} }
} }
func TestNewFixedBytesOver32(t *testing.T) {
_, err := NewType("bytes4096", "", nil)
if err == nil {
t.Errorf("fixed bytes with size over 32 is not spec'd")
}
}

View File

@ -19,6 +19,7 @@ package abi
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math"
"math/big" "math/big"
"reflect" "reflect"
@ -33,43 +34,72 @@ var (
) )
// ReadInteger reads the integer based on its kind and returns the appropriate value. // ReadInteger reads the integer based on its kind and returns the appropriate value.
func ReadInteger(typ Type, b []byte) interface{} { func ReadInteger(typ Type, b []byte) (interface{}, error) {
ret := new(big.Int).SetBytes(b)
if typ.T == UintTy { if typ.T == UintTy {
u64, isu64 := ret.Uint64(), ret.IsUint64()
switch typ.Size { switch typ.Size {
case 8: case 8:
return b[len(b)-1] if !isu64 || u64 > math.MaxUint8 {
return nil, errBadUint8
}
return byte(u64), nil
case 16: case 16:
return binary.BigEndian.Uint16(b[len(b)-2:]) if !isu64 || u64 > math.MaxUint16 {
return nil, errBadUint16
}
return uint16(u64), nil
case 32: case 32:
return binary.BigEndian.Uint32(b[len(b)-4:]) if !isu64 || u64 > math.MaxUint32 {
return nil, errBadUint32
}
return uint32(u64), nil
case 64: case 64:
return binary.BigEndian.Uint64(b[len(b)-8:]) if !isu64 {
return nil, errBadUint64
}
return u64, nil
default: default:
// the only case left for unsigned integer is uint256. // the only case left for unsigned integer is uint256.
return new(big.Int).SetBytes(b) return ret, nil
} }
} }
// big.SetBytes can't tell if a number is negative or positive in itself.
// On EVM, if the returned number > max int256, it is negative.
// A number is > max int256 if the bit at position 255 is set.
if ret.Bit(255) == 1 {
ret.Add(MaxUint256, new(big.Int).Neg(ret))
ret.Add(ret, common.Big1)
ret.Neg(ret)
}
i64, isi64 := ret.Int64(), ret.IsInt64()
switch typ.Size { switch typ.Size {
case 8: case 8:
return int8(b[len(b)-1]) if !isi64 || i64 < math.MinInt8 || i64 > math.MaxInt8 {
return nil, errBadInt8
}
return int8(i64), nil
case 16: case 16:
return int16(binary.BigEndian.Uint16(b[len(b)-2:])) if !isi64 || i64 < math.MinInt16 || i64 > math.MaxInt16 {
return nil, errBadInt16
}
return int16(i64), nil
case 32: case 32:
return int32(binary.BigEndian.Uint32(b[len(b)-4:])) if !isi64 || i64 < math.MinInt32 || i64 > math.MaxInt32 {
return nil, errBadInt32
}
return int32(i64), nil
case 64: case 64:
return int64(binary.BigEndian.Uint64(b[len(b)-8:])) if !isi64 {
return nil, errBadInt64
}
return i64, nil
default: default:
// the only case left for integer is int256 // the only case left for integer is int256
// big.SetBytes can't tell if a number is negative or positive in itself.
// On EVM, if the returned number > max int256, it is negative. return ret, nil
// A number is > max int256 if the bit at position 255 is set.
ret := new(big.Int).SetBytes(b)
if ret.Bit(255) == 1 {
ret.Add(MaxUint256, new(big.Int).Neg(ret))
ret.Add(ret, common.Big1)
ret.Neg(ret)
}
return ret
} }
} }
@ -115,7 +145,6 @@ func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
reflect.Copy(array, reflect.ValueOf(word[0:t.Size])) reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
return array.Interface(), nil return array.Interface(), nil
} }
// forEachUnpack iteratively unpack elements. // forEachUnpack iteratively unpack elements.
@ -124,7 +153,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size) return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
} }
if start+32*size > len(output) { if start+32*size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size) return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
} }
// this value will become our slice or our array, depending on the type // this value will become our slice or our array, depending on the type
@ -163,6 +192,9 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
virtualArgs := 0 virtualArgs := 0
for index, elem := range t.TupleElems { for index, elem := range t.TupleElems {
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output) marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
if err != nil {
return nil, err
}
if elem.T == ArrayTy && !isDynamicType(*elem) { if elem.T == ArrayTy && !isDynamicType(*elem) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
@ -180,9 +212,6 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
// coded as just like uint256,bool,uint256 // coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(*elem)/32 - 1 virtualArgs += getTypeSize(*elem)/32 - 1
} }
if err != nil {
return nil, err
}
retval.Field(index).Set(reflect.ValueOf(marshalledValue)) retval.Field(index).Set(reflect.ValueOf(marshalledValue))
} }
return retval.Interface(), nil return retval.Interface(), nil
@ -235,7 +264,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case StringTy: // variable arrays are written at the end of the return bytes case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+length]), nil return string(output[begin : begin+length]), nil
case IntTy, UintTy: case IntTy, UintTy:
return ReadInteger(t, returnOutput), nil return ReadInteger(t, returnOutput)
case BoolTy: case BoolTy:
return readBool(returnOutput) return readBool(returnOutput)
case AddressTy: case AddressTy:
@ -255,7 +284,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type. // lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) { func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32]) bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
bigOffsetEnd.Add(bigOffsetEnd, common.Big32) bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
outputLength := big.NewInt(int64(len(output))) outputLength := big.NewInt(int64(len(output)))
@ -268,11 +297,9 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
} }
offsetEnd := int(bigOffsetEnd.Uint64()) offsetEnd := int(bigOffsetEnd.Uint64())
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd]) lengthBig := new(big.Int).SetBytes(output[offsetEnd-32 : offsetEnd])
totalSize := big.NewInt(0) totalSize := new(big.Int).Add(bigOffsetEnd, lengthBig)
totalSize.Add(totalSize, bigOffsetEnd)
totalSize.Add(totalSize, lengthBig)
if totalSize.BitLen() > 63 { if totalSize.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize) return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
} }
@ -287,10 +314,10 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
// tuplePointsTo resolves the location reference for dynamic tuple. // tuplePointsTo resolves the location reference for dynamic tuple.
func tuplePointsTo(index int, output []byte) (start int, err error) { func tuplePointsTo(index int, output []byte) (start int, err error) {
offset := big.NewInt(0).SetBytes(output[index : index+32]) offset := new(big.Int).SetBytes(output[index : index+32])
outputLen := big.NewInt(int64(len(output))) outputLen := big.NewInt(int64(len(output)))
if offset.Cmp(big.NewInt(int64(len(output)))) > 0 { if offset.Cmp(outputLen) > 0 {
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen) return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
} }
if offset.BitLen() > 63 { if offset.BitLen() > 63 {

View File

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math"
"math/big" "math/big"
"reflect" "reflect"
"strconv" "strconv"
@ -201,6 +202,23 @@ var unpackTests = []unpackTest{
IntOne *big.Int IntOne *big.Int
}{big.NewInt(1)}, }{big.NewInt(1)},
}, },
{
def: `[{"type":"bool"}]`,
enc: "",
want: false,
err: "abi: attempting to unmarshall an empty string while arguments are expected",
},
{
def: `[{"type":"bytes32","indexed":true},{"type":"uint256","indexed":false}]`,
enc: "",
want: false,
err: "abi: attempting to unmarshall an empty string while arguments are expected",
},
{
def: `[{"type":"bool","indexed":true},{"type":"uint64","indexed":true}]`,
enc: "",
want: false,
},
} }
// TestLocalUnpackTests runs test specially designed only for unpacking. // TestLocalUnpackTests runs test specially designed only for unpacking.
@ -335,6 +353,11 @@ func TestMethodMultiReturn(t *testing.T) {
&[]interface{}{&expected.Int, &expected.String}, &[]interface{}{&expected.Int, &expected.String},
"", "",
"Can unpack into a slice", "Can unpack into a slice",
}, {
&[]interface{}{&bigint, ""},
&[]interface{}{&expected.Int, expected.String},
"",
"Can unpack into a slice without indirection",
}, { }, {
&[2]interface{}{&bigint, new(string)}, &[2]interface{}{&bigint, new(string)},
&[2]interface{}{&expected.Int, &expected.String}, &[2]interface{}{&expected.Int, &expected.String},
@ -407,7 +430,7 @@ func TestMultiReturnWithStringArray(t *testing.T) {
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000")) buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
temp, _ := big.NewInt(0).SetString("30000000000000000000", 10) temp, _ := new(big.Int).SetString("30000000000000000000", 10)
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp} ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f") ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"} ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
@ -762,20 +785,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)
} }
} }
@ -917,3 +944,164 @@ func TestOOMMaliciousInput(t *testing.T) {
} }
} }
} }
func TestPackAndUnpackIncompatibleNumber(t *testing.T) {
var encodeABI Arguments
uint256Ty, err := NewType("uint256", "", nil)
if err != nil {
panic(err)
}
encodeABI = Arguments{
{Type: uint256Ty},
}
maxU64, ok := new(big.Int).SetString(strconv.FormatUint(math.MaxUint64, 10), 10)
if !ok {
panic("bug")
}
maxU64Plus1 := new(big.Int).Add(maxU64, big.NewInt(1))
cases := []struct {
decodeType string
inputValue *big.Int
err error
expectValue interface{}
}{
{
decodeType: "uint8",
inputValue: big.NewInt(math.MaxUint8 + 1),
err: errBadUint8,
},
{
decodeType: "uint8",
inputValue: big.NewInt(math.MaxUint8),
err: nil,
expectValue: uint8(math.MaxUint8),
},
{
decodeType: "uint16",
inputValue: big.NewInt(math.MaxUint16 + 1),
err: errBadUint16,
},
{
decodeType: "uint16",
inputValue: big.NewInt(math.MaxUint16),
err: nil,
expectValue: uint16(math.MaxUint16),
},
{
decodeType: "uint32",
inputValue: big.NewInt(math.MaxUint32 + 1),
err: errBadUint32,
},
{
decodeType: "uint32",
inputValue: big.NewInt(math.MaxUint32),
err: nil,
expectValue: uint32(math.MaxUint32),
},
{
decodeType: "uint64",
inputValue: maxU64Plus1,
err: errBadUint64,
},
{
decodeType: "uint64",
inputValue: maxU64,
err: nil,
expectValue: uint64(math.MaxUint64),
},
{
decodeType: "uint256",
inputValue: maxU64Plus1,
err: nil,
expectValue: maxU64Plus1,
},
{
decodeType: "int8",
inputValue: big.NewInt(math.MaxInt8 + 1),
err: errBadInt8,
},
{
decodeType: "int8",
inputValue: big.NewInt(math.MinInt8 - 1),
err: errBadInt8,
},
{
decodeType: "int8",
inputValue: big.NewInt(math.MaxInt8),
err: nil,
expectValue: int8(math.MaxInt8),
},
{
decodeType: "int16",
inputValue: big.NewInt(math.MaxInt16 + 1),
err: errBadInt16,
},
{
decodeType: "int16",
inputValue: big.NewInt(math.MinInt16 - 1),
err: errBadInt16,
},
{
decodeType: "int16",
inputValue: big.NewInt(math.MaxInt16),
err: nil,
expectValue: int16(math.MaxInt16),
},
{
decodeType: "int32",
inputValue: big.NewInt(math.MaxInt32 + 1),
err: errBadInt32,
},
{
decodeType: "int32",
inputValue: big.NewInt(math.MinInt32 - 1),
err: errBadInt32,
},
{
decodeType: "int32",
inputValue: big.NewInt(math.MaxInt32),
err: nil,
expectValue: int32(math.MaxInt32),
},
{
decodeType: "int64",
inputValue: new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)),
err: errBadInt64,
},
{
decodeType: "int64",
inputValue: new(big.Int).Sub(big.NewInt(math.MinInt64), big.NewInt(1)),
err: errBadInt64,
},
{
decodeType: "int64",
inputValue: big.NewInt(math.MaxInt64),
err: nil,
expectValue: int64(math.MaxInt64),
},
}
for i, testCase := range cases {
packed, err := encodeABI.Pack(testCase.inputValue)
if err != nil {
panic(err)
}
ty, err := NewType(testCase.decodeType, "", nil)
if err != nil {
panic(err)
}
decodeABI := Arguments{
{Type: ty},
}
decoded, err := decodeABI.Unpack(packed)
if err != testCase.err {
t.Fatalf("Expected error %v, actual error %v. case %d", testCase.err, err, i)
}
if err != nil {
continue
}
if !reflect.DeepEqual(decoded[0], testCase.expectValue) {
t.Fatalf("Expected value %v, actual value %v", testCase.expectValue, decoded[0])
}
}
}

40
accounts/abi/utils.go Normal file
View File

@ -0,0 +1,40 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import "fmt"
// ResolveNameConflict returns the next available name for a given thing.
// This helper can be used for lots of purposes:
//
// - In solidity function overloading is supported, this function can fix
// the name conflicts of overloaded functions.
// - In golang binding generation, the parameter(in function, event, error,
// and struct definition) name will be converted to camelcase style which
// may eventually lead to name conflicts.
//
// Name conflicts are mostly resolved by adding number suffix. e.g. if the abi contains
// Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send".
func ResolveNameConflict(rawName string, used func(string) bool) string {
name := rawName
ok := used(name)
for idx := 0; ok; idx++ {
name = fmt.Sprintf("%s%d", rawName, idx)
ok = used(name)
}
return name
}

View File

@ -46,7 +46,7 @@ const (
// accounts (derived from the same seed). // accounts (derived from the same seed).
type Wallet interface { type Wallet interface {
// URL retrieves the canonical path under which this wallet is reachable. It is // URL retrieves the canonical path under which this wallet is reachable. It is
// user by upper layers to define a sorting order over all wallets from multiple // used by upper layers to define a sorting order over all wallets from multiple
// backends. // backends.
URL() URL URL() URL
@ -89,7 +89,7 @@ type Wallet interface {
// accounts. // accounts.
// //
// Note, self derivation will increment the last component of the specified path // Note, self derivation will increment the last component of the specified path
// opposed to decending into a child path to allow discovering accounts starting // opposed to descending into a child path to allow discovering accounts starting
// from non zero components. // from non zero components.
// //
// Some hardware wallets switched derivation paths through their evolution, so // Some hardware wallets switched derivation paths through their evolution, so
@ -105,7 +105,7 @@ type Wallet interface {
// or optionally with the aid of any location metadata from the embedded URL field. // or optionally with the aid of any location metadata from the embedded URL field.
// //
// If the wallet requires additional authentication to sign the request (e.g. // If the wallet requires additional authentication to sign the request (e.g.
// a password to decrypt the account, or a PIN code o verify the transaction), // a password to decrypt the account, or a PIN code to verify the transaction),
// an AuthNeededError instance will be returned, containing infos for the user // an AuthNeededError instance will be returned, containing infos for the user
// about which fields or actions are needed. The user may retry by providing // about which fields or actions are needed. The user may retry by providing
// the needed details via SignDataWithPassphrase, or by other means (e.g. unlock // the needed details via SignDataWithPassphrase, or by other means (e.g. unlock
@ -124,13 +124,13 @@ type Wallet interface {
// or optionally with the aid of any location metadata from the embedded URL field. // or optionally with the aid of any location metadata from the embedded URL field.
// //
// If the wallet requires additional authentication to sign the request (e.g. // If the wallet requires additional authentication to sign the request (e.g.
// a password to decrypt the account, or a PIN code o verify the transaction), // a password to decrypt the account, or a PIN code to verify the transaction),
// an AuthNeededError instance will be returned, containing infos for the user // an AuthNeededError instance will be returned, containing infos for the user
// about which fields or actions are needed. The user may retry by providing // about which fields or actions are needed. The user may retry by providing
// the needed details via SignTextWithPassphrase, or by other means (e.g. unlock // the needed details via SignTextWithPassphrase, or by other means (e.g. unlock
// the account in a keystore). // the account in a keystore).
// //
// This method should return the signature in 'canonical' format, with v 0 or 1 // This method should return the signature in 'canonical' format, with v 0 or 1.
SignText(account Account, text []byte) ([]byte, error) SignText(account Account, text []byte) ([]byte, error)
// SignTextWithPassphrase is identical to Signtext, but also takes a password // SignTextWithPassphrase is identical to Signtext, but also takes a password
@ -176,8 +176,9 @@ type Backend interface {
// TextHash is a helper function that calculates a hash for the given message that can be // TextHash is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from. // safely used to calculate a signature from.
// //
// The hash is calulcated as // The hash is calculated as
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). //
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
// //
// This gives context to the signed message and prevents signing of transactions. // This gives context to the signed message and prevents signing of transactions.
func TextHash(data []byte) []byte { func TextHash(data []byte) []byte {
@ -188,8 +189,9 @@ func TextHash(data []byte) []byte {
// TextAndHash is a helper function that calculates a hash for the given message that can be // TextAndHash is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from. // safely used to calculate a signature from.
// //
// The hash is calulcated as // The hash is calculated as
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). //
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
// //
// This gives context to the signed message and prevents signing of transactions. // This gives context to the signed message and prevents signing of transactions.
func TextAndHash(data []byte) ([]byte, string) { func TextAndHash(data []byte) ([]byte, string) {

View File

@ -41,8 +41,7 @@ var ErrInvalidPassphrase = errors.New("invalid password")
// second time. // second time.
var ErrWalletAlreadyOpen = errors.New("wallet already open") var ErrWalletAlreadyOpen = errors.New("wallet already open")
// ErrWalletClosed is returned if a wallet is attempted to be opened the // ErrWalletClosed is returned if a wallet is offline.
// secodn time.
var ErrWalletClosed = errors.New("wallet closed") var ErrWalletClosed = errors.New("wallet closed")
// AuthNeededError is returned by backends for signing requests where the user // AuthNeededError is returned by backends for signing requests where the user

View File

@ -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 {
@ -152,10 +152,6 @@ func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain eth
log.Error("operation SelfDerive not supported on external signers") log.Error("operation SelfDerive not supported on external signers")
} }
func (api *ExternalSigner) signHash(account accounts.Account, hash []byte) ([]byte, error) {
return []byte{}, fmt.Errorf("operation not supported on external signers")
}
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
var res hexutil.Bytes var res hexutil.Bytes
@ -196,6 +192,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,26 +203,34 @@ 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()),
Gas: hexutil.Uint64(tx.Gas()), Gas: hexutil.Uint64(tx.Gas()),
GasPrice: hexutil.Big(*tx.GasPrice()), To: to,
To: to, From: common.NewMixedcaseAddress(account.Address),
From: common.NewMixedcaseAddress(account.Address), }
switch tx.Type() {
case types.LegacyTxType, types.AccessListTxType:
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
case types.DynamicFeeTxType:
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
default:
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)
} }
// However, if the user asked for a particular chain id, then we should if tx.Type() != types.LegacyTxType {
// use that instead. // However, if the user asked for a particular chain id, then we should
if tx.Type() != types.LegacyTxType && tx.ChainId() != nil { // use that instead.
args.ChainID = (*hexutil.Big)(tx.ChainId()) if tx.ChainId().Sign() != 0 {
} args.ChainID = (*hexutil.Big)(tx.ChainId())
if tx.Type() == types.AccessListTxType { }
accessList := tx.AccessList() accessList := tx.AccessList()
args.AccessList = &accessList args.AccessList = &accessList
} }

View File

@ -41,12 +41,12 @@ var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60,
var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0} var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
// DerivationPath represents the computer friendly version of a hierarchical // DerivationPath represents the computer friendly version of a hierarchical
// deterministic wallet account derivaion path. // deterministic wallet account derivation path.
// //
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki // The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
// defines derivation paths to be of the form: // defines derivation paths to be of the form:
// //
// m / purpose' / coin_type' / account' / change / address_index // m / purpose' / coin_type' / account' / change / address_index
// //
// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki // The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and // defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and

View File

@ -27,7 +27,7 @@ import (
"sync" "sync"
"time" "time"
mapset "github.com/deckarep/golang-set" mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -79,7 +79,7 @@ func newAccountCache(keydir string) (*accountCache, chan struct{}) {
keydir: keydir, keydir: keydir,
byAddr: make(map[common.Address][]accounts.Account), byAddr: make(map[common.Address][]accounts.Account),
notify: make(chan struct{}, 1), notify: make(chan struct{}, 1),
fileC: fileCache{all: mapset.NewThreadUnsafeSet()}, fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()},
} }
ac.watcher = newWatcher(ac) ac.watcher = newWatcher(ac)
return ac, ac.notify return ac, ac.notify
@ -146,6 +146,14 @@ func (ac *accountCache) deleteByFile(path string) {
} }
} }
// watcherStarted returns true if the watcher loop started running (even if it
// has since also ended).
func (ac *accountCache) watcherStarted() bool {
ac.mu.Lock()
defer ac.mu.Unlock()
return ac.watcher.running || ac.watcher.runEnded
}
func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account { func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
for i := range slice { for i := range slice {
if slice[i] == elem { if slice[i] == elem {
@ -275,16 +283,15 @@ func (ac *accountCache) scanAccounts() error {
// Process all the file diffs // Process all the file diffs
start := time.Now() start := time.Now()
for _, p := range creates.ToSlice() { for _, path := range creates.ToSlice() {
if a := readAccount(p.(string)); a != nil { if a := readAccount(path); a != nil {
ac.add(*a) ac.add(*a)
} }
} }
for _, p := range deletes.ToSlice() { for _, path := range deletes.ToSlice() {
ac.deleteByFile(p.(string)) ac.deleteByFile(path)
} }
for _, p := range updates.ToSlice() { for _, path := range updates.ToSlice() {
path := p.(string)
ac.deleteByFile(path) ac.deleteByFile(path)
if a := readAccount(path); a != nil { if a := readAccount(path); a != nil {
ac.add(*a) ac.add(*a)

View File

@ -18,7 +18,6 @@ package keystore
import ( import (
"fmt" "fmt"
"io/ioutil"
"math/rand" "math/rand"
"os" "os"
"path/filepath" "path/filepath"
@ -51,16 +50,48 @@ var (
} }
) )
// waitWatcherStarts waits up to 1s for the keystore watcher to start.
func waitWatcherStart(ks *KeyStore) bool {
// On systems where file watch is not supported, just return "ok".
if !ks.cache.watcher.enabled() {
return true
}
// The watcher should start, and then exit.
for t0 := time.Now(); time.Since(t0) < 1*time.Second; time.Sleep(100 * time.Millisecond) {
if ks.cache.watcherStarted() {
return true
}
}
return false
}
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
var list []accounts.Account
for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(200 * time.Millisecond) {
list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
// ks should have also received change notifications
select {
case <-ks.changes:
default:
return fmt.Errorf("wasn't notified of new accounts")
}
return nil
}
}
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
}
func TestWatchNewFile(t *testing.T) { func TestWatchNewFile(t *testing.T) {
t.Parallel() t.Parallel()
dir, ks := tmpKeyStore(t, false) dir, ks := tmpKeyStore(t, false)
defer os.RemoveAll(dir)
// Ensure the watcher is started before adding any files. // Ensure the watcher is started before adding any files.
ks.Accounts() ks.Accounts()
time.Sleep(1000 * time.Millisecond) if !waitWatcherStart(ks) {
t.Fatal("keystore watcher didn't start in time")
}
// Move in the files. // Move in the files.
wantAccounts := make([]accounts.Account, len(cachetestAccounts)) wantAccounts := make([]accounts.Account, len(cachetestAccounts))
for i := range cachetestAccounts { for i := range cachetestAccounts {
@ -74,37 +105,24 @@ func TestWatchNewFile(t *testing.T) {
} }
// ks should see the accounts. // ks should see the accounts.
var list []accounts.Account if err := waitForAccounts(wantAccounts, ks); err != nil {
for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 { t.Error(err)
list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
// ks should have also received change notifications
select {
case <-ks.changes:
default:
t.Fatalf("wasn't notified of new accounts")
}
return
}
time.Sleep(d)
} }
t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
} }
func TestWatchNoDir(t *testing.T) { func TestWatchNoDir(t *testing.T) {
t.Parallel() t.Parallel()
// Create ks but not the directory that it watches. // Create ks but not the directory that it watches.
rand.Seed(time.Now().UnixNano()) dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
ks := NewKeyStore(dir, LightScryptN, LightScryptP) ks := NewKeyStore(dir, LightScryptN, LightScryptP)
list := ks.Accounts() list := ks.Accounts()
if len(list) > 0 { if len(list) > 0 {
t.Error("initial account list not empty:", list) t.Error("initial account list not empty:", list)
} }
time.Sleep(100 * time.Millisecond) // The watcher should start, and then exit.
if !waitWatcherStart(ks) {
t.Fatal("keystore watcher didn't start in time")
}
// Create the directory and copy a key file into it. // Create the directory and copy a key file into it.
os.MkdirAll(dir, 0700) os.MkdirAll(dir, 0700)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
@ -297,40 +315,22 @@ func TestCacheFind(t *testing.T) {
} }
} }
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
var list []accounts.Account
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
// ks should have also received change notifications
select {
case <-ks.changes:
default:
return fmt.Errorf("wasn't notified of new accounts")
}
return nil
}
time.Sleep(d)
}
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
}
// TestUpdatedKeyfileContents tests that updating the contents of a keystore file // TestUpdatedKeyfileContents tests that updating the contents of a keystore file
// is noticed by the watcher, and the account cache is updated accordingly // is noticed by the watcher, and the account cache is updated accordingly
func TestUpdatedKeyfileContents(t *testing.T) { func TestUpdatedKeyfileContents(t *testing.T) {
t.Parallel() t.Parallel()
// Create a temporary kesytore to test with // Create a temporary keystore to test with
rand.Seed(time.Now().UnixNano()) dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
ks := NewKeyStore(dir, LightScryptN, LightScryptP) ks := NewKeyStore(dir, LightScryptN, LightScryptP)
list := ks.Accounts() list := ks.Accounts()
if len(list) > 0 { if len(list) > 0 {
t.Error("initial account list not empty:", list) t.Error("initial account list not empty:", list)
} }
time.Sleep(100 * time.Millisecond) if !waitWatcherStart(ks) {
t.Fatal("keystore watcher didn't start in time")
}
// Create the directory and copy a key file into it. // Create the directory and copy a key file into it.
os.MkdirAll(dir, 0700) os.MkdirAll(dir, 0700)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
@ -348,9 +348,8 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
// needed so that modTime of `file` is different to its current value after forceCopyFile // needed so that modTime of `file` is different to its current value after forceCopyFile
time.Sleep(1000 * time.Millisecond) time.Sleep(time.Second)
// Now replace file contents // Now replace file contents
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil { if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
@ -366,7 +365,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
} }
// needed so that modTime of `file` is different to its current value after forceCopyFile // needed so that modTime of `file` is different to its current value after forceCopyFile
time.Sleep(1000 * time.Millisecond) time.Sleep(time.Second)
// Now replace file contents again // Now replace file contents again
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil { if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
@ -381,11 +380,11 @@ func TestUpdatedKeyfileContents(t *testing.T) {
return return
} }
// needed so that modTime of `file` is different to its current value after ioutil.WriteFile // needed so that modTime of `file` is different to its current value after os.WriteFile
time.Sleep(1000 * time.Millisecond) time.Sleep(time.Second)
// Now replace file contents with crap // Now replace file contents with crap
if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil { if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
t.Fatal(err) t.Fatal(err)
return return
} }
@ -398,9 +397,9 @@ func TestUpdatedKeyfileContents(t *testing.T) {
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists. // forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
func forceCopyFile(dst, src string) error { func forceCopyFile(dst, src string) error {
data, err := ioutil.ReadFile(src) data, err := os.ReadFile(src)
if err != nil { if err != nil {
return err return err
} }
return ioutil.WriteFile(dst, data, 0644) return os.WriteFile(dst, data, 0644)
} }

View File

@ -17,31 +17,30 @@
package keystore package keystore
import ( import (
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"time" "time"
mapset "github.com/deckarep/golang-set" mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// fileCache is a cache of files seen during scan of keystore. // fileCache is a cache of files seen during scan of keystore.
type fileCache struct { type fileCache struct {
all mapset.Set // Set of all files from the keystore folder all mapset.Set[string] // Set of all files from the keystore folder
lastMod time.Time // Last time instance when a file was modified lastMod time.Time // Last time instance when a file was modified
mu sync.Mutex mu sync.Mutex
} }
// scan performs a new scan on the given directory, compares against the already // scan performs a new scan on the given directory, compares against the already
// cached filenames, and returns file sets: creates, deletes, updates. // cached filenames, and returns file sets: creates, deletes, updates.
func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) { func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string], mapset.Set[string], error) {
t0 := time.Now() t0 := time.Now()
// List all the failes from the keystore folder // List all the files from the keystore folder
files, err := ioutil.ReadDir(keyDir) files, err := os.ReadDir(keyDir)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -51,8 +50,8 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
defer fc.mu.Unlock() defer fc.mu.Unlock()
// Iterate all the files and gather their metadata // Iterate all the files and gather their metadata
all := mapset.NewThreadUnsafeSet() all := mapset.NewThreadUnsafeSet[string]()
mods := mapset.NewThreadUnsafeSet() mods := mapset.NewThreadUnsafeSet[string]()
var newLastMod time.Time var newLastMod time.Time
for _, fi := range files { for _, fi := range files {
@ -62,10 +61,14 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
log.Trace("Ignoring file on account scan", "path", path) log.Trace("Ignoring file on account scan", "path", path)
continue continue
} }
// Gather the set of all and fresly modified files // Gather the set of all and freshly modified files
all.Add(path) all.Add(path)
modified := fi.ModTime() info, err := fi.Info()
if err != nil {
return nil, nil, nil, err
}
modified := info.ModTime()
if modified.After(fc.lastMod) { if modified.After(fc.lastMod) {
mods.Add(path) mods.Add(path)
} }
@ -89,13 +92,13 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
} }
// nonKeyFile ignores editor backups, hidden files and folders/symlinks. // nonKeyFile ignores editor backups, hidden files and folders/symlinks.
func nonKeyFile(fi os.FileInfo) bool { func nonKeyFile(fi os.DirEntry) bool {
// Skip editor backups and UNIX-style hidden files. // Skip editor backups and UNIX-style hidden files.
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
return true return true
} }
// Skip misc special files, directories (yes, symlinks too). // Skip misc special files, directories (yes, symlinks too).
if fi.IsDir() || fi.Mode()&os.ModeType != 0 { if fi.IsDir() || !fi.Type().IsRegular() {
return true return true
} }
return false return false

View File

@ -23,7 +23,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -197,7 +196,7 @@ func writeTemporaryKeyFile(file string, content []byte) (string, error) {
} }
// Atomic write: create a temporary hidden file first // Atomic write: create a temporary hidden file first
// then move it into place. TempFile assigns mode 0600. // then move it into place. TempFile assigns mode 0600.
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -498,6 +498,14 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
return a, nil return a, nil
} }
// isUpdating returns whether the event notification loop is running.
// This method is mainly meant for tests.
func (ks *KeyStore) isUpdating() bool {
ks.mu.RLock()
defer ks.mu.RUnlock()
return ks.updating
}
// zeroKey zeroes a private key in memory. // zeroKey zeroes a private key in memory.
func zeroKey(k *ecdsa.PrivateKey) { func zeroKey(k *ecdsa.PrivateKey) {
b := k.D.Bits() b := k.D.Bits()

View File

@ -17,7 +17,6 @@
package keystore package keystore
import ( import (
"io/ioutil"
"math/rand" "math/rand"
"os" "os"
"runtime" "runtime"
@ -38,7 +37,6 @@ var testSigData = make([]byte, 32)
func TestKeyStore(t *testing.T) { func TestKeyStore(t *testing.T) {
dir, ks := tmpKeyStore(t, true) dir, ks := tmpKeyStore(t, true)
defer os.RemoveAll(dir)
a, err := ks.NewAccount("foo") a, err := ks.NewAccount("foo")
if err != nil { if err != nil {
@ -72,8 +70,7 @@ func TestKeyStore(t *testing.T) {
} }
func TestSign(t *testing.T) { func TestSign(t *testing.T) {
dir, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
defer os.RemoveAll(dir)
pass := "" // not used but required by API pass := "" // not used but required by API
a1, err := ks.NewAccount(pass) a1, err := ks.NewAccount(pass)
@ -89,8 +86,7 @@ func TestSign(t *testing.T) {
} }
func TestSignWithPassphrase(t *testing.T) { func TestSignWithPassphrase(t *testing.T) {
dir, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
defer os.RemoveAll(dir)
pass := "passwd" pass := "passwd"
acc, err := ks.NewAccount(pass) acc, err := ks.NewAccount(pass)
@ -117,8 +113,8 @@ func TestSignWithPassphrase(t *testing.T) {
} }
func TestTimedUnlock(t *testing.T) { func TestTimedUnlock(t *testing.T) {
dir, ks := tmpKeyStore(t, true) t.Parallel()
defer os.RemoveAll(dir) _, ks := tmpKeyStore(t, true)
pass := "foo" pass := "foo"
a1, err := ks.NewAccount(pass) a1, err := ks.NewAccount(pass)
@ -152,8 +148,8 @@ func TestTimedUnlock(t *testing.T) {
} }
func TestOverrideUnlock(t *testing.T) { func TestOverrideUnlock(t *testing.T) {
dir, ks := tmpKeyStore(t, false) t.Parallel()
defer os.RemoveAll(dir) _, ks := tmpKeyStore(t, false)
pass := "foo" pass := "foo"
a1, err := ks.NewAccount(pass) a1, err := ks.NewAccount(pass)
@ -193,8 +189,8 @@ func TestOverrideUnlock(t *testing.T) {
// This test should fail under -race if signing races the expiration goroutine. // This test should fail under -race if signing races the expiration goroutine.
func TestSignRace(t *testing.T) { func TestSignRace(t *testing.T) {
dir, ks := tmpKeyStore(t, false) t.Parallel()
defer os.RemoveAll(dir) _, ks := tmpKeyStore(t, false)
// Create a test account. // Create a test account.
a1, err := ks.NewAccount("") a1, err := ks.NewAccount("")
@ -218,20 +214,33 @@ func TestSignRace(t *testing.T) {
t.Errorf("Account did not lock within the timeout") t.Errorf("Account did not lock within the timeout")
} }
// waitForKsUpdating waits until the updating-status of the ks reaches the
// desired wantStatus.
// It waits for a maximum time of maxTime, and returns false if it does not
// finish in time
func waitForKsUpdating(t *testing.T, ks *KeyStore, wantStatus bool, maxTime time.Duration) bool {
t.Helper()
// Wait max 250 ms, then return false
for t0 := time.Now(); time.Since(t0) < maxTime; {
if ks.isUpdating() == wantStatus {
return true
}
time.Sleep(25 * time.Millisecond)
}
return false
}
// Tests that the wallet notifier loop starts and stops correctly based on the // Tests that the wallet notifier loop starts and stops correctly based on the
// addition and removal of wallet event subscriptions. // addition and removal of wallet event subscriptions.
func TestWalletNotifierLifecycle(t *testing.T) { func TestWalletNotifierLifecycle(t *testing.T) {
// Create a temporary kesytore to test with t.Parallel()
dir, ks := tmpKeyStore(t, false) // Create a temporary keystore to test with
defer os.RemoveAll(dir) _, ks := tmpKeyStore(t, false)
// Ensure that the notification updater is not running yet // Ensure that the notification updater is not running yet
time.Sleep(250 * time.Millisecond) time.Sleep(250 * time.Millisecond)
ks.mu.RLock()
updating := ks.updating
ks.mu.RUnlock()
if updating { if ks.isUpdating() {
t.Errorf("wallet notifier running without subscribers") t.Errorf("wallet notifier running without subscribers")
} }
// Subscribe to the wallet feed and ensure the updater boots up // Subscribe to the wallet feed and ensure the updater boots up
@ -241,38 +250,26 @@ func TestWalletNotifierLifecycle(t *testing.T) {
for i := 0; i < len(subs); i++ { for i := 0; i < len(subs); i++ {
// Create a new subscription // Create a new subscription
subs[i] = ks.Subscribe(updates) subs[i] = ks.Subscribe(updates)
if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) {
// Ensure the notifier comes online
time.Sleep(250 * time.Millisecond)
ks.mu.RLock()
updating = ks.updating
ks.mu.RUnlock()
if !updating {
t.Errorf("sub %d: wallet notifier not running after subscription", i) t.Errorf("sub %d: wallet notifier not running after subscription", i)
} }
} }
// Unsubscribe and ensure the updater terminates eventually // Close all but one sub
for i := 0; i < len(subs); i++ { for i := 0; i < len(subs)-1; i++ {
// Close an existing subscription // Close an existing subscription
subs[i].Unsubscribe() subs[i].Unsubscribe()
// Ensure the notifier shuts down at and only at the last close
for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ {
ks.mu.RLock()
updating = ks.updating
ks.mu.RUnlock()
if i < len(subs)-1 && !updating {
t.Fatalf("sub %d: event notifier stopped prematurely", i)
}
if i == len(subs)-1 && !updating {
return
}
time.Sleep(250 * time.Millisecond)
}
} }
t.Errorf("wallet notifier didn't terminate after unsubscribe") // Check that it is still running
time.Sleep(250 * time.Millisecond)
if !ks.isUpdating() {
t.Fatal("event notifier stopped prematurely")
}
// Unsubscribe the last one and ensure the updater terminates eventually.
subs[len(subs)-1].Unsubscribe()
if !waitForKsUpdating(t, ks, false, 4*time.Second) {
t.Errorf("wallet notifier didn't terminate after unsubscribe")
}
} }
type walletEvent struct { type walletEvent struct {
@ -283,8 +280,7 @@ type walletEvent struct {
// Tests that wallet notifications and correctly fired when accounts are added // Tests that wallet notifications and correctly fired when accounts are added
// or deleted from the keystore. // or deleted from the keystore.
func TestWalletNotifications(t *testing.T) { func TestWalletNotifications(t *testing.T) {
dir, ks := tmpKeyStore(t, false) _, ks := tmpKeyStore(t, false)
defer os.RemoveAll(dir)
// Subscribe to the wallet feed and collect events. // Subscribe to the wallet feed and collect events.
var ( var (
@ -345,8 +341,7 @@ func TestWalletNotifications(t *testing.T) {
// TestImportExport tests the import functionality of a keystore. // TestImportExport tests the import functionality of a keystore.
func TestImportECDSA(t *testing.T) { func TestImportECDSA(t *testing.T) {
dir, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
defer os.RemoveAll(dir)
key, err := crypto.GenerateKey() key, err := crypto.GenerateKey()
if err != nil { if err != nil {
t.Fatalf("failed to generate key: %v", key) t.Fatalf("failed to generate key: %v", key)
@ -364,8 +359,7 @@ func TestImportECDSA(t *testing.T) {
// TestImportECDSA tests the import and export functionality of a keystore. // TestImportECDSA tests the import and export functionality of a keystore.
func TestImportExport(t *testing.T) { func TestImportExport(t *testing.T) {
dir, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
defer os.RemoveAll(dir)
acc, err := ks.NewAccount("old") acc, err := ks.NewAccount("old")
if err != nil { if err != nil {
t.Fatalf("failed to create account: %v", acc) t.Fatalf("failed to create account: %v", acc)
@ -374,8 +368,7 @@ func TestImportExport(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to export account: %v", acc) t.Fatalf("failed to export account: %v", acc)
} }
dir2, ks2 := tmpKeyStore(t, true) _, ks2 := tmpKeyStore(t, true)
defer os.RemoveAll(dir2)
if _, err = ks2.Import(json, "old", "old"); err == nil { if _, err = ks2.Import(json, "old", "old"); err == nil {
t.Errorf("importing with invalid password succeeded") t.Errorf("importing with invalid password succeeded")
} }
@ -389,14 +382,12 @@ func TestImportExport(t *testing.T) {
if _, err = ks2.Import(json, "new", "new"); err == nil { if _, err = ks2.Import(json, "new", "new"); err == nil {
t.Errorf("importing a key twice succeeded") t.Errorf("importing a key twice succeeded")
} }
} }
// TestImportRace tests the keystore on races. // TestImportRace tests the keystore on races.
// This test should fail under -race if importing races. // This test should fail under -race if importing races.
func TestImportRace(t *testing.T) { func TestImportRace(t *testing.T) {
dir, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
defer os.RemoveAll(dir)
acc, err := ks.NewAccount("old") acc, err := ks.NewAccount("old")
if err != nil { if err != nil {
t.Fatalf("failed to create account: %v", acc) t.Fatalf("failed to create account: %v", acc)
@ -405,8 +396,7 @@ func TestImportRace(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to export account: %v", acc) t.Fatalf("failed to export account: %v", acc)
} }
dir2, ks2 := tmpKeyStore(t, true) _, ks2 := tmpKeyStore(t, true)
defer os.RemoveAll(dir2)
var atom uint32 var atom uint32
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(2)
@ -416,7 +406,6 @@ func TestImportRace(t *testing.T) {
if _, err := ks2.Import(json, "new", "new"); err != nil { if _, err := ks2.Import(json, "new", "new"); err != nil {
atomic.AddUint32(&atom, 1) atomic.AddUint32(&atom, 1)
} }
}() }()
} }
wg.Wait() wg.Wait()
@ -462,10 +451,7 @@ func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
} }
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
d, err := ioutil.TempDir("", "eth-keystore-test") d := t.TempDir()
if err != nil {
t.Fatal(err)
}
newKs := NewPlaintextKeyStore newKs := NewPlaintextKeyStore
if encrypted { if encrypted {
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }

View File

@ -34,7 +34,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -82,7 +81,7 @@ type keyStorePassphrase struct {
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
// Load the key from the keystore and decrypt its contents // Load the key from the keystore and decrypt its contents
keyjson, err := ioutil.ReadFile(filename) keyjson, err := os.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -139,7 +138,6 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
// Encryptdata encrypts the data given as 'data' with the password 'auth'. // Encryptdata encrypts the data given as 'data' with the password 'auth'.
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
salt := make([]byte, 32) salt := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, salt); err != nil { if _, err := io.ReadFull(rand.Reader, salt); err != nil {
panic("reading from crypto/rand failed: " + err.Error()) panic("reading from crypto/rand failed: " + err.Error())
@ -342,7 +340,6 @@ func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
r := ensureInt(cryptoJSON.KDFParams["r"]) r := ensureInt(cryptoJSON.KDFParams["r"])
p := ensureInt(cryptoJSON.KDFParams["p"]) p := ensureInt(cryptoJSON.KDFParams["p"])
return scrypt.Key(authArray, salt, n, r, p, dkLen) return scrypt.Key(authArray, salt, n, r, p, dkLen)
} else if cryptoJSON.KDF == "pbkdf2" { } else if cryptoJSON.KDF == "pbkdf2" {
c := ensureInt(cryptoJSON.KDFParams["c"]) c := ensureInt(cryptoJSON.KDFParams["c"])
prf := cryptoJSON.KDFParams["prf"].(string) prf := cryptoJSON.KDFParams["prf"].(string)

View File

@ -17,7 +17,7 @@
package keystore package keystore
import ( import (
"io/ioutil" "os"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -30,7 +30,7 @@ const (
// Tests that a json key file can be decrypted and encrypted in multiple rounds. // Tests that a json key file can be decrypted and encrypted in multiple rounds.
func TestKeyEncryptDecrypt(t *testing.T) { func TestKeyEncryptDecrypt(t *testing.T) {
keyjson, err := ioutil.ReadFile("testdata/very-light-scrypt.json") keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -52,7 +52,7 @@ func TestKeyEncryptDecrypt(t *testing.T) {
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address) t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
} }
// Recrypt with a new password and start over // Recrypt with a new password and start over
password += "new data appended" password += "new data appended" // nolint: gosec
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil { if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
t.Errorf("test %d: failed to recrypt key %v", i, err) t.Errorf("test %d: failed to recrypt key %v", i, err)
} }

View File

@ -20,8 +20,6 @@ import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil"
"os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
@ -32,10 +30,7 @@ import (
) )
func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) { func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
d, err := ioutil.TempDir("", "geth-keystore-test") d := t.TempDir()
if err != nil {
t.Fatal(err)
}
if encrypted { if encrypted {
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true} ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
} else { } else {
@ -45,8 +40,7 @@ func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
} }
func TestKeyStorePlain(t *testing.T) { func TestKeyStorePlain(t *testing.T) {
dir, ks := tmpKeyStoreIface(t, false) _, ks := tmpKeyStoreIface(t, false)
defer os.RemoveAll(dir)
pass := "" // not used but required by API pass := "" // not used but required by API
k1, account, err := storeNewKey(ks, rand.Reader, pass) k1, account, err := storeNewKey(ks, rand.Reader, pass)
@ -66,8 +60,7 @@ func TestKeyStorePlain(t *testing.T) {
} }
func TestKeyStorePassphrase(t *testing.T) { func TestKeyStorePassphrase(t *testing.T) {
dir, ks := tmpKeyStoreIface(t, true) _, ks := tmpKeyStoreIface(t, true)
defer os.RemoveAll(dir)
pass := "foo" pass := "foo"
k1, account, err := storeNewKey(ks, rand.Reader, pass) k1, account, err := storeNewKey(ks, rand.Reader, pass)
@ -87,8 +80,7 @@ func TestKeyStorePassphrase(t *testing.T) {
} }
func TestKeyStorePassphraseDecryptionFail(t *testing.T) { func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
dir, ks := tmpKeyStoreIface(t, true) _, ks := tmpKeyStoreIface(t, true)
defer os.RemoveAll(dir)
pass := "foo" pass := "foo"
k1, account, err := storeNewKey(ks, rand.Reader, pass) k1, account, err := storeNewKey(ks, rand.Reader, pass)
@ -102,7 +94,6 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
func TestImportPreSaleKey(t *testing.T) { func TestImportPreSaleKey(t *testing.T) {
dir, ks := tmpKeyStoreIface(t, true) dir, ks := tmpKeyStoreIface(t, true)
defer os.RemoveAll(dir)
// file content of a presale key file generated with: // file content of a presale key file generated with:
// python pyethsaletool.py genwallet // python pyethsaletool.py genwallet

View File

@ -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
@ -22,25 +23,27 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/rjeczalik/notify" "github.com/fsnotify/fsnotify"
) )
type watcher struct { type watcher struct {
ac *accountCache ac *accountCache
starting bool running bool // set to true when runloop begins
running bool runEnded bool // set to true when runloop ends
ev chan notify.EventInfo starting bool // set to true prior to runloop starting
quit chan struct{} quit chan struct{}
} }
func newWatcher(ac *accountCache) *watcher { func newWatcher(ac *accountCache) *watcher {
return &watcher{ return &watcher{
ac: ac, ac: ac,
ev: make(chan notify.EventInfo, 10),
quit: make(chan struct{}), quit: make(chan struct{}),
} }
} }
// enabled returns false on systems not supported.
func (*watcher) enabled() bool { return true }
// starts the watcher loop in the background. // starts the watcher loop in the background.
// Start a watcher in the background if that's not already in progress. // Start a watcher in the background if that's not already in progress.
// The caller must hold w.ac.mu. // The caller must hold w.ac.mu.
@ -61,16 +64,24 @@ func (w *watcher) loop() {
w.ac.mu.Lock() w.ac.mu.Lock()
w.running = false w.running = false
w.starting = false w.starting = false
w.runEnded = true
w.ac.mu.Unlock() w.ac.mu.Unlock()
}() }()
logger := log.New("path", w.ac.keydir) logger := log.New("path", w.ac.keydir)
if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil { // Create new watcher.
logger.Trace("Failed to watch keystore folder", "err", err) watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Error("Failed to start filesystem watcher", "err", err)
return return
} }
defer notify.Stop(w.ev) defer watcher.Close()
logger.Trace("Started watching keystore folder") if err := watcher.Add(w.ac.keydir); err != nil {
logger.Warn("Failed to watch keystore folder", "err", err)
return
}
logger.Trace("Started watching keystore folder", "folder", w.ac.keydir)
defer logger.Trace("Stopped watching keystore folder") defer logger.Trace("Stopped watching keystore folder")
w.ac.mu.Lock() w.ac.mu.Lock()
@ -94,12 +105,24 @@ func (w *watcher) loop() {
select { select {
case <-w.quit: case <-w.quit:
return return
case <-w.ev: case _, ok := <-watcher.Events:
if !ok {
return
}
// Trigger the scan (with delay), if not already triggered // Trigger the scan (with delay), if not already triggered
if !rescanTriggered { if !rescanTriggered {
debounce.Reset(debounceDuration) debounce.Reset(debounceDuration)
rescanTriggered = true rescanTriggered = true
} }
// The fsnotify library does provide more granular event-info, it
// would be possible to refresh individual affected files instead
// of scheduling a full rescan. For most cases though, the
// full rescan is quick and obviously simplest.
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Info("Filsystem watcher error", "err", err)
case <-debounce.C: case <-debounce.C:
w.ac.scanAccounts() w.ac.scanAccounts()
rescanTriggered = false rescanTriggered = false

View File

@ -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.
@ -21,8 +22,14 @@
package keystore package keystore
type watcher struct{ running bool } type watcher struct {
running bool
runEnded bool
}
func newWatcher(*accountCache) *watcher { return new(watcher) } func newWatcher(*accountCache) *watcher { return new(watcher) }
func (*watcher) start() {} func (*watcher) start() {}
func (*watcher) close() {} func (*watcher) close() {}
// enabled returns false on systems not supported.
func (*watcher) enabled() bool { return false }

View File

@ -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]
} }
@ -219,7 +257,7 @@ func merge(slice []Wallet, wallets ...Wallet) []Wallet {
return slice return slice
} }
// drop is the couterpart of merge, which looks up wallets from within the sorted // drop is the counterpart of merge, which looks up wallets from within the sorted
// cache and removes the ones specified. // cache and removes the ones specified.
func drop(slice []Wallet, wallets ...Wallet) []Wallet { func drop(slice []Wallet, wallets ...Wallet) []Wallet {
for _, wallet := range wallets { for _, wallet := range wallets {

View File

@ -34,7 +34,7 @@ package scwallet
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -96,7 +96,7 @@ func (hub *Hub) readPairings() error {
return err return err
} }
pairingData, err := ioutil.ReadAll(pairingFile) pairingData, err := io.ReadAll(pairingFile)
if err != nil { if err != nil {
return err return err
} }

View File

@ -178,7 +178,7 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
return err return err
} }
if response.Sw1 != 0x90 || response.Sw2 != 0x00 { if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2) return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2)
} }
if len(response.Data) != scSecretLength { if len(response.Data) != scSecretLength {
@ -261,7 +261,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
rapdu.deserialize(plainData) rapdu.deserialize(plainData)
if rapdu.Sw1 != sw1Ok { if rapdu.Sw1 != sw1Ok {
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2) return nil, fmt.Errorf("unexpected response status Cla=%#x, Ins=%#x, Sw=%#x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
} }
return rapdu, nil return rapdu, nil

View File

@ -99,8 +99,8 @@ const (
P1DeriveKeyFromCurrent = uint8(0x10) P1DeriveKeyFromCurrent = uint8(0x10)
statusP1WalletStatus = uint8(0x00) statusP1WalletStatus = uint8(0x00)
statusP1Path = uint8(0x01) statusP1Path = uint8(0x01)
signP1PrecomputedHash = uint8(0x01) signP1PrecomputedHash = uint8(0x00)
signP2OnlyBlock = uint8(0x81) signP2OnlyBlock = uint8(0x00)
exportP1Any = uint8(0x00) exportP1Any = uint8(0x00)
exportP2Pubkey = uint8(0x01) exportP2Pubkey = uint8(0x01)
) )
@ -167,7 +167,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
} }
if response.Sw1 != sw1Ok { if response.Sw1 != sw1Ok {
return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2) return nil, fmt.Errorf("unexpected insecure response status Cla=%#x, Ins=%#x, Sw=%#x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
} }
return response, nil return response, nil
@ -638,7 +638,7 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
// accounts. // accounts.
// //
// Note, self derivation will increment the last component of the specified path // Note, self derivation will increment the last component of the specified path
// opposed to decending into a child path to allow discovering accounts starting // opposed to descending into a child path to allow discovering accounts starting
// from non zero components. // from non zero components.
// //
// Some hardware wallets switched derivation paths through their evolution, so // Some hardware wallets switched derivation paths through their evolution, so
@ -879,6 +879,7 @@ func (s *Session) walletStatus() (*walletStatus, error) {
} }
// derivationPath fetches the wallet's current derivation path from the card. // derivationPath fetches the wallet's current derivation path from the card.
//
//lint:ignore U1000 needs to be added to the console interface //lint:ignore U1000 needs to be added to the console interface
func (s *Session) derivationPath() (accounts.DerivationPath, error) { func (s *Session) derivationPath() (accounts.DerivationPath, error) {
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil) response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
@ -994,6 +995,7 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
} }
// keyExport contains information on an exported keypair. // keyExport contains information on an exported keypair.
//
//lint:ignore U1000 needs to be added to the console interface //lint:ignore U1000 needs to be added to the console interface
type keyExport struct { type keyExport struct {
PublicKey []byte `asn1:"tag:0"` PublicKey []byte `asn1:"tag:0"`
@ -1001,6 +1003,7 @@ type keyExport struct {
} }
// publicKey returns the public key for the current derivation path. // publicKey returns the public key for the current derivation path.
//
//lint:ignore U1000 needs to be added to the console interface //lint:ignore U1000 needs to be added to the console interface
func (s *Session) publicKey() ([]byte, error) { func (s *Session) publicKey() ([]byte, error) {
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil) response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)

View File

@ -92,10 +92,9 @@ func (u *URL) UnmarshalJSON(input []byte) error {
// Cmp compares x and y and returns: // Cmp compares x and y and returns:
// //
// -1 if x < y // -1 if x < y
// 0 if x == y // 0 if x == y
// +1 if x > y // +1 if x > y
//
func (u URL) Cmp(url URL) int { func (u URL) Cmp(url URL) int {
if u.Scheme == url.Scheme { if u.Scheme == url.Scheme {
return strings.Compare(u.Path, url.Path) return strings.Compare(u.Path, url.Path)

View File

@ -32,9 +32,10 @@ func TestURLParsing(t *testing.T) {
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path) t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
} }
_, err = parseURL("ethereum.org") for _, u := range []string{"ethereum.org", ""} {
if err == nil { if _, err = parseURL(u); err == nil {
t.Error("expected err, got: nil") t.Errorf("input %v, expected err, got: nil", u)
}
} }
} }

View File

@ -71,18 +71,28 @@ type Hub struct {
// NewLedgerHub creates a new hardware wallet manager for Ledger devices. // NewLedgerHub creates a new hardware wallet manager for Ledger devices.
func NewLedgerHub() (*Hub, error) { func NewLedgerHub() (*Hub, error) {
return newHub(LedgerScheme, 0x2c97, []uint16{ return newHub(LedgerScheme, 0x2c97, []uint16{
// Device definitions taken from
// https://github.com/LedgerHQ/ledger-live/blob/38012bc8899e0f07149ea9cfe7e64b2c146bc92b/libs/ledgerjs/packages/devices/src/index.ts
// Original product IDs // Original product IDs
0x0000, /* Ledger Blue */ 0x0000, /* Ledger Blue */
0x0001, /* Ledger Nano S */ 0x0001, /* Ledger Nano S */
0x0004, /* Ledger Nano X */ 0x0004, /* Ledger Nano X */
0x0005, /* Ledger Nano S Plus */
0x0006, /* Ledger Nano FTS */
// Upcoming product IDs: https://www.ledger.com/2019/05/17/windows-10-update-sunsetting-u2f-tunnel-transport-for-ledger-devices/
0x0015, /* HID + U2F + WebUSB Ledger Blue */ 0x0015, /* HID + U2F + WebUSB Ledger Blue */
0x1015, /* HID + U2F + WebUSB Ledger Nano S */ 0x1015, /* HID + U2F + WebUSB Ledger Nano S */
0x4015, /* HID + U2F + WebUSB Ledger Nano X */ 0x4015, /* HID + U2F + WebUSB Ledger Nano X */
0x5015, /* HID + U2F + WebUSB Ledger Nano S Plus */
0x6015, /* HID + U2F + WebUSB Ledger Nano FTS */
0x0011, /* HID + WebUSB Ledger Blue */ 0x0011, /* HID + WebUSB Ledger Blue */
0x1011, /* HID + WebUSB Ledger Nano S */ 0x1011, /* HID + WebUSB Ledger Nano S */
0x4011, /* HID + WebUSB Ledger Nano X */ 0x4011, /* HID + WebUSB Ledger Nano X */
0x5011, /* HID + WebUSB Ledger Nano S Plus */
0x6011, /* HID + WebUSB Ledger Nano FTS */
}, 0xffa0, 0, newLedgerDriver) }, 0xffa0, 0, newLedgerDriver)
} }

View File

@ -59,6 +59,8 @@ const (
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
ledgerEip155Size int = 3 // Size of the EIP-155 chain_id,r,s in unsigned transactions
) )
// errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange // errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange
@ -195,18 +197,18 @@ func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash
// //
// The version retrieval protocol is defined as follows: // The version retrieval protocol is defined as follows:
// //
// CLA | INS | P1 | P2 | Lc | Le // CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+----+----+--- // ----+-----+----+----+----+---
// E0 | 06 | 00 | 00 | 00 | 04 // E0 | 06 | 00 | 00 | 00 | 04
// //
// With no input data, and the output data being: // With no input data, and the output data being:
// //
// Description | Length // Description | Length
// ---------------------------------------------------+-------- // ---------------------------------------------------+--------
// Flags 01: arbitrary data signature enabled by user | 1 byte // Flags 01: arbitrary data signature enabled by user | 1 byte
// Application major version | 1 byte // Application major version | 1 byte
// Application minor version | 1 byte // Application minor version | 1 byte
// Application patch version | 1 byte // Application patch version | 1 byte
func (w *ledgerDriver) ledgerVersion() ([3]byte, error) { func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
// Send the request and wait for the response // Send the request and wait for the response
reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil) reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil)
@ -227,32 +229,32 @@ func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
// //
// The address derivation protocol is defined as follows: // The address derivation protocol is defined as follows:
// //
// CLA | INS | P1 | P2 | Lc | Le // CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+----+-----+--- // ----+-----+----+----+-----+---
// E0 | 02 | 00 return address // E0 | 02 | 00 return address
// 01 display address and confirm before returning // 01 display address and confirm before returning
// | 00: do not return the chain code // | 00: do not return the chain code
// | 01: return the chain code // | 01: return the chain code
// | var | 00 // | var | 00
// //
// Where the input data is: // Where the input data is:
// //
// Description | Length // Description | Length
// -------------------------------------------------+-------- // -------------------------------------------------+--------
// Number of BIP 32 derivations to perform (max 10) | 1 byte // Number of BIP 32 derivations to perform (max 10) | 1 byte
// First derivation index (big endian) | 4 bytes // First derivation index (big endian) | 4 bytes
// ... | 4 bytes // ... | 4 bytes
// Last derivation index (big endian) | 4 bytes // Last derivation index (big endian) | 4 bytes
// //
// And the output data is: // And the output data is:
// //
// Description | Length // Description | Length
// ------------------------+------------------- // ------------------------+-------------------
// Public Key length | 1 byte // Public Key length | 1 byte
// Uncompressed Public Key | arbitrary // Uncompressed Public Key | arbitrary
// Ethereum address length | 1 byte // Ethereum address length | 1 byte
// Ethereum address | 40 bytes hex ascii // Ethereum address | 40 bytes hex ascii
// Chain code if requested | 32 bytes // Chain code if requested | 32 bytes
func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) { func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) {
// Flatten the derivation path into the Ledger request // Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath)) path := make([]byte, 1+4*len(derivationPath))
@ -290,35 +292,35 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
// //
// The transaction signing protocol is defined as follows: // The transaction signing protocol is defined as follows:
// //
// CLA | INS | P1 | P2 | Lc | Le // CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+----+-----+--- // ----+-----+----+----+-----+---
// E0 | 04 | 00: first transaction data block // E0 | 04 | 00: first transaction data block
// 80: subsequent transaction data block // 80: subsequent transaction data block
// | 00 | variable | variable // | 00 | variable | variable
// //
// Where the input for the first transaction block (first 255 bytes) is: // Where the input for the first transaction block (first 255 bytes) is:
// //
// Description | Length // Description | Length
// -------------------------------------------------+---------- // -------------------------------------------------+----------
// Number of BIP 32 derivations to perform (max 10) | 1 byte // Number of BIP 32 derivations to perform (max 10) | 1 byte
// First derivation index (big endian) | 4 bytes // First derivation index (big endian) | 4 bytes
// ... | 4 bytes // ... | 4 bytes
// Last derivation index (big endian) | 4 bytes // Last derivation index (big endian) | 4 bytes
// RLP transaction chunk | arbitrary // RLP transaction chunk | arbitrary
// //
// And the input for subsequent transaction blocks (first 255 bytes) are: // And the input for subsequent transaction blocks (first 255 bytes) are:
// //
// Description | Length // Description | Length
// ----------------------+---------- // ----------------------+----------
// RLP transaction chunk | arbitrary // RLP transaction chunk | arbitrary
// //
// And the output data is: // And the output data is:
// //
// Description | Length // Description | Length
// ------------+--------- // ------------+---------
// signature V | 1 byte // signature V | 1 byte
// signature R | 32 bytes // signature R | 32 bytes
// signature S | 32 bytes // signature S | 32 bytes
func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) { func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
// Flatten the derivation path into the Ledger request // Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath)) path := make([]byte, 1+4*len(derivationPath))
@ -347,9 +349,15 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
op = ledgerP1InitTransactionData op = ledgerP1InitTransactionData
reply []byte reply []byte
) )
// Chunk size selection to mitigate an underlying RLP deserialization issue on the ledger app.
// https://github.com/LedgerHQ/app-ethereum/issues/409
chunk := 255
for ; len(payload)%chunk <= ledgerEip155Size; chunk-- {
}
for len(payload) > 0 { for len(payload) > 0 {
// Calculate the size of the next data chunk // Calculate the size of the next data chunk
chunk := 255
if chunk > len(payload) { if chunk > len(payload) {
chunk = len(payload) chunk = len(payload)
} }
@ -392,30 +400,28 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
// //
// The signing protocol is defined as follows: // The signing protocol is defined as follows:
// //
// CLA | INS | P1 | P2 | Lc | Le // CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+-----------------------------+-----+--- // ----+-----+----+-----------------------------+-----+---
// E0 | 0C | 00 | implementation version : 00 | variable | variable // E0 | 0C | 00 | implementation version : 00 | variable | variable
// //
// Where the input is: // Where the input is:
// //
// Description | Length // Description | Length
// -------------------------------------------------+---------- // -------------------------------------------------+----------
// Number of BIP 32 derivations to perform (max 10) | 1 byte // Number of BIP 32 derivations to perform (max 10) | 1 byte
// First derivation index (big endian) | 4 bytes // First derivation index (big endian) | 4 bytes
// ... | 4 bytes // ... | 4 bytes
// Last derivation index (big endian) | 4 bytes // Last derivation index (big endian) | 4 bytes
// domain hash | 32 bytes // domain hash | 32 bytes
// message hash | 32 bytes // message hash | 32 bytes
//
//
// //
// And the output data is: // And the output data is:
// //
// Description | Length // Description | Length
// ------------+--------- // ------------+---------
// signature V | 1 byte // signature V | 1 byte
// signature R | 32 bytes // signature R | 32 bytes
// signature S | 32 bytes // signature S | 32 bytes
func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHash []byte, messageHash []byte) ([]byte, error) { func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHash []byte, messageHash []byte) ([]byte, error) {
// Flatten the derivation path into the Ledger request // Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath)) path := make([]byte, 1+4*len(derivationPath))
@ -454,12 +460,12 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
// //
// The common transport header is defined as follows: // The common transport header is defined as follows:
// //
// Description | Length // Description | Length
// --------------------------------------+---------- // --------------------------------------+----------
// Communication channel ID (big endian) | 2 bytes // Communication channel ID (big endian) | 2 bytes
// Command tag | 1 byte // Command tag | 1 byte
// Packet sequence index (big endian) | 2 bytes // Packet sequence index (big endian) | 2 bytes
// Payload | arbitrary // Payload | arbitrary
// //
// The Communication channel ID allows commands multiplexing over the same // The Communication channel ID allows commands multiplexing over the same
// physical link. It is not used for the time being, and should be set to 0101 // physical link. It is not used for the time being, and should be set to 0101
@ -473,15 +479,15 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
// //
// APDU Command payloads are encoded as follows: // APDU Command payloads are encoded as follows:
// //
// Description | Length // Description | Length
// ----------------------------------- // -----------------------------------
// APDU length (big endian) | 2 bytes // APDU length (big endian) | 2 bytes
// APDU CLA | 1 byte // APDU CLA | 1 byte
// APDU INS | 1 byte // APDU INS | 1 byte
// APDU P1 | 1 byte // APDU P1 | 1 byte
// APDU P2 | 1 byte // APDU P2 | 1 byte
// APDU length | 1 byte // APDU length | 1 byte
// Optional APDU data | arbitrary // Optional APDU data | arbitrary
func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) { func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
// Construct the message payload, possibly split into multiple chunks // Construct the message payload, possibly split into multiple chunks
apdu := make([]byte, 2, 7+len(data)) apdu := make([]byte, 2, 7+len(data))

View File

@ -84,15 +84,15 @@ func (w *trezorDriver) Status() (string, error) {
// Open implements usbwallet.driver, attempting to initialize the connection to // Open implements usbwallet.driver, attempting to initialize the connection to
// the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation: // the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
// * The first phase is to initialize the connection and read the wallet's // - The first phase is to initialize the connection and read the wallet's
// features. This phase is invoked if the provided passphrase is empty. The // features. This phase is invoked if the provided passphrase is empty. The
// device will display the pinpad as a result and will return an appropriate // device will display the pinpad as a result and will return an appropriate
// error to notify the user that a second open phase is needed. // error to notify the user that a second open phase is needed.
// * The second phase is to unlock access to the Trezor, which is done by the // - The second phase is to unlock access to the Trezor, which is done by the
// user actually providing a passphrase mapping a keyboard keypad to the pin // user actually providing a passphrase mapping a keyboard keypad to the pin
// number of the user (shuffled according to the pinpad displayed). // number of the user (shuffled according to the pinpad displayed).
// * If needed the device will ask for passphrase which will require calling // - If needed the device will ask for passphrase which will require calling
// open again with the actual passphrase (3rd phase) // open again with the actual passphrase (3rd phase)
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error { func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
w.device, w.failure = device, nil w.device, w.failure = device, nil
@ -196,10 +196,10 @@ func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, er
if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil { if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil {
return common.Address{}, err return common.Address{}, err
} }
if addr := address.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary fomats if addr := address.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary formats
return common.BytesToAddress(addr), nil return common.BytesToAddress(addr), nil
} }
if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal fomats if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats
return common.HexToAddress(addr), nil return common.HexToAddress(addr), nil
} }
return common.Address{}, errors.New("missing derived address") return common.Address{}, errors.New("missing derived address")

View File

@ -94,7 +94,7 @@ func (Failure_FailureType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_aaf30d059fdbc38d, []int{1, 0} return fileDescriptor_aaf30d059fdbc38d, []int{1, 0}
} }
//* // *
// Type of button request // Type of button request
type ButtonRequest_ButtonRequestType int32 type ButtonRequest_ButtonRequestType int32
@ -175,7 +175,7 @@ func (ButtonRequest_ButtonRequestType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_aaf30d059fdbc38d, []int{2, 0} return fileDescriptor_aaf30d059fdbc38d, []int{2, 0}
} }
//* // *
// Type of PIN request // Type of PIN request
type PinMatrixRequest_PinMatrixRequestType int32 type PinMatrixRequest_PinMatrixRequestType int32
@ -220,7 +220,7 @@ func (PinMatrixRequest_PinMatrixRequestType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_aaf30d059fdbc38d, []int{4, 0} return fileDescriptor_aaf30d059fdbc38d, []int{4, 0}
} }
//* // *
// Response: Success of the previous request // Response: Success of the previous request
// @end // @end
type Success struct { type Success struct {
@ -262,7 +262,7 @@ func (m *Success) GetMessage() string {
return "" return ""
} }
//* // *
// Response: Failure of the previous request // Response: Failure of the previous request
// @end // @end
type Failure struct { type Failure struct {
@ -312,7 +312,7 @@ func (m *Failure) GetMessage() string {
return "" return ""
} }
//* // *
// Response: Device is waiting for HW button press. // Response: Device is waiting for HW button press.
// @auxstart // @auxstart
// @next ButtonAck // @next ButtonAck
@ -363,7 +363,7 @@ func (m *ButtonRequest) GetData() string {
return "" return ""
} }
//* // *
// Request: Computer agrees to wait for HW button press // Request: Computer agrees to wait for HW button press
// @auxend // @auxend
type ButtonAck struct { type ButtonAck struct {
@ -397,7 +397,7 @@ func (m *ButtonAck) XXX_DiscardUnknown() {
var xxx_messageInfo_ButtonAck proto.InternalMessageInfo var xxx_messageInfo_ButtonAck proto.InternalMessageInfo
//* // *
// Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme // Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme
// @auxstart // @auxstart
// @next PinMatrixAck // @next PinMatrixAck
@ -440,7 +440,7 @@ func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType {
return PinMatrixRequest_PinMatrixRequestType_Current return PinMatrixRequest_PinMatrixRequestType_Current
} }
//* // *
// Request: Computer responds with encoded PIN // Request: Computer responds with encoded PIN
// @auxend // @auxend
type PinMatrixAck struct { type PinMatrixAck struct {
@ -482,7 +482,7 @@ func (m *PinMatrixAck) GetPin() string {
return "" return ""
} }
//* // *
// Response: Device awaits encryption passphrase // Response: Device awaits encryption passphrase
// @auxstart // @auxstart
// @next PassphraseAck // @next PassphraseAck
@ -525,7 +525,7 @@ func (m *PassphraseRequest) GetOnDevice() bool {
return false return false
} }
//* // *
// Request: Send passphrase back // Request: Send passphrase back
// @next PassphraseStateRequest // @next PassphraseStateRequest
type PassphraseAck struct { type PassphraseAck struct {
@ -575,7 +575,7 @@ func (m *PassphraseAck) GetState() []byte {
return nil return nil
} }
//* // *
// Response: Device awaits passphrase state // Response: Device awaits passphrase state
// @next PassphraseStateAck // @next PassphraseStateAck
type PassphraseStateRequest struct { type PassphraseStateRequest struct {
@ -617,7 +617,7 @@ func (m *PassphraseStateRequest) GetState() []byte {
return nil return nil
} }
//* // *
// Request: Send passphrase state back // Request: Send passphrase state back
// @auxend // @auxend
type PassphraseStateAck struct { type PassphraseStateAck struct {
@ -651,7 +651,7 @@ func (m *PassphraseStateAck) XXX_DiscardUnknown() {
var xxx_messageInfo_PassphraseStateAck proto.InternalMessageInfo var xxx_messageInfo_PassphraseStateAck proto.InternalMessageInfo
//* // *
// Structure representing BIP32 (hierarchical deterministic) node // Structure representing BIP32 (hierarchical deterministic) node
// Used for imports of private key into the device and exporting public key out of device // Used for imports of private key into the device and exporting public key out of device
// @embed // @embed

View File

@ -21,7 +21,7 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
//* // *
// Request: Ask device for public key corresponding to address_n path // Request: Ask device for public key corresponding to address_n path
// @start // @start
// @next EthereumPublicKey // @next EthereumPublicKey
@ -73,7 +73,7 @@ func (m *EthereumGetPublicKey) GetShowDisplay() bool {
return false return false
} }
//* // *
// Response: Contains public key derived from device private seed // Response: Contains public key derived from device private seed
// @end // @end
type EthereumPublicKey struct { type EthereumPublicKey struct {
@ -123,7 +123,7 @@ func (m *EthereumPublicKey) GetXpub() string {
return "" return ""
} }
//* // *
// Request: Ask device for Ethereum address corresponding to address_n path // Request: Ask device for Ethereum address corresponding to address_n path
// @start // @start
// @next EthereumAddress // @next EthereumAddress
@ -175,7 +175,7 @@ func (m *EthereumGetAddress) GetShowDisplay() bool {
return false return false
} }
//* // *
// Response: Contains an Ethereum address derived from device private seed // Response: Contains an Ethereum address derived from device private seed
// @end // @end
type EthereumAddress struct { type EthereumAddress struct {
@ -225,7 +225,7 @@ func (m *EthereumAddress) GetAddressHex() string {
return "" return ""
} }
//* // *
// Request: Ask device to sign transaction // Request: Ask device to sign transaction
// All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing. // All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
// Note: the first at most 1024 bytes of data MUST be transmitted as part of this message. // Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
@ -351,7 +351,7 @@ func (m *EthereumSignTx) GetTxType() uint32 {
return 0 return 0
} }
//* // *
// Response: Device asks for more data from transaction payload, or returns the signature. // Response: Device asks for more data from transaction payload, or returns the signature.
// If data_length is set, device awaits that many more bytes of payload. // If data_length is set, device awaits that many more bytes of payload.
// Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present. // Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present.
@ -420,7 +420,7 @@ func (m *EthereumTxRequest) GetSignatureS() []byte {
return nil return nil
} }
//* // *
// Request: Transaction payload data. // Request: Transaction payload data.
// @next EthereumTxRequest // @next EthereumTxRequest
type EthereumTxAck struct { type EthereumTxAck struct {
@ -462,7 +462,7 @@ func (m *EthereumTxAck) GetDataChunk() []byte {
return nil return nil
} }
//* // *
// Request: Ask device to sign message // Request: Ask device to sign message
// @start // @start
// @next EthereumMessageSignature // @next EthereumMessageSignature
@ -514,7 +514,7 @@ func (m *EthereumSignMessage) GetMessage() []byte {
return nil return nil
} }
//* // *
// Response: Signed message // Response: Signed message
// @end // @end
type EthereumMessageSignature struct { type EthereumMessageSignature struct {
@ -572,7 +572,7 @@ func (m *EthereumMessageSignature) GetAddressHex() string {
return "" return ""
} }
//* // *
// Request: Ask device to verify message // Request: Ask device to verify message
// @start // @start
// @next Success // @next Success

View File

@ -21,7 +21,7 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
//* // *
// Structure representing passphrase source // Structure representing passphrase source
type ApplySettings_PassphraseSourceType int32 type ApplySettings_PassphraseSourceType int32
@ -66,7 +66,7 @@ func (ApplySettings_PassphraseSourceType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_0c720c20d27aa029, []int{4, 0} return fileDescriptor_0c720c20d27aa029, []int{4, 0}
} }
//* // *
// Type of recovery procedure. These should be used as bitmask, e.g., // Type of recovery procedure. These should be used as bitmask, e.g.,
// `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix` // `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
// listing every method supported by the host computer. // listing every method supported by the host computer.
@ -114,7 +114,7 @@ func (RecoveryDevice_RecoveryDeviceType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_0c720c20d27aa029, []int{17, 0} return fileDescriptor_0c720c20d27aa029, []int{17, 0}
} }
//* // *
// Type of Recovery Word request // Type of Recovery Word request
type WordRequest_WordRequestType int32 type WordRequest_WordRequestType int32
@ -159,7 +159,7 @@ func (WordRequest_WordRequestType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_0c720c20d27aa029, []int{18, 0} return fileDescriptor_0c720c20d27aa029, []int{18, 0}
} }
//* // *
// Request: Reset device to default state and ask for device details // Request: Reset device to default state and ask for device details
// @start // @start
// @next Features // @next Features
@ -210,7 +210,7 @@ func (m *Initialize) GetSkipPassphrase() bool {
return false return false
} }
//* // *
// Request: Ask for device details (no device reset) // Request: Ask for device details (no device reset)
// @start // @start
// @next Features // @next Features
@ -245,7 +245,7 @@ func (m *GetFeatures) XXX_DiscardUnknown() {
var xxx_messageInfo_GetFeatures proto.InternalMessageInfo var xxx_messageInfo_GetFeatures proto.InternalMessageInfo
//* // *
// Response: Reports various information about the device // Response: Reports various information about the device
// @end // @end
type Features struct { type Features struct {
@ -495,7 +495,7 @@ func (m *Features) GetNoBackup() bool {
return false return false
} }
//* // *
// Request: clear session (removes cached PIN, passphrase, etc). // Request: clear session (removes cached PIN, passphrase, etc).
// @start // @start
// @next Success // @next Success
@ -530,7 +530,7 @@ func (m *ClearSession) XXX_DiscardUnknown() {
var xxx_messageInfo_ClearSession proto.InternalMessageInfo var xxx_messageInfo_ClearSession proto.InternalMessageInfo
//* // *
// Request: change language and/or label of the device // Request: change language and/or label of the device
// @start // @start
// @next Success // @next Success
@ -622,7 +622,7 @@ func (m *ApplySettings) GetDisplayRotation() uint32 {
return 0 return 0
} }
//* // *
// Request: set flags of the device // Request: set flags of the device
// @start // @start
// @next Success // @next Success
@ -666,7 +666,7 @@ func (m *ApplyFlags) GetFlags() uint32 {
return 0 return 0
} }
//* // *
// Request: Starts workflow for setting/changing/removing the PIN // Request: Starts workflow for setting/changing/removing the PIN
// @start // @start
// @next Success // @next Success
@ -710,7 +710,7 @@ func (m *ChangePin) GetRemove() bool {
return false return false
} }
//* // *
// Request: Test if the device is alive, device sends back the message in Success response // Request: Test if the device is alive, device sends back the message in Success response
// @start // @start
// @next Success // @next Success
@ -777,7 +777,7 @@ func (m *Ping) GetPassphraseProtection() bool {
return false return false
} }
//* // *
// Request: Abort last operation that required user interaction // Request: Abort last operation that required user interaction
// @start // @start
// @next Failure // @next Failure
@ -812,7 +812,7 @@ func (m *Cancel) XXX_DiscardUnknown() {
var xxx_messageInfo_Cancel proto.InternalMessageInfo var xxx_messageInfo_Cancel proto.InternalMessageInfo
//* // *
// Request: Request a sample of random data generated by hardware RNG. May be used for testing. // Request: Request a sample of random data generated by hardware RNG. May be used for testing.
// @start // @start
// @next Entropy // @next Entropy
@ -856,7 +856,7 @@ func (m *GetEntropy) GetSize() uint32 {
return 0 return 0
} }
//* // *
// Response: Reply with random data generated by internal RNG // Response: Reply with random data generated by internal RNG
// @end // @end
type Entropy struct { type Entropy struct {
@ -898,7 +898,7 @@ func (m *Entropy) GetEntropy() []byte {
return nil return nil
} }
//* // *
// Request: Request device to wipe all sensitive data and settings // Request: Request device to wipe all sensitive data and settings
// @start // @start
// @next Success // @next Success
@ -934,7 +934,7 @@ func (m *WipeDevice) XXX_DiscardUnknown() {
var xxx_messageInfo_WipeDevice proto.InternalMessageInfo var xxx_messageInfo_WipeDevice proto.InternalMessageInfo
//* // *
// Request: Load seed and related internal settings from the computer // Request: Load seed and related internal settings from the computer
// @start // @start
// @next Success // @next Success
@ -1036,7 +1036,7 @@ func (m *LoadDevice) GetU2FCounter() uint32 {
return 0 return 0
} }
//* // *
// Request: Ask device to do initialization involving user interaction // Request: Ask device to do initialization involving user interaction
// @start // @start
// @next EntropyRequest // @next EntropyRequest
@ -1147,7 +1147,7 @@ func (m *ResetDevice) GetNoBackup() bool {
return false return false
} }
//* // *
// Request: Perform backup of the device seed if not backed up using ResetDevice // Request: Perform backup of the device seed if not backed up using ResetDevice
// @start // @start
// @next Success // @next Success
@ -1182,7 +1182,7 @@ func (m *BackupDevice) XXX_DiscardUnknown() {
var xxx_messageInfo_BackupDevice proto.InternalMessageInfo var xxx_messageInfo_BackupDevice proto.InternalMessageInfo
//* // *
// Response: Ask for additional entropy from host computer // Response: Ask for additional entropy from host computer
// @next EntropyAck // @next EntropyAck
type EntropyRequest struct { type EntropyRequest struct {
@ -1216,7 +1216,7 @@ func (m *EntropyRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_EntropyRequest proto.InternalMessageInfo var xxx_messageInfo_EntropyRequest proto.InternalMessageInfo
//* // *
// Request: Provide additional entropy for seed generation function // Request: Provide additional entropy for seed generation function
// @next Success // @next Success
type EntropyAck struct { type EntropyAck struct {
@ -1258,7 +1258,7 @@ func (m *EntropyAck) GetEntropy() []byte {
return nil return nil
} }
//* // *
// Request: Start recovery workflow asking user for specific words of mnemonic // Request: Start recovery workflow asking user for specific words of mnemonic
// Used to recovery device safely even on untrusted computer. // Used to recovery device safely even on untrusted computer.
// @start // @start
@ -1369,7 +1369,7 @@ func (m *RecoveryDevice) GetDryRun() bool {
return false return false
} }
//* // *
// Response: Device is waiting for user to enter word of the mnemonic // Response: Device is waiting for user to enter word of the mnemonic
// Its position is shown only on device's internal display. // Its position is shown only on device's internal display.
// @next WordAck // @next WordAck
@ -1412,7 +1412,7 @@ func (m *WordRequest) GetType() WordRequest_WordRequestType {
return WordRequest_WordRequestType_Plain return WordRequest_WordRequestType_Plain
} }
//* // *
// Request: Computer replies with word from the mnemonic // Request: Computer replies with word from the mnemonic
// @next WordRequest // @next WordRequest
// @next Success // @next Success
@ -1456,7 +1456,7 @@ func (m *WordAck) GetWord() string {
return "" return ""
} }
//* // *
// Request: Set U2F counter // Request: Set U2F counter
// @start // @start
// @next Success // @next Success

View File

@ -22,7 +22,7 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
//* // *
// Mapping between TREZOR wire identifier (uint) and a protobuf message // Mapping between TREZOR wire identifier (uint) and a protobuf message
type MessageType int32 type MessageType int32

View File

@ -380,7 +380,7 @@ func (w *wallet) selfDerive() {
// of legacy-ledger, the first account on the legacy-path will // of legacy-ledger, the first account on the legacy-path will
// be shown to the user, even if we don't actively track it // be shown to the user, even if we don't actively track it
if i < len(nextAddrs)-1 { if i < len(nextAddrs)-1 {
w.log.Info("Skipping trakcking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track", w.log.Info("Skipping tracking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
"path", path, "address", nextAddrs[i]) "path", path, "address", nextAddrs[i])
break break
} }
@ -496,7 +496,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
// accounts. // accounts.
// //
// Note, self derivation will increment the last component of the specified path // Note, self derivation will increment the last component of the specified path
// opposed to decending into a child path to allow discovering accounts starting // opposed to descending into a child path to allow discovering accounts starting
// from non zero components. // from non zero components.
// //
// Some hardware wallets switched derivation paths through their evolution, so // Some hardware wallets switched derivation paths through their evolution, so
@ -526,7 +526,6 @@ func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
// Unless we are doing 712 signing, simply dispatch to signHash // Unless we are doing 712 signing, simply dispatch to signHash
if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) { if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) {
return w.signHash(account, crypto.Keccak256(data)) return w.signHash(account, crypto.Keccak256(data))

View File

@ -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 --recursive
- 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
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%

87
beacon/engine/errors.go Normal file
View File

@ -0,0 +1,87 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package engine
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
)
// EngineAPIError is a standardized error message between consensus and execution
// clients, also containing any custom error message Geth might include.
type EngineAPIError struct {
code int
msg string
err error
}
func (e *EngineAPIError) ErrorCode() int { return e.code }
func (e *EngineAPIError) Error() string { return e.msg }
func (e *EngineAPIError) ErrorData() interface{} {
if e.err == nil {
return nil
}
return struct {
Error string `json:"err"`
}{e.err.Error()}
}
// With returns a copy of the error with a new embedded custom data field.
func (e *EngineAPIError) With(err error) *EngineAPIError {
return &EngineAPIError{
code: e.code,
msg: e.msg,
err: err,
}
}
var (
_ rpc.Error = new(EngineAPIError)
_ rpc.DataError = new(EngineAPIError)
)
var (
// VALID is returned by the engine API in the following calls:
// - newPayloadV1: if the payload was already known or was just validated and executed
// - forkchoiceUpdateV1: if the chain accepted the reorg (might ignore if it's stale)
VALID = "VALID"
// INVALID is returned by the engine API in the following calls:
// - newPayloadV1: if the payload failed to execute on top of the local chain
// - forkchoiceUpdateV1: if the new head is unknown, pre-merge, or reorg to it fails
INVALID = "INVALID"
// SYNCING is returned by the engine API in the following calls:
// - newPayloadV1: if the payload was accepted on top of an active sync
// - forkchoiceUpdateV1: if the new head was seen before, but not part of the chain
SYNCING = "SYNCING"
// ACCEPTED is returned by the engine API in the following calls:
// - newPayloadV1: if the payload was accepted, but not processed (side chain)
ACCEPTED = "ACCEPTED"
GenericServerError = &EngineAPIError{code: -32000, msg: "Server error"}
UnknownPayload = &EngineAPIError{code: -38001, msg: "Unknown payload"}
InvalidForkChoiceState = &EngineAPIError{code: -38002, msg: "Invalid forkchoice state"}
InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"}
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
INVALID_TERMINAL_BLOCK = PayloadStatusV1{Status: INVALID, LatestValidHash: &common.Hash{}}
)

View File

@ -0,0 +1,60 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package engine
import (
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
var _ = (*payloadAttributesMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
type PayloadAttributes struct {
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
var enc PayloadAttributes
enc.Timestamp = hexutil.Uint64(p.Timestamp)
enc.Random = p.Random
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
enc.Withdrawals = p.Withdrawals
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
type PayloadAttributes struct {
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Random *common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
var dec PayloadAttributes
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Timestamp == nil {
return errors.New("missing required field 'timestamp' for PayloadAttributes")
}
p.Timestamp = uint64(*dec.Timestamp)
if dec.Random == nil {
return errors.New("missing required field 'prevRandao' for PayloadAttributes")
}
p.Random = *dec.Random
if dec.SuggestedFeeRecipient == nil {
return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributes")
}
p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient
if dec.Withdrawals != nil {
p.Withdrawals = dec.Withdrawals
}
return nil
}

146
beacon/engine/gen_ed.go Normal file
View File

@ -0,0 +1,146 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package engine
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
var _ = (*executableDataMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (e ExecutableData) MarshalJSON() ([]byte, error) {
type ExecutableData struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"`
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
var enc ExecutableData
enc.ParentHash = e.ParentHash
enc.FeeRecipient = e.FeeRecipient
enc.StateRoot = e.StateRoot
enc.ReceiptsRoot = e.ReceiptsRoot
enc.LogsBloom = e.LogsBloom
enc.Random = e.Random
enc.Number = hexutil.Uint64(e.Number)
enc.GasLimit = hexutil.Uint64(e.GasLimit)
enc.GasUsed = hexutil.Uint64(e.GasUsed)
enc.Timestamp = hexutil.Uint64(e.Timestamp)
enc.ExtraData = e.ExtraData
enc.BaseFeePerGas = (*hexutil.Big)(e.BaseFeePerGas)
enc.BlockHash = e.BlockHash
if e.Transactions != nil {
enc.Transactions = make([]hexutil.Bytes, len(e.Transactions))
for k, v := range e.Transactions {
enc.Transactions[k] = v
}
}
enc.Withdrawals = e.Withdrawals
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
type ExecutableData struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random *common.Hash `json:"prevRandao" gencodec:"required"`
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
var dec ExecutableData
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.ParentHash == nil {
return errors.New("missing required field 'parentHash' for ExecutableData")
}
e.ParentHash = *dec.ParentHash
if dec.FeeRecipient == nil {
return errors.New("missing required field 'feeRecipient' for ExecutableData")
}
e.FeeRecipient = *dec.FeeRecipient
if dec.StateRoot == nil {
return errors.New("missing required field 'stateRoot' for ExecutableData")
}
e.StateRoot = *dec.StateRoot
if dec.ReceiptsRoot == nil {
return errors.New("missing required field 'receiptsRoot' for ExecutableData")
}
e.ReceiptsRoot = *dec.ReceiptsRoot
if dec.LogsBloom == nil {
return errors.New("missing required field 'logsBloom' for ExecutableData")
}
e.LogsBloom = *dec.LogsBloom
if dec.Random == nil {
return errors.New("missing required field 'prevRandao' for ExecutableData")
}
e.Random = *dec.Random
if dec.Number == nil {
return errors.New("missing required field 'blockNumber' for ExecutableData")
}
e.Number = uint64(*dec.Number)
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for ExecutableData")
}
e.GasLimit = uint64(*dec.GasLimit)
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for ExecutableData")
}
e.GasUsed = uint64(*dec.GasUsed)
if dec.Timestamp == nil {
return errors.New("missing required field 'timestamp' for ExecutableData")
}
e.Timestamp = uint64(*dec.Timestamp)
if dec.ExtraData == nil {
return errors.New("missing required field 'extraData' for ExecutableData")
}
e.ExtraData = *dec.ExtraData
if dec.BaseFeePerGas == nil {
return errors.New("missing required field 'baseFeePerGas' for ExecutableData")
}
e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
if dec.BlockHash == nil {
return errors.New("missing required field 'blockHash' for ExecutableData")
}
e.BlockHash = *dec.BlockHash
if dec.Transactions == nil {
return errors.New("missing required field 'transactions' for ExecutableData")
}
e.Transactions = make([][]byte, len(dec.Transactions))
for k, v := range dec.Transactions {
e.Transactions[k] = v
}
if dec.Withdrawals != nil {
e.Withdrawals = dec.Withdrawals
}
return nil
}

46
beacon/engine/gen_epe.go Normal file
View File

@ -0,0 +1,46 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package engine
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common/hexutil"
)
var _ = (*executionPayloadEnvelopeMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
type ExecutionPayloadEnvelope struct {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
}
var enc ExecutionPayloadEnvelope
enc.ExecutionPayload = e.ExecutionPayload
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
type ExecutionPayloadEnvelope struct {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
}
var dec ExecutionPayloadEnvelope
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.ExecutionPayload == nil {
return errors.New("missing required field 'executionPayload' for ExecutionPayloadEnvelope")
}
e.ExecutionPayload = dec.ExecutionPayload
if dec.BlockValue == nil {
return errors.New("missing required field 'blockValue' for ExecutionPayloadEnvelope")
}
e.BlockValue = (*big.Int)(dec.BlockValue)
return nil
}

237
beacon/engine/types.go Normal file
View File

@ -0,0 +1,237 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package engine
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie"
)
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
// PayloadAttributes describes the environment context in which a block should
// be built.
type PayloadAttributes struct {
Timestamp uint64 `json:"timestamp" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
// JSON type overrides for PayloadAttributes.
type payloadAttributesMarshaling struct {
Timestamp hexutil.Uint64
}
//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go
// ExecutableData is the data necessary to execute an EL payload.
type ExecutableData struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"`
Number uint64 `json:"blockNumber" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Timestamp uint64 `json:"timestamp" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions [][]byte `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
// JSON type overrides for executableData.
type executableDataMarshaling struct {
Number hexutil.Uint64
GasLimit hexutil.Uint64
GasUsed hexutil.Uint64
Timestamp hexutil.Uint64
BaseFeePerGas *hexutil.Big
ExtraData hexutil.Bytes
LogsBloom hexutil.Bytes
Transactions []hexutil.Bytes
}
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
type ExecutionPayloadEnvelope struct {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
}
// JSON type overrides for ExecutionPayloadEnvelope.
type executionPayloadEnvelopeMarshaling struct {
BlockValue *hexutil.Big
}
type PayloadStatusV1 struct {
Status string `json:"status"`
LatestValidHash *common.Hash `json:"latestValidHash"`
ValidationError *string `json:"validationError"`
}
type TransitionConfigurationV1 struct {
TerminalTotalDifficulty *hexutil.Big `json:"terminalTotalDifficulty"`
TerminalBlockHash common.Hash `json:"terminalBlockHash"`
TerminalBlockNumber hexutil.Uint64 `json:"terminalBlockNumber"`
}
// PayloadID is an identifier of the payload build process
type PayloadID [8]byte
func (b PayloadID) String() string {
return hexutil.Encode(b[:])
}
func (b PayloadID) MarshalText() ([]byte, error) {
return hexutil.Bytes(b[:]).MarshalText()
}
func (b *PayloadID) UnmarshalText(input []byte) error {
err := hexutil.UnmarshalFixedText("PayloadID", input, b[:])
if err != nil {
return fmt.Errorf("invalid payload id %q: %w", input, err)
}
return nil
}
type ForkChoiceResponse struct {
PayloadStatus PayloadStatusV1 `json:"payloadStatus"`
PayloadID *PayloadID `json:"payloadId"`
}
type ForkchoiceStateV1 struct {
HeadBlockHash common.Hash `json:"headBlockHash"`
SafeBlockHash common.Hash `json:"safeBlockHash"`
FinalizedBlockHash common.Hash `json:"finalizedBlockHash"`
}
func encodeTransactions(txs []*types.Transaction) [][]byte {
var enc = make([][]byte, len(txs))
for i, tx := range txs {
enc[i], _ = tx.MarshalBinary()
}
return enc
}
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
var txs = make([]*types.Transaction, len(enc))
for i, encTx := range enc {
var tx types.Transaction
if err := tx.UnmarshalBinary(encTx); err != nil {
return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
}
txs[i] = &tx
}
return txs, nil
}
// ExecutableDataToBlock constructs a block from executable data.
// It verifies that the following fields:
//
// len(extraData) <= 32
// uncleHash = emptyUncleHash
// difficulty = 0
//
// and that the blockhash of the constructed block matches the parameters. Nil
// Withdrawals value will propagate through the returned block. Empty
// Withdrawals value must be passed via non-nil, length 0 value in params.
func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
txs, err := decodeTransactions(params.Transactions)
if err != nil {
return nil, err
}
if len(params.ExtraData) > 32 {
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
}
if len(params.LogsBloom) != 256 {
return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom))
}
// Check that baseFeePerGas is not negative or too big
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
}
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
// ExecutableData before withdrawals are enabled by marshaling
// Withdrawals as the json null value.
var withdrawalsRoot *common.Hash
if params.Withdrawals != nil {
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
withdrawalsRoot = &h
}
header := &types.Header{
ParentHash: params.ParentHash,
UncleHash: types.EmptyUncleHash,
Coinbase: params.FeeRecipient,
Root: params.StateRoot,
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: params.ReceiptsRoot,
Bloom: types.BytesToBloom(params.LogsBloom),
Difficulty: common.Big0,
Number: new(big.Int).SetUint64(params.Number),
GasLimit: params.GasLimit,
GasUsed: params.GasUsed,
Time: params.Timestamp,
BaseFee: params.BaseFeePerGas,
Extra: params.ExtraData,
MixDigest: params.Random,
WithdrawalsHash: withdrawalsRoot,
}
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
if block.Hash() != params.BlockHash {
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
}
return block, nil
}
// BlockToExecutableData constructs the ExecutableData structure by filling the
// fields from the given block. It assumes the given block is post-merge block.
func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope {
data := &ExecutableData{
BlockHash: block.Hash(),
ParentHash: block.ParentHash(),
FeeRecipient: block.Coinbase(),
StateRoot: block.Root(),
Number: block.NumberU64(),
GasLimit: block.GasLimit(),
GasUsed: block.GasUsed(),
BaseFeePerGas: block.BaseFee(),
Timestamp: block.Time(),
ReceiptsRoot: block.ReceiptHash(),
LogsBloom: block.Bloom().Bytes(),
Transactions: encodeTransactions(block.Transactions()),
Random: block.MixDigest(),
ExtraData: block.Extra(),
Withdrawals: block.Withdrawals(),
}
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees}
}
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
type ExecutionPayloadBodyV1 struct {
TransactionData []hexutil.Bytes `json:"transactions"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}

22
build/bot/macos-build.sh Normal file
View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e -x
# -- Check XCode version
xcodebuild -version
# xcrun simctl list
# -- Build for macOS and upload to Azure
go run build/ci.go install -dlgo
go run build/ci.go archive -type tar # -signer OSX_SIGNING_KEY -upload gethstore/builds
# # -- CocoaPods
# gem uninstall cocoapods -a -x
# gem install cocoapods
# mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
# sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
# git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master
# pod setup --verbose
# # -- Build for iOS and upload to Azure
# go run build/ci.go xcode -signer IOS_SIGNING_KEY -upload gethstore/builds

17
build/bot/ppa-build.sh Normal file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -e -x
# Note: this script is meant to be run in a Debian/Ubuntu docker container,
# as user 'root'.
# Install the required tools for creating source packages.
apt-get -yq --no-install-suggests --no-install-recommends install\
devscripts debhelper dput fakeroot
# Add the SSH key of ppa.launchpad.net to known_hosts.
mkdir -p ~/.ssh
echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
# Build the source package and upload.
go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"

View File

@ -1,33 +1,46 @@
# 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 e447b498cde50215c4f7619e5124b0fc4e25fb5d16ea47271c47f278e7aa763a go1.20.3.src.tar.gz
18fe94775763db3878717393b6d41371b0b45206055e49b3838328120c977d13 go1.16.4.darwin-amd64.tar.gz c1e1161d6d859deb576e6cfabeb40e3d042ceb1c6f444f617c3c9d76269c3565 go1.20.3.darwin-amd64.tar.gz
cb6b972cc42e669f3585c648198cd5b6f6d7a0811d413ad64b50c02ba06ccc3a go1.16.4.darwin-arm64.tar.gz 86b0ed0f2b2df50fa8036eea875d1cf2d76cefdacf247c44639a1464b7e36b95 go1.20.3.darwin-arm64.tar.gz
cd1b146ef6e9006f27dd99e9687773e7fef30e8c985b7d41bff33e955a3bb53a go1.16.4.linux-386.tar.gz 340e80abd047c597fdc0f50a6cc59617f06c297d62f7fc77f4a0164e2da6f7aa go1.20.3.freebsd-386.tar.gz
7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59 go1.16.4.linux-amd64.tar.gz 2169fcd8b6c94c5fbe07c0b470ccfb6001d343f6548ad49f3d9ab78e3b5753c7 go1.20.3.freebsd-amd64.tar.gz
8b18eb05ddda2652d69ab1b1dd1f40dd731799f43c6a58b512ad01ae5b5bba21 go1.16.4.linux-arm64.tar.gz e12384311403f1389d14cc1c1295bfb4e0dd5ab919403b80da429f671a223507 go1.20.3.linux-386.tar.gz
a53391a800ddec749ee90d38992babb27b95cfb864027350c737b9aa8e069494 go1.16.4.linux-armv6l.tar.gz 979694c2c25c735755bf26f4f45e19e64e4811d661dd07b8c010f7a8e18adfca go1.20.3.linux-amd64.tar.gz
e75c0b114a09eb5499874162b208931dc260de0fedaeedac8621bf263c974605 go1.16.4.windows-386.zip eb186529f13f901e7a2c4438a05c2cd90d74706aaa0a888469b2a4a617b6ee54 go1.20.3.linux-arm64.tar.gz
d40139b7ade8a3008e3240a6f86fe8f899a9c465c917e11dac8758af216f5eb0 go1.16.4.windows-amd64.zip b421e90469a83671641f81b6e20df6500f033e9523e89cbe7b7223704dd1035c go1.20.3.linux-armv6l.tar.gz
7cf2bc8a175d6d656861165bfc554f92dc78d2abf5afe5631db3579555d97409 go1.16.4.freebsd-386.tar.gz 943c89aa1624ea544a022b31e3d6e16a037200e436370bdd5fd67f3fa60be282 go1.20.3.linux-ppc64le.tar.gz
ccdd2b76de1941b60734408fda0d750aaa69330d8a07430eed4c56bdb3502f6f go1.16.4.freebsd-amd64.tar.gz 126cf823a5634ef2544b866db107b9d351d3ea70d9e240b0bdcfb46f4dcae54b go1.20.3.linux-s390x.tar.gz
80cfac566e344096a8df8f37bbd21f89e76a6fbe601406565d71a87a665fc125 go1.16.4.linux-ppc64le.tar.gz 37e9146e1f9d681cfcaa6fee6c7b890c44c64bc50228c9588f3c4231346d33bd go1.20.3.windows-386.zip
d6431881b3573dc29ecc24fbeab5e5ec25d8c9273aa543769c86a1a3bbac1ddf go1.16.4.linux-s390x.tar.gz 143a2837821c7dbacf7744cbb1a8421c1f48307c6fdfaeffc5f8c2f69e1b7932 go1.20.3.windows-amd64.zip
158cb159e00bc979f473e0f5b5a561613129c5e51067967b72b8e072e5a4db81 go1.20.3.windows-arm64.zip
7e9a47ab540aa3e8472fbf8120d28bed3b9d9cf625b955818e8bc69628d7187c golangci-lint-1.39.0-darwin-amd64.tar.gz fba08acc4027f69f07cef48fbff70b8a7ecdfaa1c2aba9ad3fb31d60d9f5d4bc golangci-lint-1.51.1-darwin-amd64.tar.gz
574daa2c9c299b01672a6daeb1873b5f12e413cdb6dc0e30f2ff163956778064 golangci-lint-1.39.0-darwin-arm64.tar.gz 75b8f0ff3a4e68147156be4161a49d4576f1be37a0b506473f8c482140c1e7f2 golangci-lint-1.51.1-darwin-arm64.tar.gz
6225f7014987324ab78e9b511f294e3f25be013728283c33918c67c8576d543e golangci-lint-1.39.0-freebsd-386.tar.gz e06b3459aaed356e1667580be00b05f41f3b2e29685d12cdee571c23e1edb414 golangci-lint-1.51.1-freebsd-386.tar.gz
6b3e76e1e5eaf0159411c8e2727f8d533989d3bb19f10e9caa6e0b9619ee267d golangci-lint-1.39.0-freebsd-amd64.tar.gz 623ce2d0fa4d35cc2e8d69fa7334227ab592380962a13b4d9cdc77cf41db2008 golangci-lint-1.51.1-freebsd-amd64.tar.gz
a301cacfff87ed9b00313d95278533c25a4527a06b040a17d969b4b7e1b8a90d golangci-lint-1.39.0-freebsd-armv7.tar.gz 131365feb0584cc2736c43192fa673ca50e5b6b765456990cb379ecfb787e568 golangci-lint-1.51.1-freebsd-armv6.tar.gz
25bfd96a29c3112f508d5e4fc860dbad7afce657233c343acfa20715717d51e7 golangci-lint-1.39.0-freebsd-armv6.tar.gz 98fb627927cbb654f5bf85dcffc5f646666b2ce96ea0fed977c9fb28abd51532 golangci-lint-1.51.1-freebsd-armv7.tar.gz
9687e4ff15545cfc722b0e46107a94195166a505023b48a316579af25ad09505 golangci-lint-1.39.0-linux-armv7.tar.gz b36a99702fa762c15840261bc0fb41b4b1b16b8b19b8c0941bae98c85bb0f8b8 golangci-lint-1.51.1-linux-386.tar.gz
a7fa7ab2bfc99cbe5e5bcbf5684f5a997f920afbbe2f253d2feb1001d5e3c8b3 golangci-lint-1.39.0-linux-armv6.tar.gz 17aeb26c76820c22efa0e1838b0ab93e90cfedef43fbfc9a2f33f27eb9e5e070 golangci-lint-1.51.1-linux-amd64.tar.gz
c8f9634115beddb4ed9129c1f7ecd4c97c99d07aeef33e3707234097eeb51b7b golangci-lint-1.39.0-linux-mips64le.tar.gz 9744bc34e7b8d82ca788b667bfb7155a39b4be9aef43bf9f10318b1372cea338 golangci-lint-1.51.1-linux-arm64.tar.gz
d1234c213b74751f1af413302dde0e9a6d4d29aecef034af7abb07dc1b6e887f golangci-lint-1.39.0-linux-arm64.tar.gz 0dda8dbeb2ff7455a044ec8e347f2fc6d655d2e99d281b3b95e88167031c673d golangci-lint-1.51.1-linux-armv6.tar.gz
df25d9267168323b163147acb823ab0215a8a3bb6898a4a9320afdfedde66817 golangci-lint-1.39.0-linux-386.tar.gz 0512f311b11d43b8b22989d929f0fe8a2e1e5ebe497f1eb0ff73a0fc3d188fd1 golangci-lint-1.51.1-linux-armv7.tar.gz
1767e75fba357b7651b1a796d38453558f371c60af805505ec99e166908c04b5 golangci-lint-1.39.0-linux-ppc64le.tar.gz d767108dcf84a8eaa844df3454cb0f75a492f4e7102ecc2b0a3545cfe073a566 golangci-lint-1.51.1-linux-loong64.tar.gz
25fd75bf3186b3d930ecae10185689968fd18fd8fa6f9f555d6beb04348c20f6 golangci-lint-1.39.0-linux-s390x.tar.gz 3bd56c54daec16585b2668e0dfabb27af2c2b38cc0fdb46923e2521e1634846b golangci-lint-1.51.1-linux-mips64.tar.gz
3a73aa7468087caa62673c8adea99b4e4dff846dc72707222db85f8679b40cbf golangci-lint-1.39.0-linux-amd64.tar.gz f72f5adfa2219e15d2414c9a2966f86e74556cf17a85c727a7fb7770a16cf814 golangci-lint-1.51.1-linux-mips64le.tar.gz
578caceccf81739bda67dbfec52816709d03608c6878888ecdc0e186a094a41b golangci-lint-1.39.0-linux-mips64.tar.gz e605521dac98096d8737e1997c954f41f1d0d8275b8731f62783d410c23574b9 golangci-lint-1.51.1-linux-ppc64le.tar.gz
494b66ba0e32c8ddf6c4f6b1d05729b110900f6017eda943057e43598c17d7a8 golangci-lint-1.39.0-windows-386.zip 2f683217b814339e74d61ca700922d8407f15addd6d4c5e8b156fbab79f26a87 golangci-lint-1.51.1-linux-riscv64.tar.gz
52ec2e13a3cbb47147244dff8cfc35103563deb76e0459133058086fc35fb2c7 golangci-lint-1.39.0-windows-amd64.zip d98528292b65971a3594e5880530e7624597dc9806fcfccdfbe39be411713d63 golangci-lint-1.51.1-linux-s390x.tar.gz
9bb2d0fe9e692ed0aea4f2537e3e6862b2f6768fe2849a84f4a6ad09da9fd971 golangci-lint-1.51.1-netbsd-386.tar.gz
34cafdcd11ae73ae88d66c33eb8449f5c976fc3e37b44774dbe9c71caa95e592 golangci-lint-1.51.1-netbsd-amd64.tar.gz
f8b4e1e47ac17caafe8a5f32f975a2b6a7cb14c27c0f73c1fb15c20ca91c2e03 golangci-lint-1.51.1-netbsd-armv6.tar.gz
c4f58b7e227b9fd41f0e9310dc83f4a4e7d026598e2f6e95b78761081a6d9bd2 golangci-lint-1.51.1-netbsd-armv7.tar.gz
6710e2f5375dc75521c1a17980a6cbbe6ff76c2f8b852964a8af558899a97cf5 golangci-lint-1.51.1-windows-386.zip
722d7b87b9cdda0a3835d5030b3fc5385c2eba4c107f63f6391cfb2ac35f051d golangci-lint-1.51.1-windows-amd64.zip
eb57f9bcb56646f2e3d6ccaf02ec227815fb05077b2e0b1bf9e755805acdc2b9 golangci-lint-1.51.1-windows-arm64.zip
bce02f7232723cb727755ee11f168a700a00896a25d37f87c4b173bce55596b4 golangci-lint-1.51.1-windows-armv6.zip
cf6403f84707ce8c98664736772271bc8874f2e760c2fd0f00cf3e85963507e9 golangci-lint-1.51.1-windows-armv7.zip
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767 go1.19.6.src.tar.gz

View File

@ -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
/* /*
@ -23,41 +24,36 @@ Usage: go run build/ci.go <command> <command flags/arguments>
Available commands are: Available commands are:
install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables
test [ -coverage ] [ packages... ] -- runs the tests test [ -coverage ] [ packages... ] -- runs the tests
lint -- runs certain pre-selected linters lint -- runs certain pre-selected linters
archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -signify key-envvar ] [ -upload dest ] -- archives build artifacts archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -signify key-envvar ] [ -upload dest ] -- archives build artifacts
importkeys -- imports signing keys from env importkeys -- imports signing keys from env
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
nsis -- creates a Windows NSIS installer nsis -- creates a Windows NSIS installer
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
xgo [ -alltools ] [ options ] -- cross builds according to options
purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore
For all commands, -n prevents execution of external programs (dry run mode). For all commands, -n prevents execution of external programs (dry run mode).
*/ */
package main package main
import ( import (
"bufio"
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
"github.com/cespare/cp" "github.com/cespare/cp"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/signify" "github.com/ethereum/go-ethereum/crypto/signify"
"github.com/ethereum/go-ethereum/internal/build" "github.com/ethereum/go-ethereum/internal/build"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -77,7 +73,6 @@ var (
executablePath("bootnode"), executablePath("bootnode"),
executablePath("evm"), executablePath("evm"),
executablePath("geth"), executablePath("geth"),
executablePath("puppeth"),
executablePath("rlpdump"), executablePath("rlpdump"),
executablePath("clef"), executablePath("clef"),
} }
@ -100,10 +95,6 @@ var (
BinaryName: "geth", BinaryName: "geth",
Description: "Ethereum CLI client.", Description: "Ethereum CLI client.",
}, },
{
BinaryName: "puppeth",
Description: "Ethereum private network manager.",
},
{ {
BinaryName: "rlpdump", BinaryName: "rlpdump",
Description: "Developer utility tool that prints RLP structures.", Description: "Developer utility tool that prints RLP structures.",
@ -128,20 +119,16 @@ 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, hirsuite, impish
// 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", // EOL: 04/2024
"xenial": "golang-go", "xenial": "golang-go", // EOL: 04/2026
"bionic": "golang-go", "bionic": "golang-go", // EOL: 04/2028
"focal": "golang-go", "focal": "golang-go", // EOL: 04/2030
"groovy": "golang-go", "jammy": "golang-go", // EOL: 04/2032
"hirsute": "golang-go", "kinetic": "golang-go", // EOL: 07/2023
"lunar": "golang-go", // EOL: 01/2024
} }
debGoBootPaths = map[string]string{ debGoBootPaths = map[string]string{
@ -149,10 +136,18 @@ var (
"golang-go": "/usr/lib/go", "golang-go": "/usr/lib/go",
} }
// 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.20.3"
// This is the version of Go that will be used to bootstrap the PPA builder.
//
// This version is fine to be old and full of security holes, we just use it
// to build the latest Go. Don't change it. If it ever becomes insufficient,
// we need to switch over to a recursive builder to jumpt across supported
// versions.
gobootVersion = "1.19.6"
) )
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@ -167,7 +162,7 @@ func executablePath(name string) string {
func main() { func main() {
log.SetFlags(log.Lshortfile) log.SetFlags(log.Lshortfile)
if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) { if !common.FileExist(filepath.Join("build", "ci.go")) {
log.Fatal("this script must be run from the root of the repository") log.Fatal("this script must be run from the root of the repository")
} }
if len(os.Args) < 2 { if len(os.Args) < 2 {
@ -182,16 +177,12 @@ func main() {
doLint(os.Args[2:]) doLint(os.Args[2:])
case "archive": case "archive":
doArchive(os.Args[2:]) doArchive(os.Args[2:])
case "docker":
doDocker(os.Args[2:])
case "debsrc": case "debsrc":
doDebianSource(os.Args[2:]) doDebianSource(os.Args[2:])
case "nsis": case "nsis":
doWindowsInstaller(os.Args[2:]) doWindowsInstaller(os.Args[2:])
case "aar":
doAndroidArchive(os.Args[2:])
case "xcode":
doXCodeFramework(os.Args[2:])
case "xgo":
doXgo(os.Args[2:])
case "purge": case "purge":
doPurge(os.Args[2:]) doPurge(os.Args[2:])
default: default:
@ -203,9 +194,10 @@ func main() {
func doInstall(cmdline []string) { func doInstall(cmdline []string) {
var ( var (
dlgo = flag.Bool("dlgo", false, "Download Go and build with it") dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
arch = flag.String("arch", "", "Architecture to cross build for") arch = flag.String("arch", "", "Architecture to cross build for")
cc = flag.String("cc", "", "C compiler to cross build with") cc = flag.String("cc", "", "C compiler to cross build with")
staticlink = flag.Bool("static", false, "Create statically-linked executable")
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
@ -216,9 +208,12 @@ func doInstall(cmdline []string) {
tc.Root = build.DownloadGo(csdb, dlgoVersion) tc.Root = build.DownloadGo(csdb, dlgoVersion)
} }
// Disable CLI markdown doc generation in release builds.
buildTags := []string{"urfave_cli_no_docs"}
// Configure the build. // Configure the build.
env := build.Env() env := build.Env()
gobuild := tc.Go("build", buildFlags(env)...) gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
// arm64 CI builders are memory-constrained and can't handle concurrent builds, // arm64 CI builders are memory-constrained and can't handle concurrent builds,
// better disable it. This check isn't the best, it should probably // better disable it. This check isn't the best, it should probably
@ -251,20 +246,35 @@ func doInstall(cmdline []string) {
} }
// buildFlags returns the go tool flags for building. // buildFlags returns the go tool flags for building.
func buildFlags(env build.Environment) (flags []string) { func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
var ld []string var ld []string
if env.Commit != "" { if env.Commit != "" {
ld = append(ld, "-X", "main.gitCommit="+env.Commit) ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitCommit="+env.Commit)
ld = append(ld, "-X", "main.gitDate="+env.Date) ld = append(ld, "-X", "github.com/ethereum/go-ethereum/internal/version.gitDate="+env.Date)
} }
// Strip DWARF on darwin. This used to be required for certain things, // Strip DWARF on darwin. This used to be required for certain things,
// and there is no downside to this, so we just keep doing it. // and there is no downside to this, so we just keep doing it.
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
ld = append(ld, "-s") ld = append(ld, "-s")
} }
if runtime.GOOS == "linux" {
// Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux.
extld := []string{"-Wl,-z,stack-size=0x800000"}
if staticLinking {
extld = append(extld, "-static")
// Under static linking, use of certain glibc features must be
// disabled to avoid shared library dependencies.
buildTags = append(buildTags, "osusergo", "netgo")
}
ld = append(ld, "-extldflags", "'"+strings.Join(extld, " ")+"'")
}
if len(ld) > 0 { if len(ld) > 0 {
flags = append(flags, "-ldflags", strings.Join(ld, " ")) flags = append(flags, "-ldflags", strings.Join(ld, " "))
} }
if len(buildTags) > 0 {
flags = append(flags, "-tags", strings.Join(buildTags, ","))
}
return flags return flags
} }
@ -279,6 +289,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)
@ -299,6 +310,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 {
@ -327,12 +341,21 @@ 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.51.1"
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) arch := runtime.GOARCH
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s.tar.gz", version, base) ext := ".tar.gz"
archivePath := filepath.Join(cachedir, base+".tar.gz")
if runtime.GOOS == "windows" {
ext = ".zip"
}
if arch == "arm" {
arch += "v" + os.Getenv("GOARM")
}
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext)
archivePath := filepath.Join(cachedir, base+ext)
if err := csdb.DownloadFile(url, archivePath); err != nil { if err := csdb.DownloadFile(url, archivePath); err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -442,13 +465,175 @@ func maybeSkipArchive(env build.Environment) {
log.Printf("skipping archive creation because this is a PR build") log.Printf("skipping archive creation because this is a PR build")
os.Exit(0) os.Exit(0)
} }
if env.IsCronJob { if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
log.Printf("skipping archive creation because this is a cron job") 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)
} }
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)
os.Exit(0) // Builds the docker images and optionally uploads them to Docker Hub.
func doDocker(cmdline []string) {
var (
image = flag.Bool("image", false, `Whether to build and push an arch specific docker image`)
manifest = flag.String("manifest", "", `Push a multi-arch docker image for the specified architectures (usually "amd64,arm64")`)
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
)
flag.CommandLine.Parse(cmdline)
// Skip building and pushing docker images for PR builds
env := build.Env()
maybeSkipArchive(env)
// Retrieve the upload credentials and authenticate
user := getenvBase64("DOCKER_HUB_USERNAME")
pass := getenvBase64("DOCKER_HUB_PASSWORD")
if len(user) > 0 && len(pass) > 0 {
auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
auther.Stdin = bytes.NewReader(pass)
build.MustRun(auther)
}
// Retrieve the version infos to build and push to the following paths:
// - ethereum/client-go:latest - Pushes to the master branch, Geth only
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-latest - Pushes to the master branch, Geth & tools
// - ethereum/client-go:alltools-stable - Version tag publish on GitHub, Geth & tools
// - ethereum/client-go:release-<major>.<minor> - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-release-<major>.<minor> - Version tag publish on GitHub, Geth & tools
// - ethereum/client-go:v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth only
// - ethereum/client-go:alltools-v<major>.<minor>.<patch> - Version tag publish on GitHub, Geth & tools
var tags []string
switch {
case env.Branch == "master":
tags = []string{"latest"}
case strings.HasPrefix(env.Tag, "v1."):
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
}
// If architecture specific image builds are requested, build and push them
if *image {
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
// Tag and upload the images to Docker Hub
for _, tag := range tags {
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, runtime.GOARCH)
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, runtime.GOARCH)
// If the image already exists (non version tag), check the build
// number to prevent overwriting a newer commit if concurrent builds
// are running. This is still a tiny bit racey if two published are
// done at the same time, but that's extremely unlikely even on the
// master branch.
for _, img := range []string{gethImage, toolImage} {
if exec.Command("docker", "pull", img).Run() != nil {
continue // Generally the only failure is a missing image, which is good
}
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
if err != nil {
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
}
buildnum = bytes.TrimSpace(buildnum)
if len(buildnum) > 0 && len(env.Buildnum) > 0 {
oldnum, err := strconv.Atoi(string(buildnum))
if err != nil {
log.Fatalf("Failed to parse old image build number: %v", err)
}
newnum, err := strconv.Atoi(env.Buildnum)
if err != nil {
log.Fatalf("Failed to parse current build number: %v", err)
}
if oldnum > newnum {
log.Fatalf("Current build number %d not newer than existing %d", newnum, oldnum)
} else {
log.Printf("Updating %s from build %d to %d", img, oldnum, newnum)
}
}
}
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), gethImage)
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), toolImage)
build.MustRunCommand("docker", "push", gethImage)
build.MustRunCommand("docker", "push", toolImage)
}
}
// If multi-arch image manifest push is requested, assemble it
if len(*manifest) != 0 {
// Since different architectures are pushed by different builders, wait
// until all required images are updated.
var mismatch bool
for i := 0; i < 2; i++ { // 2 attempts, second is race check
mismatch = false // hope there's no mismatch now
for _, tag := range tags {
for _, arch := range strings.Split(*manifest, ",") {
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, arch)
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, arch)
for _, img := range []string{gethImage, toolImage} {
if out, err := exec.Command("docker", "pull", img).CombinedOutput(); err != nil {
log.Printf("Required image %s unavailable: %v\nOutput: %s", img, err, out)
mismatch = true
break
}
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
if err != nil {
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
}
buildnum = bytes.TrimSpace(buildnum)
if string(buildnum) != env.Buildnum {
log.Printf("Build number mismatch on %s: want %s, have %s", img, env.Buildnum, buildnum)
mismatch = true
break
}
}
if mismatch {
break
}
}
if mismatch {
break
}
}
if mismatch {
// Build numbers mismatching, retry in a short time to
// avoid concurrent fails in both publisher images. If
// however the retry failed too, it means the concurrent
// builder is still crunching, let that do the publish.
if i == 0 {
time.Sleep(30 * time.Second)
}
continue
}
break
}
if mismatch {
log.Println("Relinquishing publish to other builder")
return
}
// Assemble and push the Geth manifest image
for _, tag := range tags {
gethImage := fmt.Sprintf("%s:%s", *upload, tag)
var gethSubImages []string
for _, arch := range strings.Split(*manifest, ",") {
gethSubImages = append(gethSubImages, gethImage+"-"+arch)
}
build.MustRunCommand("docker", append([]string{"manifest", "create", gethImage}, gethSubImages...)...)
build.MustRunCommand("docker", "manifest", "push", gethImage)
}
// Assemble and push the alltools manifest image
for _, tag := range tags {
toolImage := fmt.Sprintf("%s:alltools-%s", *upload, tag)
var toolSubImages []string
for _, arch := range strings.Split(*manifest, ",") {
toolSubImages = append(toolSubImages, toolImage+"-"+arch)
}
build.MustRunCommand("docker", append([]string{"manifest", "create", toolImage}, toolSubImages...)...)
build.MustRunCommand("docker", "manifest", "push", toolImage)
}
} }
} }
@ -474,10 +659,11 @@ func doDebianSource(cmdline []string) {
gpg.Stdin = bytes.NewReader(key) gpg.Stdin = bytes.NewReader(key)
build.MustRun(gpg) build.MustRun(gpg)
} }
// Download and verify the Go source packages.
// Download and verify the Go source package. var (
gobundle := downloadGoSources(*cachedir) gobootbundle = downloadGoBootstrapSources(*cachedir)
gobundle = downloadGoSources(*cachedir)
)
// Download all the dependencies needed to build the sources and run the ci script // Download all the dependencies needed to build the sources and run the ci script
srcdepfetch := tc.Go("mod", "download") srcdepfetch := tc.Go("mod", "download")
srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath")) srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
@ -494,12 +680,19 @@ func doDebianSource(cmdline []string) {
meta := newDebMetadata(distro, goboot, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables) meta := newDebMetadata(distro, goboot, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
pkgdir := stageDebianSource(*workdir, meta) pkgdir := stageDebianSource(*workdir, meta)
// Add Go source code // Add bootstrapper Go source code
if err := build.ExtractArchive(gobootbundle, pkgdir); err != nil {
log.Fatalf("Failed to extract bootstrapper Go sources: %v", err)
}
if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".goboot")); err != nil {
log.Fatalf("Failed to rename bootstrapper Go source folder: %v", err)
}
// Add builder Go source code
if err := build.ExtractArchive(gobundle, pkgdir); err != nil { if err := build.ExtractArchive(gobundle, pkgdir); err != nil {
log.Fatalf("Failed to extract Go sources: %v", err) log.Fatalf("Failed to extract builder Go sources: %v", err)
} }
if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".go")); err != nil { if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".go")); err != nil {
log.Fatalf("Failed to rename Go source folder: %v", err) log.Fatalf("Failed to rename builder Go source folder: %v", err)
} }
// Add all dependency modules in compressed form // Add all dependency modules in compressed form
os.MkdirAll(filepath.Join(pkgdir, ".mod", "cache"), 0755) os.MkdirAll(filepath.Join(pkgdir, ".mod", "cache"), 0755)
@ -528,6 +721,19 @@ func doDebianSource(cmdline []string) {
} }
} }
// downloadGoBootstrapSources downloads the Go source tarball that will be used
// to bootstrap the builder Go.
func downloadGoBootstrapSources(cachedir string) string {
csdb := build.MustLoadChecksums("build/checksums.txt")
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
url := "https://dl.google.com/go/" + file
dst := filepath.Join(cachedir, file)
if err := csdb.DownloadFile(url, dst); err != nil {
log.Fatal(err)
}
return dst
}
// downloadGoSources downloads the Go source tarball. // downloadGoSources downloads the Go source tarball.
func downloadGoSources(cachedir string) string { func downloadGoSources(cachedir string) string {
csdb := build.MustLoadChecksums("build/checksums.txt") csdb := build.MustLoadChecksums("build/checksums.txt")
@ -553,8 +759,8 @@ func ppaUpload(workdir, ppa, sshUser string, files []string) {
var idfile string var idfile string
if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 { if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 {
idfile = filepath.Join(workdir, "sshkey") idfile = filepath.Join(workdir, "sshkey")
if _, err := os.Stat(idfile); os.IsNotExist(err) { if !common.FileExist(idfile) {
ioutil.WriteFile(idfile, sshkey, 0600) os.WriteFile(idfile, sshkey, 0600)
} }
} }
// Upload // Upload
@ -577,7 +783,7 @@ func makeWorkdir(wdflag string) string {
if wdflag != "" { if wdflag != "" {
err = os.MkdirAll(wdflag, 0744) err = os.MkdirAll(wdflag, 0744)
} else { } else {
wdflag, err = ioutil.TempDir("", "geth-build-") wdflag, err = os.MkdirTemp("", "geth-build-")
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -775,10 +981,10 @@ func doWindowsInstaller(cmdline []string) {
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil) build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil) build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil { if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil {
log.Fatal("Failed to copy SimpleFC.dll: %v", err) log.Fatalf("Failed to copy SimpleFC.dll: %v", err)
} }
if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil { if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil {
log.Fatal("Failed to copy copyright note: %v", err) log.Fatalf("Failed to copy copyright note: %v", err)
} }
// Build the installer. This assumes that all the needed files have been previously // Build the installer. This assumes that all the needed files have been previously
// built (don't mix building and packaging to keep cross compilation complexity to a // built (don't mix building and packaging to keep cross compilation complexity to a
@ -787,7 +993,10 @@ func doWindowsInstaller(cmdline []string) {
if env.Commit != "" { if env.Commit != "" {
version[2] += "-" + env.Commit[:8] version[2] += "-" + env.Commit[:8]
} }
installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe") installer, err := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe")
if err != nil {
log.Fatalf("Failed to convert installer file path: %v", err)
}
build.MustRunCommand("makensis.exe", build.MustRunCommand("makensis.exe",
"/DOUTPUTFILE="+installer, "/DOUTPUTFILE="+installer,
"/DMAJORVERSION="+version[0], "/DMAJORVERSION="+version[0],
@ -802,282 +1011,6 @@ func doWindowsInstaller(cmdline []string) {
} }
} }
// Android archives
func doAndroidArchive(cmdline []string) {
var (
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. ANDROID_SIGNIFY_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
)
flag.CommandLine.Parse(cmdline)
env := build.Env()
tc := new(build.GoToolchain)
// Sanity check that the SDK and NDK are installed and set
if os.Getenv("ANDROID_HOME") == "" {
log.Fatal("Please ensure ANDROID_HOME points to your Android SDK")
}
// Build gomobile.
install := tc.Install(GOBIN, "golang.org/x/mobile/cmd/gomobile@latest", "golang.org/x/mobile/cmd/gobind@latest")
install.Env = append(install.Env)
build.MustRun(install)
// Ensure all dependencies are available. This is required to make
// gomobile bind work because it expects go.sum to contain all checksums.
build.MustRun(tc.Go("mod", "download"))
// Build the Android archive and Maven resources
build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
if *local {
// If we're building locally, copy bundle to build dir and skip Maven
os.Rename("geth.aar", filepath.Join(GOBIN, "geth.aar"))
os.Rename("geth-sources.jar", filepath.Join(GOBIN, "geth-sources.jar"))
return
}
meta := newMavenMetadata(env)
build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
// Skip Maven deploy and Azure upload for PR builds
maybeSkipArchive(env)
// Sign and upload the archive to Azure
archive := "geth-" + archiveBasename("android", params.ArchiveVersion(env.Commit)) + ".aar"
os.Rename("geth.aar", archive)
if err := archiveUpload(archive, *upload, *signer, *signify); err != nil {
log.Fatal(err)
}
// Sign and upload all the artifacts to Maven Central
os.Rename(archive, meta.Package+".aar")
if *signer != "" && *deploy != "" {
// Import the signing key into the local GPG instance
key := getenvBase64(*signer)
gpg := exec.Command("gpg", "--import")
gpg.Stdin = bytes.NewReader(key)
build.MustRun(gpg)
keyID, err := build.PGPKeyID(string(key))
if err != nil {
log.Fatal(err)
}
// Upload the artifacts to Sonatype and/or Maven Central
repo := *deploy + "/service/local/staging/deploy/maven2"
if meta.Develop {
repo = *deploy + "/content/repositories/snapshots"
}
build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", "-e", "-X",
"-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
"-Dgpg.keyname="+keyID,
"-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar")
}
}
func gomobileTool(subcmd string, args ...string) *exec.Cmd {
cmd := exec.Command(filepath.Join(GOBIN, "gomobile"), subcmd)
cmd.Args = append(cmd.Args, args...)
cmd.Env = []string{
"PATH=" + GOBIN + string(os.PathListSeparator) + os.Getenv("PATH"),
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "PATH=") || strings.HasPrefix(e, "GOBIN=") {
continue
}
cmd.Env = append(cmd.Env, e)
}
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
return cmd
}
type mavenMetadata struct {
Version string
Package string
Develop bool
Contributors []mavenContributor
}
type mavenContributor struct {
Name string
Email string
}
func newMavenMetadata(env build.Environment) mavenMetadata {
// Collect the list of authors from the repo root
contribs := []mavenContributor{}
if authors, err := os.Open("AUTHORS"); err == nil {
defer authors.Close()
scanner := bufio.NewScanner(authors)
for scanner.Scan() {
// Skip any whitespace from the authors list
line := strings.TrimSpace(scanner.Text())
if line == "" || line[0] == '#' {
continue
}
// Split the author and insert as a contributor
re := regexp.MustCompile("([^<]+) <(.+)>")
parts := re.FindStringSubmatch(line)
if len(parts) == 3 {
contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]})
}
}
}
// Render the version and package strings
version := params.Version
if isUnstableBuild(env) {
version += "-SNAPSHOT"
}
return mavenMetadata{
Version: version,
Package: "geth-" + version,
Develop: isUnstableBuild(env),
Contributors: contribs,
}
}
// XCode frameworks
func doXCodeFramework(cmdline []string) {
var (
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. IOS_SIGNIFY_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
)
flag.CommandLine.Parse(cmdline)
env := build.Env()
tc := new(build.GoToolchain)
// Build gomobile.
build.MustRun(tc.Install(GOBIN, "golang.org/x/mobile/cmd/gomobile@latest", "golang.org/x/mobile/cmd/gobind@latest"))
// Ensure all dependencies are available. This is required to make
// gomobile bind work because it expects go.sum to contain all checksums.
build.MustRun(tc.Go("mod", "download"))
// Build the iOS XCode framework
bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
if *local {
// If we're building locally, use the build folder and stop afterwards
bind.Dir = GOBIN
build.MustRun(bind)
return
}
// Create the archive.
maybeSkipArchive(env)
archive := "geth-" + archiveBasename("ios", params.ArchiveVersion(env.Commit))
if err := os.MkdirAll(archive, 0755); err != nil {
log.Fatal(err)
}
bind.Dir, _ = filepath.Abs(archive)
build.MustRun(bind)
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
// Sign and upload the framework to Azure
if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil {
log.Fatal(err)
}
// Prepare and upload a PodSpec to CocoaPods
if *deploy != "" {
meta := newPodMetadata(env, archive)
build.Render("build/pod.podspec", "Geth.podspec", 0755, meta)
build.MustRunCommand("pod", *deploy, "push", "Geth.podspec", "--allow-warnings")
}
}
type podMetadata struct {
Version string
Commit string
Archive string
Contributors []podContributor
}
type podContributor struct {
Name string
Email string
}
func newPodMetadata(env build.Environment, archive string) podMetadata {
// Collect the list of authors from the repo root
contribs := []podContributor{}
if authors, err := os.Open("AUTHORS"); err == nil {
defer authors.Close()
scanner := bufio.NewScanner(authors)
for scanner.Scan() {
// Skip any whitespace from the authors list
line := strings.TrimSpace(scanner.Text())
if line == "" || line[0] == '#' {
continue
}
// Split the author and insert as a contributor
re := regexp.MustCompile("([^<]+) <(.+)>")
parts := re.FindStringSubmatch(line)
if len(parts) == 3 {
contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]})
}
}
}
version := params.Version
if isUnstableBuild(env) {
version += "-unstable." + env.Buildnum
}
return podMetadata{
Archive: archive,
Version: version,
Commit: env.Commit,
Contributors: contribs,
}
}
// Cross compilation
func doXgo(cmdline []string) {
var (
alltools = flag.Bool("alltools", false, `Flag whether we're building all known tools, or only on in particular`)
)
flag.CommandLine.Parse(cmdline)
env := build.Env()
var tc build.GoToolchain
// Make sure xgo is available for cross compilation
build.MustRun(tc.Install(GOBIN, "github.com/karalabe/xgo@latest"))
// If all tools building is requested, build everything the builder wants
args := append(buildFlags(env), flag.Args()...)
if *alltools {
args = append(args, []string{"--dest", GOBIN}...)
for _, res := range allToolsArchiveFiles {
if strings.HasPrefix(res, GOBIN) {
// Binary tool found, cross build it explicitly
args = append(args, "./"+filepath.Join("cmd", filepath.Base(res)))
build.MustRun(xgoTool(args))
args = args[:len(args)-1]
}
}
return
}
// Otherwise execute the explicit cross compilation
path := args[len(args)-1]
args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...)
build.MustRun(xgoTool(args))
}
func xgoTool(args []string) *exec.Cmd {
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, []string{"GOBIN=" + GOBIN}...)
return cmd
}
// Binary distribution cleanups // Binary distribution cleanups
func doPurge(cmdline []string) { func doPurge(cmdline []string) {
@ -1105,21 +1038,21 @@ func doPurge(cmdline []string) {
// Iterate over the blobs, collect and sort all unstable builds // Iterate over the blobs, collect and sort all unstable builds
for i := 0; i < len(blobs); i++ { for i := 0; i < len(blobs); i++ {
if !strings.Contains(blobs[i].Name, "unstable") { if !strings.Contains(*blobs[i].Name, "unstable") {
blobs = append(blobs[:i], blobs[i+1:]...) blobs = append(blobs[:i], blobs[i+1:]...)
i-- i--
} }
} }
for i := 0; i < len(blobs); i++ { for i := 0; i < len(blobs); i++ {
for j := i + 1; j < len(blobs); j++ { for j := i + 1; j < len(blobs); j++ {
if blobs[i].Properties.LastModified.After(blobs[j].Properties.LastModified) { if blobs[i].Properties.LastModified.After(*blobs[j].Properties.LastModified) {
blobs[i], blobs[j] = blobs[j], blobs[i] blobs[i], blobs[j] = blobs[j], blobs[i]
} }
} }
} }
// Filter out all archives more recent that the given threshold // Filter out all archives more recent that the given threshold
for i, blob := range blobs { for i, blob := range blobs {
if time.Since(blob.Properties.LastModified) < time.Duration(*limit)*24*time.Hour { if time.Since(*blob.Properties.LastModified) < time.Duration(*limit)*24*time.Hour {
blobs = blobs[:i] blobs = blobs[:i]
break break
} }

View File

@ -0,0 +1,16 @@
_geth_bash_autocomplete() {
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
local cur opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == "-"* ]]; then
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
else
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -o bashdefault -o default -o nospace -F _geth_bash_autocomplete geth

View File

@ -0,0 +1,18 @@
_geth_zsh_autocomplete() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
}
compdef _geth_zsh_autocomplete geth

View File

@ -5,7 +5,7 @@ Maintainer: {{.Author}}
Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}} Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}}
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Homepage: https://ethereum.org Homepage: https://ethereum.org
Vcs-Git: git://github.com/ethereum/go-ethereum.git Vcs-Git: https://github.com/ethereum/go-ethereum.git
Vcs-Browser: https://github.com/ethereum/go-ethereum Vcs-Browser: https://github.com/ethereum/go-ethereum
Package: {{.Name}} Package: {{.Name}}

View File

@ -1 +1,5 @@
build/bin/{{.BinaryName}} usr/bin build/bin/{{.BinaryName}} usr/bin
{{- if eq .BinaryName "geth" }}
build/deb/ethereum/completions/bash/geth etc/bash_completion.d
build/deb/ethereum/completions/zsh/_geth usr/share/zsh/vendor-completions
{{end -}}

View File

@ -16,7 +16,11 @@ override_dh_auto_build:
# We can't download a fresh Go within Launchpad, so we're shipping and building # We can't download a fresh Go within Launchpad, so we're shipping and building
# one on the fly. However, we can't build it inside the go-ethereum folder as # one on the fly. However, we can't build it inside the go-ethereum folder as
# bootstrapping clashes with go modules, so build in a sibling folder. # bootstrapping clashes with go modules, so build in a sibling folder.
(mv .go ../ && cd ../.go/src && ./make.bash) #
# We're also shipping the bootstrapper as of Go 1.20 as it had minimum version
# requirements opposed to older versions of Go.
(mv .goboot ../ && cd ../.goboot/src && ./make.bash)
(mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot ./make.bash)
# We can't download external go modules within Launchpad, so we're shipping the # We can't download external go modules within Launchpad, so we're shipping the
# entire dependency source cache with go-ethereum. # entire dependency source cache with go-ethereum.

View File

@ -1,57 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.ethereum</groupId>
<artifactId>geth</artifactId>
<version>{{.Version}}</version>
<packaging>aar</packaging>
<name>Android Ethereum Client</name>
<description>Android port of the go-ethereum libraries and node</description>
<url>https://github.com/ethereum/go-ethereum</url>
<inceptionYear>2015</inceptionYear>
<licenses>
<license>
<name>GNU Lesser General Public License, Version 3.0</name>
<url>https://www.gnu.org/licenses/lgpl-3.0.en.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<organization>
<name>Ethereum</name>
<url>https://ethereum.org</url>
</organization>
<developers>
<developer>
<id>karalabe</id>
<name>Péter Szilágyi</name>
<email>peterke@gmail.com</email>
<url>https://github.com/karalabe</url>
<properties>
<picUrl>https://www.gravatar.com/avatar/2ecbf0f5b4b79eebf8c193e5d324357f?s=256</picUrl>
</properties>
</developer>
</developers>
<contributors>{{range .Contributors}}
<contributor>
<name>{{.Name}}</name>
<email>{{.Email}}</email>
</contributor>{{end}}
</contributors>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/ethereum/go-ethereum/issues/</url>
</issueManagement>
<scm>
<url>https://github.com/ethereum/go-ethereum</url>
</scm>
</project>

View File

@ -1,24 +0,0 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>ossrh</id>
<username>${env.ANDROID_SONATYPE_USERNAME}</username>
<password>${env.ANDROID_SONATYPE_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<id>ossrh</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>gpg</gpg.executable>
<gpg.passphrase></gpg.passphrase>
</properties>
</profile>
</profiles>
</settings>

View File

@ -1,22 +0,0 @@
Pod::Spec.new do |spec|
spec.name = 'Geth'
spec.version = '{{.Version}}'
spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
spec.homepage = 'https://github.com/ethereum/go-ethereum'
spec.authors = { {{range .Contributors}}
'{{.Name}}' => '{{.Email}}',{{end}}
}
spec.summary = 'iOS Ethereum Client'
spec.source = { :git => 'https://github.com/ethereum/go-ethereum.git', :commit => '{{.Commit}}' }
spec.platform = :ios
spec.ios.deployment_target = '9.0'
spec.ios.vendored_frameworks = 'Frameworks/Geth.framework'
spec.prepare_command = <<-CMD
curl https://gethstore.blob.core.windows.net/builds/{{.Archive}}.tar.gz | tar -xvz
mkdir Frameworks
mv {{.Archive}}/Geth.framework Frameworks
rm -rf {{.Archive}}
CMD
end

View File

@ -1,4 +1,4 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2019 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
@ -14,9 +14,14 @@
// 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/>.
// +build android //go:build tools
// +build tools
package geth package tools
// clientIdentifier is a hard coded identifier to report into the network. import (
var clientIdentifier = "GethDroid" // Tool imports for go:generate.
_ "github.com/fjl/gencodec"
_ "github.com/golang/protobuf/protoc-gen-go"
_ "golang.org/x/tools/cmd/stringer"
)

View File

@ -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
/* /*
@ -39,7 +40,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@ -68,7 +68,9 @@ var (
"common/bitutil/bitutil", "common/bitutil/bitutil",
"common/prque/", "common/prque/",
"consensus/ethash/xor.go", "consensus/ethash/xor.go",
"crypto/blake2b/",
"crypto/bn256/", "crypto/bn256/",
"crypto/bls12381/",
"crypto/ecies/", "crypto/ecies/",
"graphql/graphiql.go", "graphql/graphiql.go",
"internal/jsre/deps", "internal/jsre/deps",
@ -241,7 +243,7 @@ func gitAuthors(files []string) []string {
} }
func readAuthors() []string { func readAuthors() []string {
content, err := ioutil.ReadFile("AUTHORS") content, err := os.ReadFile("AUTHORS")
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
log.Fatalln("error reading AUTHORS:", err) log.Fatalln("error reading AUTHORS:", err)
} }
@ -305,7 +307,7 @@ func writeAuthors(files []string) {
content.WriteString("\n") content.WriteString("\n")
} }
fmt.Println("writing AUTHORS") fmt.Println("writing AUTHORS")
if err := ioutil.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil { if err := os.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
} }
@ -340,7 +342,10 @@ func isGenerated(file string) bool {
} }
defer fd.Close() defer fd.Close()
buf := make([]byte, 2048) buf := make([]byte, 2048)
n, _ := fd.Read(buf) n, err := fd.Read(buf)
if err != nil {
return false
}
buf = buf[:n] buf = buf[:n]
for _, l := range bytes.Split(buf, []byte("\n")) { for _, l := range bytes.Split(buf, []byte("\n")) {
if bytes.HasPrefix(l, []byte("// Code generated")) { if bytes.HasPrefix(l, []byte("// Code generated")) {
@ -381,7 +386,7 @@ func writeLicense(info *info) {
if err != nil { if err != nil {
log.Fatalf("error stat'ing %s: %v\n", info.file, err) log.Fatalf("error stat'ing %s: %v\n", info.file, err)
} }
content, err := ioutil.ReadFile(info.file) content, err := os.ReadFile(info.file)
if err != nil { if err != nil {
log.Fatalf("error reading %s: %v\n", info.file, err) log.Fatalf("error reading %s: %v\n", info.file, err)
} }
@ -400,7 +405,7 @@ func writeLicense(info *info) {
return return
} }
fmt.Println("writing", info.ShortLicense(), info.file) fmt.Println("writing", info.ShortLicense(), info.file)
if err := ioutil.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil { if err := os.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil {
log.Fatalf("error writing %s: %v", info.file, err) log.Fatalf("error writing %s: %v", info.file, err)
} }
} }

View File

@ -1,18 +1,18 @@
// Copyright 2019 The go-ethereum Authors // Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of go-ethereum.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// The go-ethereum library is distributed in the hope that it will be useful, // go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main package main
@ -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)

Some files were not shown because too many files have changed in this diff Show More