mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
Merge pull request #603 from maticnetwork/arpit/v0.3.1-mumbai-candidate
v0.3.1 mumbai candidate
This commit is contained in:
commit
b7f37c7ce3
203 changed files with 8727 additions and 1745 deletions
23
.github/CODEOWNERS
vendored
23
.github/CODEOWNERS
vendored
|
|
@ -1,23 +0,0 @@
|
||||||
# Lines starting with '#' are comments.
|
|
||||||
# Each line is a file pattern followed by one or more owners.
|
|
||||||
|
|
||||||
accounts/usbwallet @karalabe
|
|
||||||
accounts/scwallet @gballet
|
|
||||||
accounts/abi @gballet @MariusVanDerWijden
|
|
||||||
cmd/clef @holiman
|
|
||||||
cmd/puppeth @karalabe
|
|
||||||
consensus @karalabe
|
|
||||||
core/ @karalabe @holiman @rjl493456442
|
|
||||||
eth/ @karalabe @holiman @rjl493456442
|
|
||||||
eth/catalyst/ @gballet
|
|
||||||
graphql/ @gballet
|
|
||||||
les/ @zsfelfoldi @rjl493456442
|
|
||||||
light/ @zsfelfoldi @rjl493456442
|
|
||||||
mobile/ @karalabe @ligi
|
|
||||||
node/ @fjl @renaynay
|
|
||||||
p2p/ @fjl @zsfelfoldi
|
|
||||||
rpc/ @fjl @holiman
|
|
||||||
p2p/simulations @fjl
|
|
||||||
p2p/protocols @fjl
|
|
||||||
p2p/testing @fjl
|
|
||||||
signer/ @holiman
|
|
||||||
67
.github/ISSUE_TEMPLATE/bug.md
vendored
67
.github/ISSUE_TEMPLATE/bug.md
vendored
|
|
@ -1,30 +1,71 @@
|
||||||
---
|
---
|
||||||
name: Report a bug
|
name: Report a bug
|
||||||
about: Something with go-ethereum is not working as expected
|
about: Something with bor client is not working as expected
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'type:bug'
|
labels: 'type:bug'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
#### System information
|
Our support team has aggregated some common issues and their solutions from past which are faced while running or interacting with a bor client. In order to prevent redundant efforts, we would encourage you to have a look at the [FAQ's section](https://docs.polygon.technology/docs/faq/technical-faqs) of our documentation mentioning the same, before filing an issue here. In case of additional support, you can also join our [discord](https://discord.com/invite/zdwkdvMNY2) server
|
||||||
|
|
||||||
Geth version: `geth version`
|
<!--
|
||||||
OS & Version: Windows/Linux/OSX
|
NOTE: Please make sure to check of any addresses / private keys / rpc url's / IP's before sharing the logs or anything from the additional information section (start.sh or heimdall config).
|
||||||
Commit hash : (if `develop`)
|
-->
|
||||||
|
|
||||||
#### Expected behaviour
|
#### **System information**
|
||||||
|
|
||||||
|
Bor client version: [e.g. v0.2.16] <!--Can be found by running the command `bor version`-->
|
||||||
|
|
||||||
#### Actual behaviour
|
Heimdall client version: [e.g. v0.2.10] <!--Can be found by running the command `heimdalld version`-->
|
||||||
|
|
||||||
|
OS & Version: Windows / Linux / OSX
|
||||||
|
|
||||||
#### Steps to reproduce the behaviour
|
Environment: Polygon Mainnet / Polygon Mumbai / Devnet
|
||||||
|
|
||||||
|
Type of node: Validator / Sentry / Archive
|
||||||
|
|
||||||
#### Backtrace
|
Additional Information: <!--Modifications in the client (if any)-->
|
||||||
|
|
||||||
````
|
#### **Overview of the problem**
|
||||||
[backtrace]
|
|
||||||
````
|
|
||||||
|
|
||||||
When submitting logs: please submit them as text and not screenshots.
|
Please describe the issue you experiencing.
|
||||||
|
<!--
|
||||||
|
Mention in detail about the issue. Also mention the actual and expected behaviour.
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### **Reproduction Steps**
|
||||||
|
|
||||||
|
Please mention the steps required to reproduce this issue.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
E.g.
|
||||||
|
1. Start bor using these flags.
|
||||||
|
2. Node is unable to connect with other peers in the network and keeps disconnecting.
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### **Logs / Traces / Output / Error Messages**
|
||||||
|
|
||||||
|
Please post any logs/traces/output/error messages (as text and not screenshots) which you believe may have caused the issue. If the log is longer than a few dozen lines, please include the URL to the [gist](https://gist.github.com/) of the log instead of posting it in the issue.
|
||||||
|
|
||||||
|
**Additional Information**
|
||||||
|
|
||||||
|
In order to debug the issue faster, we would stongly encourage if you can provide some of the details mentioned below (whichever seems relevant to your issue)
|
||||||
|
|
||||||
|
1. Your `start.sh` file or `bor.service`, if you're facing some peering issue or unable to use some service (like `http` endpoint) as expected. Moreover, if possible mention the chain configuration printed while starting the node which looks something like `Initialised chain configuration config="{ChainID: 137, ..., Engine: bor}"`
|
||||||
|
<!--
|
||||||
|
It should be start.sh if you're using bor v0.2.x and bor.service (ideally located under `/lib/systemd/system/`) if it's bor v0.3.x. Mention this file if you're facing any issues like unable to use some flag/s according to their expected behaviour.
|
||||||
|
-->
|
||||||
|
2. The result of `eth.syncing`, `admin.peers.length`, `admin.nodeInfo`, value of the `maxpeers` flag in start.sh, and bootnodes/static nodes (if any) is you're facing some syncing issue.
|
||||||
|
<!--
|
||||||
|
You can get the above results by attaching to the IPC using the command `bor attach $BORDIR/bor.ipc` or `bor attach $DATADIR/bor.ipc` and running the mentioned commands.
|
||||||
|
Mention this if you're facing issues where bor keeps stalling and is not importing new blocks or making any progress. Adding chain configuration mentioned in the previous step would also be really helpful here as it might also be a genesis mismatch issue.
|
||||||
|
-->
|
||||||
|
3. Your `heimdall-config.toml` parameters for checking the ETH and BOR RPC url's, incase of issue with bor heimdall communication.
|
||||||
|
<!--
|
||||||
|
The location should be `~/.heimdalld/config/` if running heimdall v0.2.x and `/var/lib/heimdalld/config` if running heimdall v0.3.x.
|
||||||
|
As a sub-set of syncing issues, if your node keeps printing logs like `Retrying again in 5 seconds to fetch data from Heimdall`, it might be an issue with the communication between your bor node and heimdall node. In this case, also check if all the heimdall services (heimdalld, bridge, rest-server) are running correctly.
|
||||||
|
-->
|
||||||
|
4. The CURL request (for that specific error) if you're facing any issues or identify a bug while making RPC request.
|
||||||
|
<!--
|
||||||
|
Make sure you hide the IP of your machine if you're doing the request externally.
|
||||||
|
-->
|
||||||
|
|
|
||||||
7
.github/ISSUE_TEMPLATE/feature.md
vendored
7
.github/ISSUE_TEMPLATE/feature.md
vendored
|
|
@ -8,10 +8,11 @@ assignees: ''
|
||||||
|
|
||||||
# Rationale
|
# Rationale
|
||||||
|
|
||||||
Why should this feature exist?
|
The motivation behind the feature and why should this feature exist?
|
||||||
What are the use-cases?
|
What are the use-cases?
|
||||||
|
|
||||||
# Implementation
|
# Implementation
|
||||||
|
|
||||||
Do you have ideas regarding the implementation of this feature?
|
Do you have ideas regarding the implementation of this feature? (Mention reference links if any)
|
||||||
Are you willing to implement this feature?
|
Any alternative solutions or features you've considered?
|
||||||
|
Are you willing to implement this feature?
|
||||||
|
|
|
||||||
8
.github/ISSUE_TEMPLATE/question.md
vendored
8
.github/ISSUE_TEMPLATE/question.md
vendored
|
|
@ -1,9 +1,11 @@
|
||||||
---
|
---
|
||||||
name: Ask a question
|
name: Question/Support
|
||||||
about: Something is unclear
|
about: Ask a question or request support
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'type:docs'
|
labels: 'type:docs'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
This should only be used in very rare cases e.g. if you are not 100% sure if something is a bug or asking a question that leads to improving the documentation. For general questions please use [discord](https://discord.gg/nthXNEv) or the Ethereum stack exchange at https://ethereum.stackexchange.com.
|
This should only be used in very rare cases e.g. if you are not 100% sure if something is a bug or asking a question that leads to improving the documentation.
|
||||||
|
|
||||||
|
For general questions please join our [discord](https://discord.com/invite/zdwkdvMNY2) server.
|
||||||
|
|
|
||||||
6
.github/matic-cli-config.yml
vendored
6
.github/matic-cli-config.yml
vendored
|
|
@ -3,9 +3,13 @@ defaultFee: 2000
|
||||||
borChainId: "15001"
|
borChainId: "15001"
|
||||||
heimdallChainId: heimdall-15001
|
heimdallChainId: heimdall-15001
|
||||||
contractsBranch: jc/v0.3.1-backport
|
contractsBranch: jc/v0.3.1-backport
|
||||||
|
sprintSize: 64
|
||||||
|
blockNumber: '0'
|
||||||
|
blockTime: '2'
|
||||||
numOfValidators: 3
|
numOfValidators: 3
|
||||||
numOfNonValidators: 0
|
numOfNonValidators: 0
|
||||||
ethURL: http://ganache:9545
|
ethURL: http://ganache:9545
|
||||||
|
ethHostUser: ubuntu
|
||||||
devnetType: docker
|
devnetType: docker
|
||||||
borDockerBuildContext: "../../bor"
|
borDockerBuildContext: "../../bor"
|
||||||
heimdallDockerBuildContext: "https://github.com/maticnetwork/heimdall.git#develop"
|
heimdallDockerBuildContext: "https://github.com/maticnetwork/heimdall.git#develop"
|
||||||
|
|
|
||||||
44
.github/pull_request_template.md
vendored
Normal file
44
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Description
|
||||||
|
|
||||||
|
Please provide a detailed description of what was done in this PR
|
||||||
|
|
||||||
|
# Changes
|
||||||
|
|
||||||
|
- [ ] Bugfix (non-breaking change that solves an issue)
|
||||||
|
- [ ] Hotfix (change that solves an urgent issue, and requires immediate attention)
|
||||||
|
- [ ] New feature (non-breaking change that adds functionality)
|
||||||
|
- [ ] Breaking change (change that is not backwards-compatible and/or changes current functionality)
|
||||||
|
|
||||||
|
# Breaking changes
|
||||||
|
|
||||||
|
Please complete this section if any breaking changes have been made, otherwise delete it
|
||||||
|
|
||||||
|
# Checklist
|
||||||
|
|
||||||
|
- [ ] I have added at least 2 reviewer or the whole pos-v1 team
|
||||||
|
- [ ] I have added sufficient documentation in code
|
||||||
|
- [ ] I will be resolving comments - if any - by pushing each fix in a separate commit and linking the commit hash in the comment reply
|
||||||
|
|
||||||
|
# Cross repository changes
|
||||||
|
|
||||||
|
- [ ] This PR requires changes to heimdall
|
||||||
|
- In case link the PR here:
|
||||||
|
- [ ] This PR requires changes to matic-cli
|
||||||
|
- In case link the PR here:
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- [ ] I have added unit tests
|
||||||
|
- [ ] I have added tests to CI
|
||||||
|
- [ ] I have tested this code manually on local environment
|
||||||
|
- [ ] I have tested this code manually on remote devnet using express-cli
|
||||||
|
- [ ] I have tested this code manually on mumbai
|
||||||
|
- [ ] I have created new e2e tests into express-cli
|
||||||
|
|
||||||
|
### Manual tests
|
||||||
|
|
||||||
|
Please complete this section with the steps you performed if you ran manual tests for this functionality, otherwise delete it
|
||||||
|
|
||||||
|
# Additional comments
|
||||||
|
|
||||||
|
Please post additional comments in this section if you have them, otherwise delete it
|
||||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
|
|
@ -8,7 +8,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- "**"
|
- "**"
|
||||||
types: [opened, synchronize, edited]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: build-${{ github.event.pull_request.number || github.ref }}
|
group: build-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
|
@ -19,7 +19,7 @@ jobs:
|
||||||
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
|
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-20.04, macos-11 ] # list of os: https://github.com/actions/virtual-environments
|
os: [ ubuntu-20.04 ] # list of os: https://github.com/actions/virtual-environments
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
@ -29,7 +29,7 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.19.x
|
||||||
|
|
||||||
- name: Install dependencies on Linux
|
- name: Install dependencies on Linux
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
@ -63,7 +63,7 @@ jobs:
|
||||||
|
|
||||||
- name: test-integration
|
- name: test-integration
|
||||||
run: make test-integration
|
run: make test-integration
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
with:
|
with:
|
||||||
|
|
@ -104,7 +104,7 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: maticnetwork/matic-cli
|
repository: maticnetwork/matic-cli
|
||||||
ref: v0.3.0-dev
|
ref: arpit/pos-655-2
|
||||||
path: matic-cli
|
path: matic-cli
|
||||||
|
|
||||||
- name: Install dependencies on Linux
|
- name: Install dependencies on Linux
|
||||||
|
|
@ -154,7 +154,7 @@ jobs:
|
||||||
cd matic-cli/devnet/code/contracts
|
cd matic-cli/devnet/code/contracts
|
||||||
npm run truffle exec scripts/deposit.js -- --network development $(jq -r .root.tokens.MaticToken contractAddresses.json) 100000000000000000000
|
npm run truffle exec scripts/deposit.js -- --network development $(jq -r .root.tokens.MaticToken contractAddresses.json) 100000000000000000000
|
||||||
cd -
|
cd -
|
||||||
bash bor/integration-tests/smoke_test.sh
|
timeout 20m bash bor/integration-tests/smoke_test.sh
|
||||||
|
|
||||||
- name: Upload logs
|
- name: Upload logs
|
||||||
if: always()
|
if: always()
|
||||||
|
|
|
||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@master
|
uses: actions/setup-go@master
|
||||||
with:
|
with:
|
||||||
go-version: 1.17.x
|
go-version: 1.19.x
|
||||||
|
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
id: prepare
|
id: prepare
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -53,3 +53,5 @@ profile.cov
|
||||||
./bor-debug-*
|
./bor-debug-*
|
||||||
|
|
||||||
dist
|
dist
|
||||||
|
|
||||||
|
*.csv
|
||||||
|
|
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -6,3 +6,6 @@
|
||||||
path = tests/evm-benchmarks
|
path = tests/evm-benchmarks
|
||||||
url = https://github.com/ipsilon/evm-benchmarks
|
url = https://github.com/ipsilon/evm-benchmarks
|
||||||
shallow = true
|
shallow = true
|
||||||
|
[submodule "tests/testdata"]
|
||||||
|
path = tests/testdata
|
||||||
|
url = https://github.com/ethereum/tests.git
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ linters:
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
- wsl
|
- wsl
|
||||||
|
- asasalint
|
||||||
#- errorlint causes stack overflow. TODO: recheck after each golangci update
|
#- errorlint causes stack overflow. TODO: recheck after each golangci update
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
|
|
|
||||||
13
Makefile
13
Makefile
|
|
@ -34,6 +34,10 @@ bor:
|
||||||
protoc:
|
protoc:
|
||||||
protoc --go_out=. --go-grpc_out=. ./internal/cli/server/proto/*.proto
|
protoc --go_out=. --go-grpc_out=. ./internal/cli/server/proto/*.proto
|
||||||
|
|
||||||
|
generate-mocks:
|
||||||
|
go generate mockgen -destination=./tests/bor/mocks/IHeimdallClient.go -package=mocks ./consensus/bor IHeimdallClient
|
||||||
|
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
||||||
|
|
||||||
geth:
|
geth:
|
||||||
$(GORUN) build/ci.go install ./cmd/geth
|
$(GORUN) build/ci.go install ./cmd/geth
|
||||||
@echo "Done building."
|
@echo "Done building."
|
||||||
|
|
@ -61,7 +65,7 @@ test-race:
|
||||||
$(GOTEST) --timeout 15m -race -shuffle=on $(TESTALL)
|
$(GOTEST) --timeout 15m -race -shuffle=on $(TESTALL)
|
||||||
|
|
||||||
test-integration:
|
test-integration:
|
||||||
$(GOTEST) --timeout 30m -tags integration $(TESTE2E)
|
$(GOTEST) --timeout 60m -tags integration $(TESTE2E)
|
||||||
|
|
||||||
escape:
|
escape:
|
||||||
cd $(path) && go test -gcflags "-m -m" -run none -bench=BenchmarkJumpdest* -benchmem -memprofile mem.out
|
cd $(path) && go test -gcflags "-m -m" -run none -bench=BenchmarkJumpdest* -benchmem -memprofile mem.out
|
||||||
|
|
@ -71,7 +75,7 @@ lint:
|
||||||
|
|
||||||
lintci-deps:
|
lintci-deps:
|
||||||
rm -f ./build/bin/golangci-lint
|
rm -f ./build/bin/golangci-lint
|
||||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./build/bin v1.46.0
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./build/bin v1.48.0
|
||||||
|
|
||||||
goimports:
|
goimports:
|
||||||
goimports -local "$(PACKAGE)" -w .
|
goimports -local "$(PACKAGE)" -w .
|
||||||
|
|
@ -93,7 +97,7 @@ devtools:
|
||||||
$(GOBUILD) -o $(GOBIN)/codecgen github.com/ugorji/go/codec/codecgen
|
$(GOBUILD) -o $(GOBIN)/codecgen github.com/ugorji/go/codec/codecgen
|
||||||
$(GOBUILD) -o $(GOBIN)/abigen ./cmd/abigen
|
$(GOBUILD) -o $(GOBIN)/abigen ./cmd/abigen
|
||||||
$(GOBUILD) -o $(GOBIN)/mockgen github.com/golang/mock/mockgen
|
$(GOBUILD) -o $(GOBIN)/mockgen github.com/golang/mock/mockgen
|
||||||
$(GOBUILD) -o $(GOBIN)/protoc-gen-go github.com/golang/protobuf/protoc-gen-go
|
$(GOBUILD) -o $(GOBIN)/protoc-gen-go google.golang.org/protobuf/cmd/protoc-gen-go
|
||||||
PATH=$(GOBIN):$(PATH) go generate ./common
|
PATH=$(GOBIN):$(PATH) go generate ./common
|
||||||
PATH=$(GOBIN):$(PATH) go generate ./core/types
|
PATH=$(GOBIN):$(PATH) go generate ./core/types
|
||||||
PATH=$(GOBIN):$(PATH) go generate ./consensus/bor
|
PATH=$(GOBIN):$(PATH) go generate ./consensus/bor
|
||||||
|
|
@ -192,7 +196,7 @@ geth-windows-amd64:
|
||||||
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
|
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
|
||||||
|
|
||||||
PACKAGE_NAME := github.com/maticnetwork/bor
|
PACKAGE_NAME := github.com/maticnetwork/bor
|
||||||
GOLANG_CROSS_VERSION ?= v1.18.1
|
GOLANG_CROSS_VERSION ?= v1.19.1
|
||||||
|
|
||||||
.PHONY: release-dry-run
|
.PHONY: release-dry-run
|
||||||
release-dry-run:
|
release-dry-run:
|
||||||
|
|
@ -220,6 +224,7 @@ release:
|
||||||
-e DOCKER_PASSWORD \
|
-e DOCKER_PASSWORD \
|
||||||
-e SLACK_WEBHOOK \
|
-e SLACK_WEBHOOK \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v $(HOME)/.docker/config.json:/root/.docker/config.json \
|
||||||
-v `pwd`:/go/src/$(PACKAGE_NAME) \
|
-v `pwd`:/go/src/$(PACKAGE_NAME) \
|
||||||
-w /go/src/$(PACKAGE_NAME) \
|
-w /go/src/$(PACKAGE_NAME) \
|
||||||
goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \
|
goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Bor Overview
|
# Bor Overview
|
||||||
Bor is the Official Golang implementation of the Matic protocol. It is a fork of Go Ethereum - https://github.com/ethereum/go-ethereum and EVM compabile.
|
Bor is the Official Golang implementation of the Matic protocol. It is a fork of Go Ethereum - https://github.com/ethereum/go-ethereum and EVM compatible.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
|
||||||
103
RETESTBOR.md
Normal file
103
RETESTBOR.md
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
|
||||||
|
# Retesteth - bor
|
||||||
|
|
||||||
|
These integration tests are included in the bor repo via using the git submodule
|
||||||
|
|
||||||
|
```
|
||||||
|
[submodule "tests/testdata"]
|
||||||
|
path = tests/testdata
|
||||||
|
url = https://github.com/ethereum/tests.git
|
||||||
|
```
|
||||||
|
|
||||||
|
The version used is the last stable release, tagged as v10.4 from branch develop in ethereum/tests
|
||||||
|
Details on release code can be found here https://github.com/ethereum/tests/commit/a380655e5ffab1a5ea0f4d860224bdb19013f06a
|
||||||
|
|
||||||
|
To run the tests, we hava a `make` command:
|
||||||
|
```
|
||||||
|
make test-integration
|
||||||
|
```
|
||||||
|
which is also integrated into the CI pipeline on GitHub
|
||||||
|
|
||||||
|
|
||||||
|
## Retesteth - bor on remote machine
|
||||||
|
|
||||||
|
To explore and test the `retesteth` package, the following steps were executed.
|
||||||
|
This is only for educational purposes.
|
||||||
|
For future usage, there is no need to go through this section, the only thing needed is to have 'green' integration tests.
|
||||||
|
|
||||||
|
- `ssh` into a VM running bor
|
||||||
|
- Change configs by replacing geth with bor inside the docker container
|
||||||
|
```
|
||||||
|
mkdir ~/retestethBuild
|
||||||
|
cd ~/retestethBuild
|
||||||
|
wget https://raw.githubusercontent.com/ethereum/retesteth/develop/dretesteth.sh
|
||||||
|
chmod +x dretesteth.sh
|
||||||
|
wget https://raw.githubusercontent.com/ethereum/retesteth/develop/Dockerfile
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify the RUN git clone line in the Dockerfile for repo “retesteth” to change branch -b from master to develop. Do not modify repo branches for “winsvega/solidity” [LLLC opcode support] and “go-ethereum”.
|
||||||
|
Modify the Dockerfile so that the eth client points to bor
|
||||||
|
e.g. : `https://github.com/ethereum/retesteth/blob/master/Dockerfile#L41`
|
||||||
|
from `RUN git clone --depth 1 -b master https://github.com/ethereum/go-ethereum.git /geth`
|
||||||
|
to: `RUN git clone --depth 1 -b master https://github.com/maticnetwork/bor.git /geth`
|
||||||
|
|
||||||
|
- build docker image
|
||||||
|
`sudo ./dretesteth.sh build`
|
||||||
|
|
||||||
|
- clone repo
|
||||||
|
```
|
||||||
|
git clone --branch develop https://github.com/ethereum/tests.git
|
||||||
|
```
|
||||||
|
this step is eventually replaced by adding the git submodule directly into bor repo with
|
||||||
|
```
|
||||||
|
git submodule add --depth 1 https://github.com/ethereum/tests.git tests/testdata
|
||||||
|
```
|
||||||
|
- Let's move to the restestethBuild folder
|
||||||
|
```
|
||||||
|
cd /home/ubuntu/retestethBuild
|
||||||
|
```
|
||||||
|
Now we have the tests repo here
|
||||||
|
```
|
||||||
|
ls
|
||||||
|
> Dockerfile dretesteth.sh tests
|
||||||
|
```
|
||||||
|
- Run test example
|
||||||
|
```
|
||||||
|
./dretesteth.sh -t GeneralStateTests/stExample -- --testpath /home/ubuntu/retestethBuild/tests --datadir /tests/config
|
||||||
|
```
|
||||||
|
This will create the config files for the different clients in ~/tests/config
|
||||||
|
Eventually. these config needs to be adapted according to the following doc
|
||||||
|
https://ethereum-tests.readthedocs.io/en/latest/retesteth-tutorial.html
|
||||||
|
Specifically:
|
||||||
|
```
|
||||||
|
f you look inside ~/tests/config, you’ll see a directory for each configured client. Typically this directory has these files:
|
||||||
|
|
||||||
|
config, which contains the configuration for the client:
|
||||||
|
The communication protocol to use with the client (typically TCP)
|
||||||
|
The address(es) to use with that protocol
|
||||||
|
The forks the client supports
|
||||||
|
The exceptions the client can throw, and how retesteth should interpret them. This is particularly important when testing the client’s behavior when given invalid blocks.
|
||||||
|
start.sh, which starts the client inside the docker image
|
||||||
|
stop.sh, which stops the client instance(s)
|
||||||
|
genesis, a directory which includes the genesis blocks for various forks the client supports. If this directory does not exist for a client, it uses the genesis blocks for the default client.
|
||||||
|
```
|
||||||
|
|
||||||
|
We replaced geth inside docker by using https://ethereum-tests.readthedocs.io/en/latest/retesteth-tutorial.html#replace-geth-inside-the-docker
|
||||||
|
Theoretically, we would not need any additional config change
|
||||||
|
|
||||||
|
- Run test suites
|
||||||
|
```
|
||||||
|
./dretesteth.sh -t <TestSuiteName> -- --testpath /home/ubuntu/retestethBuild/tests --datadir /tests/config
|
||||||
|
```
|
||||||
|
Where `TestSuiteName` is one of the maintained test suites, reported here https://github.com/ethereum/tests
|
||||||
|
```
|
||||||
|
BasicTests
|
||||||
|
BlockchainTests
|
||||||
|
GeneralStateTests
|
||||||
|
TransactionTests
|
||||||
|
RLPTest
|
||||||
|
src
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to run retestheth against a bor client on localhost:8545 (using 8 threads), instead of isolating it into a docker image, run
|
||||||
|
`sudo ./dretesteth.sh -t GeneralStateTests -- --testpath ~/tests --datadir /tests/config --clients t8ntool --nodes 127.0.0.1:8545 -j 8`
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"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"
|
||||||
|
|
@ -45,14 +44,17 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := keystore.DecryptKey(json, passphrase)
|
key, err := keystore.DecryptKey(json, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewKeyedTransactor(key.PrivateKey), nil
|
return NewKeyedTransactor(key.PrivateKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +108,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ type SimulatedBackend struct {
|
||||||
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{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||||
genesis.MustCommit(database)
|
genesis.MustCommit(database)
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
|
||||||
backend := &SimulatedBackend{
|
backend := &SimulatedBackend{
|
||||||
database: database,
|
database: database,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -1966,7 +1965,7 @@ func TestGolangBindings(t *testing.T) {
|
||||||
t.Skip("go sdk not found for testing")
|
t.Skip("go sdk not found for testing")
|
||||||
}
|
}
|
||||||
// Create a temporary workspace for the test suite
|
// Create a temporary workspace for the test suite
|
||||||
ws, err := ioutil.TempDir("", "binding-test")
|
ws, err := os.MkdirTemp("", "binding-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temporary workspace: %v", err)
|
t.Fatalf("failed to create temporary workspace: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1990,7 +1989,7 @@ func TestGolangBindings(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: failed to generate binding: %v", i, err)
|
t.Fatalf("test %d: failed to generate binding: %v", i, err)
|
||||||
}
|
}
|
||||||
if err = ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil {
|
if err = os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil {
|
||||||
t.Fatalf("test %d: failed to write binding: %v", i, err)
|
t.Fatalf("test %d: failed to write binding: %v", i, err)
|
||||||
}
|
}
|
||||||
// Generate the test file with the injected test code
|
// Generate the test file with the injected test code
|
||||||
|
|
@ -2006,7 +2005,7 @@ func TestGolangBindings(t *testing.T) {
|
||||||
%s
|
%s
|
||||||
}
|
}
|
||||||
`, tt.imports, tt.name, tt.tester)
|
`, tt.imports, tt.name, tt.tester)
|
||||||
if err := ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0600); err != nil {
|
if err := os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0600); err != nil {
|
||||||
t.Fatalf("test %d: failed to write tests: %v", i, err)
|
t.Fatalf("test %d: failed to write tests: %v", i, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ syncmode = "full"
|
||||||
# noprefetch = false
|
# noprefetch = false
|
||||||
# preimages = false
|
# preimages = false
|
||||||
# txlookuplimit = 2350000
|
# txlookuplimit = 2350000
|
||||||
|
# triesinmemory = 128
|
||||||
# timeout = "1h0m0s"
|
# timeout = "1h0m0s"
|
||||||
|
|
||||||
[accounts]
|
[accounts]
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,12 @@
|
||||||
"period": {
|
"period": {
|
||||||
"0": 2
|
"0": 2
|
||||||
},
|
},
|
||||||
"producerDelay": 6,
|
"producerDelay": {
|
||||||
"sprint": 64,
|
"0": 6
|
||||||
|
},
|
||||||
|
"sprint": {
|
||||||
|
"0": 64
|
||||||
|
},
|
||||||
"backupMultiplier": {
|
"backupMultiplier": {
|
||||||
"0": 2
|
"0": 2
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,24 @@
|
||||||
"londonBlock": 22640000,
|
"londonBlock": 22640000,
|
||||||
"bor": {
|
"bor": {
|
||||||
"jaipurBlock": 22770000,
|
"jaipurBlock": 22770000,
|
||||||
|
"delhiBlock": 29638656,
|
||||||
"period": {
|
"period": {
|
||||||
"0": 2,
|
"0": 2,
|
||||||
"25275000": 5
|
"25275000": 5,
|
||||||
|
"29638656": 2
|
||||||
|
},
|
||||||
|
"producerDelay": {
|
||||||
|
"0": 6,
|
||||||
|
"29638656": 4
|
||||||
|
},
|
||||||
|
"sprint": {
|
||||||
|
"0": 64,
|
||||||
|
"29638656": 16
|
||||||
},
|
},
|
||||||
"producerDelay": 6,
|
|
||||||
"sprint": 64,
|
|
||||||
"backupMultiplier": {
|
"backupMultiplier": {
|
||||||
"0": 2,
|
"0": 2,
|
||||||
"25275000": 5
|
"25275000": 5,
|
||||||
|
"29638656": 2
|
||||||
},
|
},
|
||||||
"validatorContract": "0x0000000000000000000000000000000000001000",
|
"validatorContract": "0x0000000000000000000000000000000000001000",
|
||||||
"stateReceiverContract": "0x0000000000000000000000000000000000001001",
|
"stateReceiverContract": "0x0000000000000000000000000000000000001001",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -188,7 +189,7 @@ func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) {
|
||||||
// If the testmode is used, the sealer will return quickly, and complain
|
// If the testmode is used, the sealer will return quickly, and complain
|
||||||
// "Sealing result is not read by miner" if it cannot write the result.
|
// "Sealing result is not read by miner" if it cannot write the result.
|
||||||
results := make(chan *types.Block, 1)
|
results := make(chan *types.Block, 1)
|
||||||
if err := engine.Seal(nil, block, results, nil); err != nil {
|
if err := engine.Seal(context.Background(), nil, block, results, nil); err != nil {
|
||||||
panic(fmt.Sprintf("failed to seal block: %v", err))
|
panic(fmt.Sprintf("failed to seal block: %v", err))
|
||||||
}
|
}
|
||||||
found := <-results
|
found := <-results
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
|
|
||||||
txIndex++
|
txIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
|
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
// Add mining reward?
|
// Add mining reward?
|
||||||
if miningReward > 0 {
|
if miningReward > 0 {
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to
|
||||||
// bor related flags
|
// bor related flags
|
||||||
utils.HeimdallURLFlag,
|
utils.HeimdallURLFlag,
|
||||||
utils.WithoutHeimdallFlag,
|
utils.WithoutHeimdallFlag,
|
||||||
|
utils.HeimdallgRPCAddressFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
Description: `
|
Description: `
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/external"
|
"github.com/ethereum/go-ethereum/accounts/external"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
|
@ -71,7 +69,7 @@ type gethConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig(file string, cfg *gethConfig) error {
|
func loadConfig(file string, cfg *gethConfig) error {
|
||||||
data, err := ioutil.ReadFile(file)
|
data, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -30,10 +31,18 @@ var (
|
||||||
Usage: "Run without Heimdall service (for testing purpose)",
|
Usage: "Run without Heimdall service (for testing purpose)",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeimdallgRPCAddressFlag flag for heimdall gRPC address
|
||||||
|
HeimdallgRPCAddressFlag = cli.StringFlag{
|
||||||
|
Name: "bor.heimdallgRPC",
|
||||||
|
Usage: "Address of Heimdall gRPC service",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
|
||||||
// BorFlags all bor related flags
|
// BorFlags all bor related flags
|
||||||
BorFlags = []cli.Flag{
|
BorFlags = []cli.Flag{
|
||||||
HeimdallURLFlag,
|
HeimdallURLFlag,
|
||||||
WithoutHeimdallFlag,
|
WithoutHeimdallFlag,
|
||||||
|
HeimdallgRPCAddressFlag,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -56,6 +65,7 @@ func getGenesis(genesisPath string) (*core.Genesis, error) {
|
||||||
func SetBorConfig(ctx *cli.Context, cfg *eth.Config) {
|
func SetBorConfig(ctx *cli.Context, cfg *eth.Config) {
|
||||||
cfg.HeimdallURL = ctx.GlobalString(HeimdallURLFlag.Name)
|
cfg.HeimdallURL = ctx.GlobalString(HeimdallURLFlag.Name)
|
||||||
cfg.WithoutHeimdall = ctx.GlobalBool(WithoutHeimdallFlag.Name)
|
cfg.WithoutHeimdall = ctx.GlobalBool(WithoutHeimdallFlag.Name)
|
||||||
|
cfg.HeimdallgRPCAddress = ctx.GlobalString(HeimdallgRPCAddressFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBorEthereum Creates bor ethereum object from eth.Config
|
// CreateBorEthereum Creates bor ethereum object from eth.Config
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
pcsclite "github.com/gballet/go-libpcsclite"
|
||||||
|
gopsutil "github.com/shirou/gopsutil/mem"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -68,9 +72,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
pcsclite "github.com/gballet/go-libpcsclite"
|
|
||||||
gopsutil "github.com/shirou/gopsutil/mem"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -1503,7 +1504,7 @@ func setPeerRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
|
|
||||||
if peerRequiredBlocks == "" {
|
if peerRequiredBlocks == "" {
|
||||||
if ctx.GlobalIsSet(LegacyWhitelistFlag.Name) {
|
if ctx.GlobalIsSet(LegacyWhitelistFlag.Name) {
|
||||||
log.Warn("The flag --rpc is deprecated and will be removed, please use --peer.requiredblocks")
|
log.Warn("The flag --whitelist is deprecated and will be removed, please use --eth.requiredblocks")
|
||||||
peerRequiredBlocks = ctx.GlobalString(LegacyWhitelistFlag.Name)
|
peerRequiredBlocks = ctx.GlobalString(LegacyWhitelistFlag.Name)
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
|
|
@ -2030,9 +2031,10 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
||||||
engine = clique.New(config.Clique, chainDb)
|
engine = clique.New(config.Clique, chainDb)
|
||||||
} else if config.Bor != nil {
|
} else if config.Bor != nil {
|
||||||
ethereum = CreateBorEthereum(ð.Config{
|
ethereum = CreateBorEthereum(ð.Config{
|
||||||
Genesis: genesis,
|
Genesis: genesis,
|
||||||
HeimdallURL: ctx.GlobalString(HeimdallURLFlag.Name),
|
HeimdallURL: ctx.GlobalString(HeimdallURLFlag.Name),
|
||||||
WithoutHeimdall: ctx.GlobalBool(WithoutHeimdallFlag.Name),
|
WithoutHeimdall: ctx.GlobalBool(WithoutHeimdallFlag.Name),
|
||||||
|
HeimdallgRPCAddress: ctx.GlobalString(HeimdallgRPCAddressFlag.Name),
|
||||||
})
|
})
|
||||||
engine = ethereum.Engine()
|
engine = ethereum.Engine()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2079,7 +2081,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
||||||
|
|
||||||
// TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only.
|
// TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only.
|
||||||
// Disable transaction indexing/unindexing by default.
|
// Disable transaction indexing/unindexing by default.
|
||||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil)
|
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Can't create BlockChain: %v", err)
|
Fatalf("Can't create BlockChain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
common/network/port.go
Normal file
36
common/network/port.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxPortCheck = 100
|
||||||
|
|
||||||
|
emptyPort = "127.0.0.1:0"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCantFindAPort = errors.New("no available port found")
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindAvailablePort returns the an available port
|
||||||
|
func FindAvailablePort() (int, net.Listener, error) {
|
||||||
|
var (
|
||||||
|
listener net.Listener
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := uint(0); i < maxPortCheck; i++ {
|
||||||
|
listener, err = net.Listen("tcp", emptyPort)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener.Addr().(*net.TCPAddr).Port, listener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil, fmt.Errorf("%w: %s", ErrCantFindAPort, err)
|
||||||
|
}
|
||||||
96
common/tracing/context.go
Normal file
96
common/tracing/context.go
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
package tracing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tracerKey struct{}
|
||||||
|
|
||||||
|
type Option func(context.Context, trace.Span)
|
||||||
|
|
||||||
|
func WithTracer(ctx context.Context, tr trace.Tracer) context.Context {
|
||||||
|
return context.WithValue(ctx, tracerKey{}, tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromContext(ctx context.Context) trace.Tracer {
|
||||||
|
tr, _ := ctx.Value(tracerKey{}).(trace.Tracer)
|
||||||
|
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartSpan(ctx context.Context, snapName string) (context.Context, trace.Span) {
|
||||||
|
tr := FromContext(ctx)
|
||||||
|
|
||||||
|
if tr == nil {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, span := tr.Start(ctx, snapName)
|
||||||
|
ctx = WithTracer(ctx, tr)
|
||||||
|
|
||||||
|
return ctx, span
|
||||||
|
}
|
||||||
|
|
||||||
|
func EndSpan(span trace.Span) {
|
||||||
|
if span != nil {
|
||||||
|
span.End()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Trace(ctx context.Context, spanName string) (context.Context, trace.Span) {
|
||||||
|
tr := FromContext(ctx)
|
||||||
|
|
||||||
|
if tr == nil {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr.Start(ctx, spanName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec(ctx context.Context, spanName string, opts ...Option) {
|
||||||
|
var span trace.Span
|
||||||
|
|
||||||
|
tr := FromContext(ctx)
|
||||||
|
|
||||||
|
if tr != nil {
|
||||||
|
ctx, span = tr.Start(ctx, spanName)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, optFn := range opts {
|
||||||
|
optFn(ctx, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr != nil {
|
||||||
|
span.End()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTime(fn func(context.Context, trace.Span)) Option {
|
||||||
|
return func(ctx context.Context, span trace.Span) {
|
||||||
|
ElapsedTime(ctx, span, "elapsed", fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ElapsedTime(ctx context.Context, span trace.Span, msg string, fn func(context.Context, trace.Span)) {
|
||||||
|
var now time.Time
|
||||||
|
|
||||||
|
if span != nil {
|
||||||
|
now = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(ctx, span)
|
||||||
|
|
||||||
|
if span != nil {
|
||||||
|
span.SetAttributes(attribute.Int(msg, int(time.Since(now).Milliseconds())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetAttributes(span trace.Span, kvs ...attribute.KeyValue) {
|
||||||
|
if span != nil {
|
||||||
|
span.SetAttributes(kvs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package beacon
|
package beacon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -170,10 +171,11 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
||||||
// verifyHeader checks whether a header conforms to the consensus rules of the
|
// verifyHeader checks whether a header conforms to the consensus rules of the
|
||||||
// stock Ethereum consensus engine. The difference between the beacon and classic is
|
// stock Ethereum consensus engine. The difference between the beacon and classic is
|
||||||
// (a) The following fields are expected to be constants:
|
// (a) The following fields are expected to be constants:
|
||||||
// - difficulty is expected to be 0
|
// - difficulty is expected to be 0
|
||||||
// - nonce is expected to be 0
|
// - nonce is expected to be 0
|
||||||
// - unclehash is expected to be Hash(emptyHeader)
|
// - unclehash is expected to be Hash(emptyHeader)
|
||||||
// to be the desired constants
|
// to be the desired constants
|
||||||
|
//
|
||||||
// (b) the timestamp is not verified anymore
|
// (b) the timestamp is not verified anymore
|
||||||
// (c) the extradata is limited to 32 bytes
|
// (c) the extradata is limited to 32 bytes
|
||||||
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
||||||
|
|
@ -278,11 +280,11 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
||||||
// assembling the block.
|
// assembling the block.
|
||||||
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (beacon *Beacon) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
// FinalizeAndAssemble is different with Prepare, it can be used in both block
|
// FinalizeAndAssemble is different with Prepare, it can be used in both block
|
||||||
// generation and verification. So determine the consensus rules by header type.
|
// generation and verification. So determine the consensus rules by header type.
|
||||||
if !beacon.IsPoSHeader(header) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
|
return beacon.ethone.FinalizeAndAssemble(ctx, chain, header, state, txs, uncles, receipts)
|
||||||
}
|
}
|
||||||
// Finalize and assemble the block
|
// Finalize and assemble the block
|
||||||
beacon.Finalize(chain, header, state, txs, uncles)
|
beacon.Finalize(chain, header, state, txs, uncles)
|
||||||
|
|
@ -294,9 +296,9 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
||||||
//
|
//
|
||||||
// Note, the method returns immediately and will send the result async. More
|
// Note, the method returns immediately and will send the result async. More
|
||||||
// than one result may also be returned depending on the consensus algorithm.
|
// than one result may also be returned depending on the consensus algorithm.
|
||||||
func (beacon *Beacon) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
func (beacon *Beacon) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
||||||
if !beacon.IsPoSHeader(block.Header()) {
|
if !beacon.IsPoSHeader(block.Header()) {
|
||||||
return beacon.ethone.Seal(chain, block, results, stop)
|
return beacon.ethone.Seal(ctx, chain, block, results, stop)
|
||||||
}
|
}
|
||||||
// The seal verification is done by the external consensus engine,
|
// The seal verification is done by the external consensus engine,
|
||||||
// return directly without pushing any block back. In another word
|
// return directly without pushing any block back. In another word
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package bor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -11,13 +12,17 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
||||||
"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/common/tracing"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/api"
|
"github.com/ethereum/go-ethereum/consensus/bor/api"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||||
|
|
@ -45,7 +50,9 @@ const (
|
||||||
|
|
||||||
// Bor protocol constants.
|
// Bor protocol constants.
|
||||||
var (
|
var (
|
||||||
defaultSprintLength = uint64(64) // Default number of blocks after which to checkpoint and reset the pending votes
|
defaultSprintLength = map[string]uint64{
|
||||||
|
"0": 64,
|
||||||
|
} // Default number of blocks after which to checkpoint and reset the pending votes
|
||||||
|
|
||||||
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
|
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
|
||||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||||
|
|
@ -96,6 +103,9 @@ var (
|
||||||
// errOutOfRangeChain is returned if an authorization list is attempted to
|
// errOutOfRangeChain is returned if an authorization list is attempted to
|
||||||
// be modified via out-of-range or non-contiguous headers.
|
// be modified via out-of-range or non-contiguous headers.
|
||||||
errOutOfRangeChain = errors.New("out of range or non-contiguous chain")
|
errOutOfRangeChain = errors.New("out of range or non-contiguous chain")
|
||||||
|
|
||||||
|
errUncleDetected = errors.New("uncles not allowed")
|
||||||
|
errUnknownValidators = errors.New("unknown validators")
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignerFn is a signer callback function to request a header to be signed by a
|
// SignerFn is a signer callback function to request a header to be signed by a
|
||||||
|
|
@ -159,7 +169,7 @@ func encodeSigHeader(w io.Writer, header *types.Header, c *params.BorConfig) {
|
||||||
header.Nonce,
|
header.Nonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsJaipur(header.Number.Uint64()) {
|
if c.IsJaipur(header.Number) {
|
||||||
if header.BaseFee != nil {
|
if header.BaseFee != nil {
|
||||||
enc = append(enc, header.BaseFee)
|
enc = append(enc, header.BaseFee)
|
||||||
}
|
}
|
||||||
|
|
@ -175,8 +185,8 @@ func CalcProducerDelay(number uint64, succession int, c *params.BorConfig) uint6
|
||||||
// When the block is the first block of the sprint, it is expected to be delayed by `producerDelay`.
|
// When the block is the first block of the sprint, it is expected to be delayed by `producerDelay`.
|
||||||
// That is to allow time for block propagation in the last sprint
|
// That is to allow time for block propagation in the last sprint
|
||||||
delay := c.CalculatePeriod(number)
|
delay := c.CalculatePeriod(number)
|
||||||
if number%c.Sprint == 0 {
|
if number%c.CalculateSprint(number) == 0 {
|
||||||
delay = c.ProducerDelay
|
delay = c.CalculateProducerDelay(number)
|
||||||
}
|
}
|
||||||
|
|
||||||
if succession > 0 {
|
if succession > 0 {
|
||||||
|
|
@ -209,9 +219,7 @@ type Bor struct {
|
||||||
recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
|
recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
|
||||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||||
|
|
||||||
signer common.Address // Ethereum address of the signing key
|
authorizedSigner atomic.Pointer[signer] // Ethereum address and sign function of the signing key
|
||||||
signFn SignerFn // Signer function to authorize hashes with
|
|
||||||
lock sync.RWMutex // Protects the signer fields
|
|
||||||
|
|
||||||
ethAPI api.Caller
|
ethAPI api.Caller
|
||||||
spanner Spanner
|
spanner Spanner
|
||||||
|
|
@ -224,6 +232,11 @@ type Bor struct {
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type signer struct {
|
||||||
|
signer common.Address // Ethereum address of the signing key
|
||||||
|
signFn SignerFn // Signer function to authorize hashes with
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a Matic Bor consensus engine.
|
// New creates a Matic Bor consensus engine.
|
||||||
func New(
|
func New(
|
||||||
chainConfig *params.ChainConfig,
|
chainConfig *params.ChainConfig,
|
||||||
|
|
@ -237,7 +250,7 @@ func New(
|
||||||
borConfig := chainConfig.Bor
|
borConfig := chainConfig.Bor
|
||||||
|
|
||||||
// Set any missing consensus parameters to their defaults
|
// Set any missing consensus parameters to their defaults
|
||||||
if borConfig != nil && borConfig.Sprint == 0 {
|
if borConfig != nil && borConfig.CalculateSprint(0) == 0 {
|
||||||
borConfig.Sprint = defaultSprintLength
|
borConfig.Sprint = defaultSprintLength
|
||||||
}
|
}
|
||||||
// Allocate the snapshot caches and create the engine
|
// Allocate the snapshot caches and create the engine
|
||||||
|
|
@ -256,6 +269,14 @@ func New(
|
||||||
HeimdallClient: heimdallClient,
|
HeimdallClient: heimdallClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.authorizedSigner.Store(&signer{
|
||||||
|
common.Address{},
|
||||||
|
func(_ accounts.Account, _ string, i []byte) ([]byte, error) {
|
||||||
|
// return an error to prevent panics
|
||||||
|
return nil, &UnauthorizedSignerError{0, common.Address{}.Bytes()}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// make sure we can decode all the GenesisAlloc in the BorConfig.
|
// make sure we can decode all the GenesisAlloc in the BorConfig.
|
||||||
for key, genesisAlloc := range c.config.BlockAlloc {
|
for key, genesisAlloc := range c.config.BlockAlloc {
|
||||||
if _, err := decodeGenesisAlloc(genesisAlloc); err != nil {
|
if _, err := decodeGenesisAlloc(genesisAlloc); err != nil {
|
||||||
|
|
@ -273,14 +294,14 @@ func (c *Bor) Author(header *types.Header) (common.Address, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyHeader checks whether a header conforms to the consensus rules.
|
// VerifyHeader checks whether a header conforms to the consensus rules.
|
||||||
func (c *Bor) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
|
func (c *Bor) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, _ bool) error {
|
||||||
return c.verifyHeader(chain, header, nil)
|
return c.verifyHeader(chain, header, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
|
||||||
// method returns a quit channel to abort the operations and a results channel to
|
// method returns a quit channel to abort the operations and a results channel to
|
||||||
// retrieve the async verifications (the order is that of the input slice).
|
// retrieve the async verifications (the order is that of the input slice).
|
||||||
func (c *Bor) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
func (c *Bor) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, _ []bool) (chan<- struct{}, <-chan error) {
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
results := make(chan error, len(headers))
|
results := make(chan error, len(headers))
|
||||||
|
|
||||||
|
|
@ -320,7 +341,7 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head
|
||||||
}
|
}
|
||||||
|
|
||||||
// check extr adata
|
// check extr adata
|
||||||
isSprintEnd := IsSprintStart(number+1, c.config.Sprint)
|
isSprintEnd := IsSprintStart(number+1, c.config.CalculateSprint(number))
|
||||||
|
|
||||||
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
|
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
|
||||||
signersBytes := len(header.Extra) - extraVanity - extraSeal
|
signersBytes := len(header.Extra) - extraVanity - extraSeal
|
||||||
|
|
@ -434,7 +455,7 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the validator list in the last sprint block
|
// verify the validator list in the last sprint block
|
||||||
if IsSprintStart(number, c.config.Sprint) {
|
if IsSprintStart(number, c.config.CalculateSprint(number)) {
|
||||||
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
|
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
|
||||||
validatorsBytes := make([]byte, len(snap.ValidatorSet.Validators)*validatorHeaderBytesLength)
|
validatorsBytes := make([]byte, len(snap.ValidatorSet.Validators)*validatorHeaderBytesLength)
|
||||||
|
|
||||||
|
|
@ -487,6 +508,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
|
||||||
// at a checkpoint block without a parent (light client CHT), or we have piled
|
// at a checkpoint block without a parent (light client CHT), or we have piled
|
||||||
// up more headers than allowed to be reorged (chain reinit from a freezer),
|
// up more headers than allowed to be reorged (chain reinit from a freezer),
|
||||||
// consider the checkpoint trusted and snapshot it.
|
// consider the checkpoint trusted and snapshot it.
|
||||||
|
|
||||||
// TODO fix this
|
// TODO fix this
|
||||||
// nolint:nestif
|
// nolint:nestif
|
||||||
if number == 0 {
|
if number == 0 {
|
||||||
|
|
@ -496,7 +518,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
|
||||||
hash := checkpoint.Hash()
|
hash := checkpoint.Hash()
|
||||||
|
|
||||||
// get validators and current span
|
// get validators and current span
|
||||||
validators, err := c.spanner.GetCurrentValidators(hash, number+1)
|
validators, err := c.spanner.GetCurrentValidators(context.Background(), hash, number+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -537,7 +559,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
|
||||||
|
|
||||||
// check if snapshot is nil
|
// check if snapshot is nil
|
||||||
if snap == nil {
|
if snap == nil {
|
||||||
return nil, fmt.Errorf("Unknown error while retrieving snapshot at block number %v", number)
|
return nil, fmt.Errorf("unknown error while retrieving snapshot at block number %v", number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Previous snapshot found, apply any pending headers on top of it
|
// Previous snapshot found, apply any pending headers on top of it
|
||||||
|
|
@ -566,9 +588,9 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
|
||||||
|
|
||||||
// VerifyUncles implements consensus.Engine, always returning an error for any
|
// VerifyUncles implements consensus.Engine, always returning an error for any
|
||||||
// uncles as this consensus mechanism doesn't permit uncles.
|
// uncles as this consensus mechanism doesn't permit uncles.
|
||||||
func (c *Bor) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
func (c *Bor) VerifyUncles(_ consensus.ChainReader, block *types.Block) error {
|
||||||
if len(block.Uncles()) > 0 {
|
if len(block.Uncles()) > 0 {
|
||||||
return errors.New("uncles not allowed")
|
return errUncleDetected
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -602,7 +624,7 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
if !snap.ValidatorSet.HasAddress(signer) {
|
||||||
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
|
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
|
||||||
return &UnauthorizedSignerError{number - 1, signer.Bytes()}
|
return &UnauthorizedSignerError{number - 1, signer.Bytes()}
|
||||||
}
|
}
|
||||||
|
|
@ -619,13 +641,13 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
|
||||||
parent = chain.GetHeader(header.ParentHash, number-1)
|
parent = chain.GetHeader(header.ParentHash, number-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parent != nil && header.Time < parent.Time+CalcProducerDelay(number, succession, c.config) {
|
if IsBlockOnTime(parent, header, number, succession, c.config) {
|
||||||
return &BlockTooSoonError{number, succession}
|
return &BlockTooSoonError{number, succession}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
||||||
if !c.fakeDiff {
|
if !c.fakeDiff {
|
||||||
difficulty := snap.Difficulty(signer)
|
difficulty := Difficulty(snap.ValidatorSet, signer)
|
||||||
if header.Difficulty.Uint64() != difficulty {
|
if header.Difficulty.Uint64() != difficulty {
|
||||||
return &WrongDifficultyError{number, difficulty, header.Difficulty.Uint64(), signer.Bytes()}
|
return &WrongDifficultyError{number, difficulty, header.Difficulty.Uint64(), signer.Bytes()}
|
||||||
}
|
}
|
||||||
|
|
@ -634,6 +656,10 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsBlockOnTime(parent *types.Header, header *types.Header, number uint64, succession int, cfg *params.BorConfig) bool {
|
||||||
|
return parent != nil && header.Time < parent.Time+CalcProducerDelay(number, succession, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||||
// header for running the transactions on top.
|
// header for running the transactions on top.
|
||||||
func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||||
|
|
@ -648,8 +674,10 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentSigner := *c.authorizedSigner.Load()
|
||||||
|
|
||||||
// Set the correct difficulty
|
// Set the correct difficulty
|
||||||
header.Difficulty = new(big.Int).SetUint64(snap.Difficulty(c.signer))
|
header.Difficulty = new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, currentSigner.signer))
|
||||||
|
|
||||||
// Ensure the extra data has all it's components
|
// Ensure the extra data has all it's components
|
||||||
if len(header.Extra) < extraVanity {
|
if len(header.Extra) < extraVanity {
|
||||||
|
|
@ -659,10 +687,10 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
|
||||||
header.Extra = header.Extra[:extraVanity]
|
header.Extra = header.Extra[:extraVanity]
|
||||||
|
|
||||||
// get validator set if number
|
// get validator set if number
|
||||||
if IsSprintStart(number+1, c.config.Sprint) {
|
if IsSprintStart(number+1, c.config.CalculateSprint(number)) {
|
||||||
newValidators, err := c.spanner.GetCurrentValidators(header.ParentHash, number+1)
|
newValidators, err := c.spanner.GetCurrentValidators(context.Background(), header.ParentHash, number+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("unknown validators")
|
return errUnknownValidators
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort validator by address
|
// sort validator by address
|
||||||
|
|
@ -687,8 +715,8 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
|
||||||
|
|
||||||
var succession int
|
var succession int
|
||||||
// if signer is not empty
|
// if signer is not empty
|
||||||
if c.signer != (common.Address{}) {
|
if currentSigner.signer != (common.Address{}) {
|
||||||
succession, err = snap.GetSignerSuccessionNumber(c.signer)
|
succession, err = snap.GetSignerSuccessionNumber(currentSigner.signer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -704,24 +732,26 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
|
||||||
|
|
||||||
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
||||||
// rewards given.
|
// rewards given.
|
||||||
func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
|
func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, _ []*types.Transaction, _ []*types.Header) {
|
||||||
stateSyncData := []*types.StateSyncData{}
|
var (
|
||||||
|
stateSyncData []*types.StateSyncData
|
||||||
var err error
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
headerNumber := header.Number.Uint64()
|
headerNumber := header.Number.Uint64()
|
||||||
|
|
||||||
if headerNumber%c.config.Sprint == 0 {
|
if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) {
|
||||||
|
ctx := context.Background()
|
||||||
cx := statefull.ChainContext{Chain: chain, Bor: c}
|
cx := statefull.ChainContext{Chain: chain, Bor: c}
|
||||||
// check and commit span
|
// check and commit span
|
||||||
if err := c.checkAndCommitSpan(state, header, cx); err != nil {
|
if err := c.checkAndCommitSpan(ctx, state, header, cx); err != nil {
|
||||||
log.Error("Error while committing span", "error", err)
|
log.Error("Error while committing span", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.HeimdallClient != nil {
|
if c.HeimdallClient != nil {
|
||||||
// commit statees
|
// commit states
|
||||||
stateSyncData, err = c.CommitStates(state, header, cx)
|
stateSyncData, err = c.CommitStates(ctx, state, header, cx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error while committing states", "error", err)
|
log.Error("Error while committing states", "error", err)
|
||||||
return
|
return
|
||||||
|
|
@ -763,7 +793,7 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
|
||||||
if blockNumber == strconv.FormatUint(headerNumber, 10) {
|
if blockNumber == strconv.FormatUint(headerNumber, 10) {
|
||||||
allocs, err := decodeGenesisAlloc(genesisAlloc)
|
allocs, err := decodeGenesisAlloc(genesisAlloc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decode genesis alloc: %v", err)
|
return fmt.Errorf("failed to decode genesis alloc: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for addr, account := range allocs {
|
for addr, account := range allocs {
|
||||||
|
|
@ -778,24 +808,35 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
|
||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
||||||
// nor block rewards given, and returns the final block.
|
// nor block rewards given, and returns the final block.
|
||||||
func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (c *Bor) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
|
finalizeCtx, finalizeSpan := tracing.StartSpan(ctx, "bor.FinalizeAndAssemble")
|
||||||
|
defer tracing.EndSpan(finalizeSpan)
|
||||||
|
|
||||||
stateSyncData := []*types.StateSyncData{}
|
stateSyncData := []*types.StateSyncData{}
|
||||||
|
|
||||||
headerNumber := header.Number.Uint64()
|
headerNumber := header.Number.Uint64()
|
||||||
|
|
||||||
if headerNumber%c.config.Sprint == 0 {
|
var err error
|
||||||
|
|
||||||
|
if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) {
|
||||||
cx := statefull.ChainContext{Chain: chain, Bor: c}
|
cx := statefull.ChainContext{Chain: chain, Bor: c}
|
||||||
|
|
||||||
// check and commit span
|
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
|
||||||
err := c.checkAndCommitSpan(state, header, cx)
|
// check and commit span
|
||||||
|
err = c.checkAndCommitSpan(finalizeCtx, state, header, cx)
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error while committing span", "error", err)
|
log.Error("Error while committing span", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.HeimdallClient != nil {
|
if c.HeimdallClient != nil {
|
||||||
// commit states
|
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
|
||||||
stateSyncData, err = c.CommitStates(state, header, cx)
|
// commit states
|
||||||
|
stateSyncData, err = c.CommitStates(finalizeCtx, state, header, cx)
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error while committing states", "error", err)
|
log.Error("Error while committing states", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -803,13 +844,21 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.changeContractCodeIfNeeded(headerNumber, state); err != nil {
|
tracing.Exec(finalizeCtx, "bor.changeContractCodeIfNeeded", func(ctx context.Context, span trace.Span) {
|
||||||
|
err = c.changeContractCodeIfNeeded(headerNumber, state)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
log.Error("Error changing contract code", "error", err)
|
log.Error("Error changing contract code", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// No block rewards in PoA, so the state remains as is and uncles are dropped
|
// No block rewards in PoA, so the state remains as it is
|
||||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
tracing.Exec(finalizeCtx, "bor.IntermediateRoot", func(ctx context.Context, span trace.Span) {
|
||||||
|
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Uncles are dropped
|
||||||
header.UncleHash = types.CalcUncleHash(nil)
|
header.UncleHash = types.CalcUncleHash(nil)
|
||||||
|
|
||||||
// Assemble block
|
// Assemble block
|
||||||
|
|
@ -819,23 +868,41 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
|
||||||
bc := chain.(core.BorStateSyncer)
|
bc := chain.(core.BorStateSyncer)
|
||||||
bc.SetStateSync(stateSyncData)
|
bc.SetStateSync(stateSyncData)
|
||||||
|
|
||||||
|
tracing.SetAttributes(
|
||||||
|
finalizeSpan,
|
||||||
|
attribute.Int("number", int(header.Number.Int64())),
|
||||||
|
attribute.String("hash", header.Hash().String()),
|
||||||
|
attribute.Int("number of txs", len(txs)),
|
||||||
|
attribute.Int("gas used", int(block.GasUsed())),
|
||||||
|
)
|
||||||
|
|
||||||
// return the final block for sealing
|
// return the final block for sealing
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorize injects a private key into the consensus engine to mint new blocks
|
// Authorize injects a private key into the consensus engine to mint new blocks
|
||||||
// with.
|
// with.
|
||||||
func (c *Bor) Authorize(signer common.Address, signFn SignerFn) {
|
func (c *Bor) Authorize(currentSigner common.Address, signFn SignerFn) {
|
||||||
c.lock.Lock()
|
c.authorizedSigner.Store(&signer{
|
||||||
defer c.lock.Unlock()
|
signer: currentSigner,
|
||||||
|
signFn: signFn,
|
||||||
c.signer = signer
|
})
|
||||||
c.signFn = signFn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seal implements consensus.Engine, attempting to create a sealed block using
|
// Seal implements consensus.Engine, attempting to create a sealed block using
|
||||||
// the local signing credentials.
|
// the local signing credentials.
|
||||||
func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
func (c *Bor) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
||||||
|
_, sealSpan := tracing.StartSpan(ctx, "bor.Seal")
|
||||||
|
|
||||||
|
var endSpan bool = true
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Only end span in case of early-returns/errors
|
||||||
|
if endSpan {
|
||||||
|
tracing.EndSpan(sealSpan)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
// Sealing the genesis block is not supported
|
// Sealing the genesis block is not supported
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
|
|
@ -847,10 +914,9 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
||||||
log.Info("Sealing paused, waiting for transactions")
|
log.Info("Sealing paused, waiting for transactions")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't hold the signer fields for the entire sealing procedure
|
// Don't hold the signer fields for the entire sealing procedure
|
||||||
c.lock.RLock()
|
currentSigner := *c.authorizedSigner.Load()
|
||||||
signer, signFn := c.signer, c.signFn
|
|
||||||
c.lock.RUnlock()
|
|
||||||
|
|
||||||
snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
|
snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -858,12 +924,12 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bail out if we're unauthorized to sign a block
|
// Bail out if we're unauthorized to sign a block
|
||||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
if !snap.ValidatorSet.HasAddress(currentSigner.signer) {
|
||||||
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
|
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
|
||||||
return &UnauthorizedSignerError{number - 1, signer.Bytes()}
|
return &UnauthorizedSignerError{number - 1, currentSigner.signer.Bytes()}
|
||||||
}
|
}
|
||||||
|
|
||||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
successionNumber, err := snap.GetSignerSuccessionNumber(currentSigner.signer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -874,7 +940,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
||||||
wiggle := time.Duration(successionNumber) * time.Duration(c.config.CalculateBackupMultiplier(number)) * time.Second
|
wiggle := time.Duration(successionNumber) * time.Duration(c.config.CalculateBackupMultiplier(number)) * time.Second
|
||||||
|
|
||||||
// Sign all the things!
|
// Sign all the things!
|
||||||
err = Sign(signFn, signer, header, c.config)
|
err = Sign(currentSigner.signFn, currentSigner.signer, header, c.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -882,7 +948,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
||||||
// Wait until sealing is terminated or delay timeout.
|
// Wait until sealing is terminated or delay timeout.
|
||||||
log.Info("Waiting for slot to sign and propagate", "number", number, "hash", header.Hash, "delay-in-sec", uint(delay), "delay", common.PrettyDuration(delay))
|
log.Info("Waiting for slot to sign and propagate", "number", number, "hash", header.Hash, "delay-in-sec", uint(delay), "delay", common.PrettyDuration(delay))
|
||||||
|
|
||||||
go func() {
|
go func(sealSpan trace.Span) {
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
log.Debug("Discarding sealing operation for block", "number", number)
|
log.Debug("Discarding sealing operation for block", "number", number)
|
||||||
|
|
@ -905,13 +971,27 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
||||||
"delay", delay,
|
"delay", delay,
|
||||||
"headerDifficulty", header.Difficulty,
|
"headerDifficulty", header.Difficulty,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
tracing.SetAttributes(
|
||||||
|
sealSpan,
|
||||||
|
attribute.Int("number", int(number)),
|
||||||
|
attribute.String("hash", header.Hash().String()),
|
||||||
|
attribute.Int("delay", int(delay.Milliseconds())),
|
||||||
|
attribute.Int("wiggle", int(wiggle.Milliseconds())),
|
||||||
|
attribute.Bool("out-of-turn", wiggle > 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
tracing.EndSpan(sealSpan)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case results <- block.WithSeal(header):
|
case results <- block.WithSeal(header):
|
||||||
default:
|
default:
|
||||||
log.Warn("Sealing result was not read by miner", "number", number, "sealhash", SealHash(header, c.config))
|
log.Warn("Sealing result was not read by miner", "number", number, "sealhash", SealHash(header, c.config))
|
||||||
}
|
}
|
||||||
}()
|
}(sealSpan)
|
||||||
|
|
||||||
|
// Set the endSpan flag to false, as the go routine will handle it
|
||||||
|
endSpan = false
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -930,13 +1010,13 @@ func Sign(signFn SignerFn, signer common.Address, header *types.Header, c *param
|
||||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
|
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
|
||||||
// that a new block should have based on the previous blocks in the chain and the
|
// that a new block should have based on the previous blocks in the chain and the
|
||||||
// current signer.
|
// current signer.
|
||||||
func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, _ uint64, parent *types.Header) *big.Int {
|
||||||
snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
|
snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return new(big.Int).SetUint64(snap.Difficulty(c.signer))
|
return new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, c.authorizedSigner.Load().signer))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SealHash returns the hash of a block prior to it being sealed.
|
// SealHash returns the hash of a block prior to it being sealed.
|
||||||
|
|
@ -967,37 +1047,38 @@ func (c *Bor) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Bor) checkAndCommitSpan(
|
func (c *Bor) checkAndCommitSpan(
|
||||||
|
ctx context.Context,
|
||||||
state *state.StateDB,
|
state *state.StateDB,
|
||||||
header *types.Header,
|
header *types.Header,
|
||||||
chain core.ChainContext,
|
chain core.ChainContext,
|
||||||
) error {
|
) error {
|
||||||
headerNumber := header.Number.Uint64()
|
headerNumber := header.Number.Uint64()
|
||||||
|
|
||||||
span, err := c.spanner.GetCurrentSpan(header.ParentHash)
|
span, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.needToCommitSpan(span, headerNumber) {
|
if c.needToCommitSpan(span, headerNumber) {
|
||||||
return c.FetchAndCommitSpan(span.ID+1, state, header, chain)
|
return c.FetchAndCommitSpan(ctx, span.ID+1, state, header, chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Bor) needToCommitSpan(span *span.Span, headerNumber uint64) bool {
|
func (c *Bor) needToCommitSpan(currentSpan *span.Span, headerNumber uint64) bool {
|
||||||
// if span is nil
|
// if span is nil
|
||||||
if span == nil {
|
if currentSpan == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check span is not set initially
|
// check span is not set initially
|
||||||
if span.EndBlock == 0 {
|
if currentSpan.EndBlock == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// if current block is first block of last sprint in current span
|
// if current block is first block of last sprint in current span
|
||||||
if span.EndBlock > c.config.Sprint && span.EndBlock-c.config.Sprint+1 == headerNumber {
|
if currentSpan.EndBlock > c.config.CalculateSprint(headerNumber) && currentSpan.EndBlock-c.config.CalculateSprint(headerNumber)+1 == headerNumber {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1005,6 +1086,7 @@ func (c *Bor) needToCommitSpan(span *span.Span, headerNumber uint64) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Bor) FetchAndCommitSpan(
|
func (c *Bor) FetchAndCommitSpan(
|
||||||
|
ctx context.Context,
|
||||||
newSpanID uint64,
|
newSpanID uint64,
|
||||||
state *state.StateDB,
|
state *state.StateDB,
|
||||||
header *types.Header,
|
header *types.Header,
|
||||||
|
|
@ -1014,14 +1096,14 @@ func (c *Bor) FetchAndCommitSpan(
|
||||||
|
|
||||||
if c.HeimdallClient == nil {
|
if c.HeimdallClient == nil {
|
||||||
// fixme: move to a new mock or fake and remove c.HeimdallClient completely
|
// fixme: move to a new mock or fake and remove c.HeimdallClient completely
|
||||||
s, err := c.getNextHeimdallSpanForTest(newSpanID, header, chain)
|
s, err := c.getNextHeimdallSpanForTest(ctx, newSpanID, header, chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
heimdallSpan = *s
|
heimdallSpan = *s
|
||||||
} else {
|
} else {
|
||||||
response, err := c.HeimdallClient.Span(newSpanID)
|
response, err := c.HeimdallClient.Span(ctx, newSpanID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1038,16 +1120,17 @@ func (c *Bor) FetchAndCommitSpan(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.spanner.CommitSpan(heimdallSpan, state, header, chain)
|
return c.spanner.CommitSpan(ctx, heimdallSpan, state, header, chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitStates commit states
|
// CommitStates commit states
|
||||||
func (c *Bor) CommitStates(
|
func (c *Bor) CommitStates(
|
||||||
|
ctx context.Context,
|
||||||
state *state.StateDB,
|
state *state.StateDB,
|
||||||
header *types.Header,
|
header *types.Header,
|
||||||
chain statefull.ChainContext,
|
chain statefull.ChainContext,
|
||||||
) ([]*types.StateSyncData, error) {
|
) ([]*types.StateSyncData, error) {
|
||||||
stateSyncs := make([]*types.StateSyncData, 0)
|
fetchStart := time.Now()
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
|
|
||||||
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
|
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
|
||||||
|
|
@ -1055,7 +1138,7 @@ func (c *Bor) CommitStates(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.Sprint).Time), 0)
|
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
|
||||||
lastStateID := _lastStateID.Uint64()
|
lastStateID := _lastStateID.Uint64()
|
||||||
|
|
||||||
log.Info(
|
log.Info(
|
||||||
|
|
@ -1063,7 +1146,7 @@ func (c *Bor) CommitStates(
|
||||||
"fromID", lastStateID+1,
|
"fromID", lastStateID+1,
|
||||||
"to", to.Format(time.RFC3339))
|
"to", to.Format(time.RFC3339))
|
||||||
|
|
||||||
eventRecords, err := c.HeimdallClient.StateSyncEvents(lastStateID+1, to.Unix())
|
eventRecords, err := c.HeimdallClient.StateSyncEvents(ctx, lastStateID+1, to.Unix())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error occurred when fetching state sync events", "stateID", lastStateID+1, "error", err)
|
log.Error("Error occurred when fetching state sync events", "stateID", lastStateID+1, "error", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1074,16 +1157,20 @@ func (c *Bor) CommitStates(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchTime := time.Since(fetchStart)
|
||||||
|
processStart := time.Now()
|
||||||
totalGas := 0 /// limit on gas for state sync per block
|
totalGas := 0 /// limit on gas for state sync per block
|
||||||
|
|
||||||
chainID := c.chainConfig.ChainID.String()
|
chainID := c.chainConfig.ChainID.String()
|
||||||
|
stateSyncs := make([]*types.StateSyncData, len(eventRecords))
|
||||||
|
|
||||||
|
var gasUsed uint64
|
||||||
|
|
||||||
for _, eventRecord := range eventRecords {
|
for _, eventRecord := range eventRecords {
|
||||||
if eventRecord.ID <= lastStateID {
|
if eventRecord.ID <= lastStateID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
|
if err = validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
|
||||||
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error())
|
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -1097,7 +1184,10 @@ func (c *Bor) CommitStates(
|
||||||
|
|
||||||
stateSyncs = append(stateSyncs, &stateData)
|
stateSyncs = append(stateSyncs, &stateData)
|
||||||
|
|
||||||
gasUsed, err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain)
|
// we expect that this call MUST emit an event, otherwise we wouldn't make a receipt
|
||||||
|
// if the receiver address is not a contract then we'll skip the most of the execution and emitting an event as well
|
||||||
|
// https://github.com/maticnetwork/genesis-contracts/blob/master/contracts/StateReceiver.sol#L27
|
||||||
|
gasUsed, err = c.GenesisContractsClient.CommitState(eventRecord, state, header, chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1107,7 +1197,9 @@ func (c *Bor) CommitStates(
|
||||||
lastStateID++
|
lastStateID++
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("StateSyncData", "Gas", totalGas, "Block-number", number, "LastStateID", lastStateID, "TotalRecords", len(eventRecords))
|
processTime := time.Since(processStart)
|
||||||
|
|
||||||
|
log.Info("StateSyncData", "gas", totalGas, "number", number, "lastStateID", lastStateID, "total records", len(eventRecords), "fetch time", int(fetchTime.Milliseconds()), "process time", int(processTime.Milliseconds()))
|
||||||
|
|
||||||
return stateSyncs, nil
|
return stateSyncs, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1125,8 +1217,8 @@ func (c *Bor) SetHeimdallClient(h IHeimdallClient) {
|
||||||
c.HeimdallClient = h
|
c.HeimdallClient = h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
|
func (c *Bor) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
|
||||||
return c.spanner.GetCurrentValidators(headerHash, blockNumber)
|
return c.spanner.GetCurrentValidators(ctx, headerHash, blockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -1134,13 +1226,14 @@ func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) (
|
||||||
//
|
//
|
||||||
|
|
||||||
func (c *Bor) getNextHeimdallSpanForTest(
|
func (c *Bor) getNextHeimdallSpanForTest(
|
||||||
|
ctx context.Context,
|
||||||
newSpanID uint64,
|
newSpanID uint64,
|
||||||
header *types.Header,
|
header *types.Header,
|
||||||
chain core.ChainContext,
|
chain core.ChainContext,
|
||||||
) (*span.HeimdallSpan, error) {
|
) (*span.HeimdallSpan, error) {
|
||||||
headerNumber := header.Number.Uint64()
|
headerNumber := header.Number.Uint64()
|
||||||
|
|
||||||
spanBor, err := c.spanner.GetCurrentSpan(header.ParentHash)
|
spanBor, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1161,7 +1254,7 @@ func (c *Bor) getNextHeimdallSpanForTest(
|
||||||
spanBor.StartBlock = spanBor.EndBlock + 1
|
spanBor.StartBlock = spanBor.EndBlock + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
spanBor.EndBlock = spanBor.StartBlock + (100 * c.config.Sprint) - 1
|
spanBor.EndBlock = spanBor.StartBlock + (100 * c.config.CalculateSprint(headerNumber)) - 1
|
||||||
|
|
||||||
selectedProducers := make([]valset.Validator, len(snap.ValidatorSet.Validators))
|
selectedProducers := make([]valset.Validator, len(snap.ValidatorSet.Validators))
|
||||||
for i, v := range snap.ValidatorSet.Validators {
|
for i, v := range snap.ValidatorSet.Validators {
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil" //nolint:typecheck
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -23,7 +23,9 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
|
|
||||||
b := &Bor{
|
b := &Bor{
|
||||||
config: ¶ms.BorConfig{
|
config: ¶ms.BorConfig{
|
||||||
Sprint: 10, // skip sprint transactions in sprint
|
Sprint: map[string]uint64{
|
||||||
|
"0": 10,
|
||||||
|
}, // skip sprint transactions in sprint
|
||||||
BlockAlloc: map[string]interface{}{
|
BlockAlloc: map[string]interface{}{
|
||||||
// write as interface since that is how it is decoded in genesis
|
// write as interface since that is how it is decoded in genesis
|
||||||
"2": map[string]interface{}{
|
"2": map[string]interface{}{
|
||||||
|
|
@ -55,11 +57,11 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
genesis := genspec.MustCommit(db)
|
genesis := genspec.MustCommit(db)
|
||||||
|
|
||||||
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
|
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
config := params.ChainConfig{}
|
config := params.ChainConfig{}
|
||||||
chain, err := core.NewBlockChain(db, nil, &config, b, vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, nil, &config, b, vm.Config{}, nil, nil, nil)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
addBlock := func(root common.Hash, num int64) (common.Hash, *state.StateDB) {
|
addBlock := func(root common.Hash, num int64) (common.Hash, *state.StateDB) {
|
||||||
h := &types.Header{
|
h := &types.Header{
|
||||||
|
|
@ -70,37 +72,37 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
|
|
||||||
// write state to database
|
// write state to database
|
||||||
root, err := statedb.Commit(false)
|
root, err := statedb.Commit(false)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NoError(t, statedb.Database().TrieDB().Commit(root, true, nil))
|
require.NoError(t, statedb.Database().TrieDB().Commit(root, true, nil))
|
||||||
|
|
||||||
statedb, err := state.New(h.Root, state.NewDatabase(db), nil)
|
statedb, err := state.New(h.Root, state.NewDatabase(db), nil)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return root, statedb
|
return root, statedb
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
|
||||||
|
|
||||||
root := genesis.Root()
|
root := genesis.Root()
|
||||||
|
|
||||||
// code does not change
|
// code does not change
|
||||||
root, statedb = addBlock(root, 1)
|
root, statedb = addBlock(root, 1)
|
||||||
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
|
||||||
|
|
||||||
// code changes 1st time
|
// code changes 1st time
|
||||||
root, statedb = addBlock(root, 2)
|
root, statedb = addBlock(root, 2)
|
||||||
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
||||||
|
|
||||||
// code same as 1st change
|
// code same as 1st change
|
||||||
root, statedb = addBlock(root, 3)
|
root, statedb = addBlock(root, 3)
|
||||||
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
||||||
|
|
||||||
// code changes 2nd time
|
// code changes 2nd time
|
||||||
_, statedb = addBlock(root, 4)
|
_, statedb = addBlock(root, 4)
|
||||||
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3})
|
||||||
|
|
||||||
// make sure balance change DOES NOT take effect
|
// make sure balance change DOES NOT take effect
|
||||||
assert.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeSigHeaderJaipur(t *testing.T) {
|
func TestEncodeSigHeaderJaipur(t *testing.T) {
|
||||||
|
|
@ -123,20 +125,20 @@ func TestEncodeSigHeaderJaipur(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Jaipur NOT enabled and BaseFee not set
|
// Jaipur NOT enabled and BaseFee not set
|
||||||
hash := SealHash(h, ¶ms.BorConfig{JaipurBlock: 10})
|
hash := SealHash(h, ¶ms.BorConfig{JaipurBlock: big.NewInt(10)})
|
||||||
assert.Equal(t, hash, hashWithoutBaseFee)
|
require.Equal(t, hash, hashWithoutBaseFee)
|
||||||
|
|
||||||
// Jaipur enabled (Jaipur=0) and BaseFee not set
|
// Jaipur enabled (Jaipur=0) and BaseFee not set
|
||||||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 0})
|
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: common.Big0})
|
||||||
assert.Equal(t, hash, hashWithoutBaseFee)
|
require.Equal(t, hash, hashWithoutBaseFee)
|
||||||
|
|
||||||
h.BaseFee = big.NewInt(2)
|
h.BaseFee = big.NewInt(2)
|
||||||
|
|
||||||
// Jaipur enabled (Jaipur=Header block) and BaseFee set
|
// Jaipur enabled (Jaipur=Header block) and BaseFee set
|
||||||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 1})
|
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: common.Big1})
|
||||||
assert.Equal(t, hash, hashWithBaseFee)
|
require.Equal(t, hash, hashWithBaseFee)
|
||||||
|
|
||||||
// Jaipur NOT enabled and BaseFee set
|
// Jaipur NOT enabled and BaseFee set
|
||||||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 10})
|
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: big.NewInt(10)})
|
||||||
assert.Equal(t, hash, hashWithoutBaseFee)
|
require.Equal(t, hash, hashWithoutBaseFee)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ func (gc *GenesisContractsClient) CommitState(
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := statefull.GetSystemMessage(common.HexToAddress(gc.StateReceiverContract), data)
|
msg := statefull.GetSystemMessage(common.HexToAddress(gc.StateReceiverContract), data)
|
||||||
gasUsed, err := statefull.ApplyMessage(msg, state, header, gc.chainConfig, chCtx)
|
gasUsed, err := statefull.ApplyMessage(context.Background(), msg, state, header, gc.chainConfig, chCtx)
|
||||||
|
|
||||||
// Logging event log with time and individual gasUsed
|
// Logging event log with time and individual gasUsed
|
||||||
log.Info("→ committing new state", "eventRecord", event.String(gasUsed))
|
log.Info("→ committing new state", "eventRecord", event.String(gasUsed))
|
||||||
|
|
@ -102,7 +102,8 @@ func (gc *GenesisContractsClient) CommitState(
|
||||||
|
|
||||||
func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int, error) {
|
func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int, error) {
|
||||||
blockNr := rpc.BlockNumber(snapshotNumber)
|
blockNr := rpc.BlockNumber(snapshotNumber)
|
||||||
method := "lastStateId"
|
|
||||||
|
const method = "lastStateId"
|
||||||
|
|
||||||
data, err := gc.stateReceiverABI.Pack(method)
|
data, err := gc.stateReceiverABI.Pack(method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package bor
|
package bor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||||
|
|
@ -8,8 +10,9 @@ import (
|
||||||
|
|
||||||
//go:generate mockgen -destination=../../tests/bor/mocks/IHeimdallClient.go -package=mocks . IHeimdallClient
|
//go:generate mockgen -destination=../../tests/bor/mocks/IHeimdallClient.go -package=mocks . IHeimdallClient
|
||||||
type IHeimdallClient interface {
|
type IHeimdallClient interface {
|
||||||
StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
|
StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
|
||||||
Span(spanID uint64) (*span.HeimdallSpan, error)
|
Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error)
|
||||||
FetchLatestCheckpoint() (*checkpoint.Checkpoint, error)
|
FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
|
||||||
|
FetchCheckpointCount(ctx context.Context) (int64, error)
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,12 @@ type CheckpointResponse struct {
|
||||||
Height string `json:"height"`
|
Height string `json:"height"`
|
||||||
Result Checkpoint `json:"result"`
|
Result Checkpoint `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CheckpointCount struct {
|
||||||
|
Result int64 `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckpointCountResponse struct {
|
||||||
|
Height string `json:"height"`
|
||||||
|
Result CheckpointCount `json:"result"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -15,14 +15,20 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errShutdownDetected is returned if a shutdown was detected
|
var (
|
||||||
var errShutdownDetected = errors.New("shutdown detected")
|
// ErrShutdownDetected is returned if a shutdown was detected
|
||||||
|
ErrShutdownDetected = errors.New("shutdown detected")
|
||||||
|
ErrNoResponse = errors.New("got a nil response")
|
||||||
|
ErrNotSuccessfulResponse = errors.New("error while fetching data from Heimdall")
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
stateFetchLimit = 50
|
stateFetchLimit = 50
|
||||||
apiHeimdallTimeout = 5 * time.Second
|
apiHeimdallTimeout = 5 * time.Second
|
||||||
|
retryCall = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type StateSyncEventsResponse struct {
|
type StateSyncEventsResponse struct {
|
||||||
|
|
@ -41,6 +47,12 @@ type HeimdallClient struct {
|
||||||
closeCh chan struct{}
|
closeCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
client http.Client
|
||||||
|
url *url.URL
|
||||||
|
start time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func NewHeimdallClient(urlString string) *HeimdallClient {
|
func NewHeimdallClient(urlString string) *HeimdallClient {
|
||||||
return &HeimdallClient{
|
return &HeimdallClient{
|
||||||
urlString: urlString,
|
urlString: urlString,
|
||||||
|
|
@ -54,12 +66,13 @@ func NewHeimdallClient(urlString string) *HeimdallClient {
|
||||||
const (
|
const (
|
||||||
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
|
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
|
||||||
fetchStateSyncEventsPath = "clerk/event-record/list"
|
fetchStateSyncEventsPath = "clerk/event-record/list"
|
||||||
fetchLatestCheckpoint = "/checkpoints/latest"
|
fetchCheckpoint = "/checkpoints/%s"
|
||||||
|
fetchCheckpointCount = "/checkpoints/count"
|
||||||
|
|
||||||
fetchSpanFormat = "bor/span/%d"
|
fetchSpanFormat = "bor/span/%d"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
|
func (h *HeimdallClient) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
|
||||||
eventRecords := make([]*clerk.EventRecordWithTime, 0)
|
eventRecords := make([]*clerk.EventRecordWithTime, 0)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
@ -70,7 +83,9 @@ func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.Even
|
||||||
|
|
||||||
log.Info("Fetching state sync events", "queryParams", url.RawQuery)
|
log.Info("Fetching state sync events", "queryParams", url.RawQuery)
|
||||||
|
|
||||||
response, err := FetchWithRetry[StateSyncEventsResponse](h.client, url, h.closeCh)
|
ctx = withRequestType(ctx, stateSyncRequest)
|
||||||
|
|
||||||
|
response, err := FetchWithRetry[StateSyncEventsResponse](ctx, h.client, url, h.closeCh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -96,13 +111,15 @@ func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.Even
|
||||||
return eventRecords, nil
|
return eventRecords, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HeimdallClient) Span(spanID uint64) (*span.HeimdallSpan, error) {
|
func (h *HeimdallClient) Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error) {
|
||||||
url, err := spanURL(h.urlString, spanID)
|
url, err := spanURL(h.urlString, spanID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := FetchWithRetry[SpanResponse](h.client, url, h.closeCh)
|
ctx = withRequestType(ctx, spanRequest)
|
||||||
|
|
||||||
|
response, err := FetchWithRetry[SpanResponse](ctx, h.client, url, h.closeCh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -110,14 +127,16 @@ func (h *HeimdallClient) Span(spanID uint64) (*span.HeimdallSpan, error) {
|
||||||
return &response.Result, nil
|
return &response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchLatestCheckpoint fetches the latest bor submitted checkpoint from heimdall
|
// FetchCheckpoint fetches the checkpoint from heimdall
|
||||||
func (h *HeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error) {
|
func (h *HeimdallClient) FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error) {
|
||||||
url, err := latestCheckpointURL(h.urlString)
|
url, err := checkpointURL(h.urlString, number)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := FetchWithRetry[checkpoint.CheckpointResponse](h.client, url, h.closeCh)
|
ctx = withRequestType(ctx, checkpointRequest)
|
||||||
|
|
||||||
|
response, err := FetchWithRetry[checkpoint.CheckpointResponse](ctx, h.client, url, h.closeCh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -125,59 +144,107 @@ func (h *HeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error)
|
||||||
return &response.Result, nil
|
return &response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FetchCheckpointCount fetches the checkpoint count from heimdall
|
||||||
|
func (h *HeimdallClient) FetchCheckpointCount(ctx context.Context) (int64, error) {
|
||||||
|
url, err := checkpointCountURL(h.urlString)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = withRequestType(ctx, checkpointCountRequest)
|
||||||
|
|
||||||
|
response, err := FetchWithRetry[checkpoint.CheckpointCountResponse](ctx, h.client, url, h.closeCh)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FetchWithRetry returns data from heimdall with retry
|
// FetchWithRetry returns data from heimdall with retry
|
||||||
func FetchWithRetry[T any](client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
|
func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
|
||||||
// attempt counter
|
|
||||||
attempt := 1
|
|
||||||
result := new(T)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), apiHeimdallTimeout)
|
|
||||||
|
|
||||||
// request data once
|
// request data once
|
||||||
body, err := internalFetch(ctx, client, url)
|
request := &Request{client: client, url: url, start: time.Now()}
|
||||||
|
result, err := Fetch[T](ctx, request)
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err == nil && body != nil {
|
|
||||||
err = json.Unmarshal(body, result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attempt counter
|
||||||
|
attempt := 1
|
||||||
|
|
||||||
|
log.Warn("an error while trying fetching from Heimdall", "attempt", attempt, "error", err)
|
||||||
|
|
||||||
// create a new ticker for retrying the request
|
// create a new ticker for retrying the request
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(retryCall)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
const logEach = 5
|
||||||
|
|
||||||
|
retryLoop:
|
||||||
for {
|
for {
|
||||||
log.Info("Retrying again in 5 seconds to fetch data from Heimdall", "path", url.Path, "attempt", attempt)
|
log.Info("Retrying again in 5 seconds to fetch data from Heimdall", "path", url.Path, "attempt", attempt)
|
||||||
|
|
||||||
attempt++
|
attempt++
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Debug("Shutdown detected, terminating request by context.Done")
|
||||||
|
|
||||||
|
return nil, ctx.Err()
|
||||||
case <-closeCh:
|
case <-closeCh:
|
||||||
log.Debug("Shutdown detected, terminating request")
|
log.Debug("Shutdown detected, terminating request by closing")
|
||||||
|
|
||||||
return nil, errShutdownDetected
|
return nil, ErrShutdownDetected
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), apiHeimdallTimeout)
|
request = &Request{client: client, url: url, start: time.Now()}
|
||||||
|
result, err = Fetch[T](ctx, request)
|
||||||
|
|
||||||
body, err = internalFetch(ctx, client, url)
|
if err != nil {
|
||||||
|
if attempt%logEach == 0 {
|
||||||
cancel()
|
log.Warn("an error while trying fetching from Heimdall", "attempt", attempt, "error", err)
|
||||||
|
|
||||||
if err == nil && body != nil {
|
|
||||||
err = json.Unmarshal(body, result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
continue retryLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch returns data from heimdall
|
||||||
|
func Fetch[T any](ctx context.Context, request *Request) (*T, error) {
|
||||||
|
isSuccessful := false
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if metrics.EnabledExpensive {
|
||||||
|
sendMetrics(ctx, request.start, isSuccessful)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
result := new(T)
|
||||||
|
|
||||||
|
body, err := internalFetchWithTimeout(ctx, request.client, request.url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if body == nil {
|
||||||
|
return nil, ErrNoResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isSuccessful = true
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func spanURL(urlString string, spanID uint64) (*url.URL, error) {
|
func spanURL(urlString string, spanID uint64) (*url.URL, error) {
|
||||||
return makeURL(urlString, fmt.Sprintf(fetchSpanFormat, spanID), "")
|
return makeURL(urlString, fmt.Sprintf(fetchSpanFormat, spanID), "")
|
||||||
}
|
}
|
||||||
|
|
@ -188,8 +255,19 @@ func stateSyncURL(urlString string, fromID uint64, to int64) (*url.URL, error) {
|
||||||
return makeURL(urlString, fetchStateSyncEventsPath, queryParams)
|
return makeURL(urlString, fetchStateSyncEventsPath, queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func latestCheckpointURL(urlString string) (*url.URL, error) {
|
func checkpointURL(urlString string, number int64) (*url.URL, error) {
|
||||||
return makeURL(urlString, fetchLatestCheckpoint, "")
|
url := ""
|
||||||
|
if number == -1 {
|
||||||
|
url = fmt.Sprintf(fetchCheckpoint, "latest")
|
||||||
|
} else {
|
||||||
|
url = fmt.Sprintf(fetchCheckpoint, fmt.Sprint(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeURL(urlString, url, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkpointCountURL(urlString string) (*url.URL, error) {
|
||||||
|
return makeURL(urlString, fetchCheckpointCount, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
|
func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
|
||||||
|
|
@ -215,11 +293,12 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
// check status code
|
// check status code
|
||||||
if res.StatusCode != 200 && res.StatusCode != 204 {
|
if res.StatusCode != 200 && res.StatusCode != 204 {
|
||||||
return nil, fmt.Errorf("Error while fetching data from Heimdall")
|
return nil, fmt.Errorf("%w: response code %d", ErrNotSuccessfulResponse, res.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshall data from buffer
|
// unmarshall data from buffer
|
||||||
|
|
@ -228,7 +307,7 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// get response
|
// get response
|
||||||
body, err := ioutil.ReadAll(res.Body)
|
body, err := io.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -236,6 +315,14 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
|
||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func internalFetchWithTimeout(ctx context.Context, client http.Client, url *url.URL) ([]byte, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, apiHeimdallTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// request data once
|
||||||
|
return internalFetch(ctx, client, url)
|
||||||
|
}
|
||||||
|
|
||||||
// Close sends a signal to stop the running process
|
// Close sends a signal to stop the running process
|
||||||
func (h *HeimdallClient) Close() {
|
func (h *HeimdallClient) Close() {
|
||||||
close(h.closeCh)
|
close(h.closeCh)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,306 @@
|
||||||
package heimdall
|
package heimdall
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/network"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HttpHandlerFake defines the handler functions required to serve
|
||||||
|
// requests to the mock heimdal server for specific functions. Add more handlers
|
||||||
|
// according to requirements.
|
||||||
|
type HttpHandlerFake struct {
|
||||||
|
handleFetchCheckpoint http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HttpHandlerFake) GetCheckpointHandler() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.handleFetchCheckpoint.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateMockHeimdallServer(wg *sync.WaitGroup, port int, listener net.Listener, handler *HttpHandlerFake) (*http.Server, error) {
|
||||||
|
// Create a new server mux
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
// Create a route for fetching latest checkpoint
|
||||||
|
mux.HandleFunc("/checkpoints/latest", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
handler.GetCheckpointHandler()(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add other routes as per requirement
|
||||||
|
|
||||||
|
// Create the server with given port and mux
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: fmt.Sprintf("localhost:%d", port),
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the listener using the port and immediately consume it below
|
||||||
|
err := listener.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// always returns error. ErrServerClosed on graceful close
|
||||||
|
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
fmt.Printf("error in server.ListenAndServe(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return srv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFetchCheckpointFromMockHeimdall tests the heimdall client side logic
|
||||||
|
// to fetch checkpoints (latest for the scope of test) from a mock heimdall server.
|
||||||
|
// It can be used for debugging purpose (like response fields, marshalling/unmarshalling, etc).
|
||||||
|
func TestFetchCheckpointFromMockHeimdall(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create a wait group for sending across the mock server
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
// Initialize the fake handler and add a fake checkpoint handler function
|
||||||
|
handler := &HttpHandlerFake{}
|
||||||
|
handler.handleFetchCheckpoint = func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
err := json.NewEncoder(w).Encode(checkpoint.CheckpointResponse{
|
||||||
|
Height: "0",
|
||||||
|
Result: checkpoint.Checkpoint{
|
||||||
|
Proposer: common.Address{},
|
||||||
|
StartBlock: big.NewInt(0),
|
||||||
|
EndBlock: big.NewInt(512),
|
||||||
|
RootHash: common.Hash{},
|
||||||
|
BorChainID: "15001",
|
||||||
|
Timestamp: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500) // Return 500 Internal Server Error.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch available port
|
||||||
|
port, listener, err := network.FindAvailablePort()
|
||||||
|
require.NoError(t, err, "expect no error in finding available port")
|
||||||
|
|
||||||
|
// Create mock heimdall server and pass handler instance for setting up the routes
|
||||||
|
srv, err := CreateMockHeimdallServer(wg, port, listener, handler)
|
||||||
|
require.NoError(t, err, "expect no error in starting mock heimdall server")
|
||||||
|
|
||||||
|
// Create a new heimdall client and use same port for connection
|
||||||
|
client := NewHeimdallClient(fmt.Sprintf("http://localhost:%d", port))
|
||||||
|
_, err = client.FetchCheckpoint(context.Background(), -1)
|
||||||
|
require.NoError(t, err, "expect no error in fetching checkpoint")
|
||||||
|
|
||||||
|
// Shutdown the server
|
||||||
|
err = srv.Shutdown(context.TODO())
|
||||||
|
require.NoError(t, err, "expect no error in shutting down mock heimdall server")
|
||||||
|
|
||||||
|
// Wait for `wg.Done()` to be called in the mock server's routine.
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFetchShutdown tests the heimdall client side logic for context timeout and
|
||||||
|
// interrupt handling while fetching checkpoints (latest for the scope of test)
|
||||||
|
// from a mock heimdall server.
|
||||||
|
func TestFetchShutdown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create a wait group for sending across the mock server
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
// Initialize the fake handler and add a fake checkpoint handler function
|
||||||
|
handler := &HttpHandlerFake{}
|
||||||
|
|
||||||
|
// Case1 - Testing context timeout: Create delay in serving requests for simulating timeout. Add delay slightly
|
||||||
|
// greater than `retryDelay`. This should cause the request to timeout and trigger shutdown
|
||||||
|
// due to `ctx.Done()`. Expect context timeout error.
|
||||||
|
handler.handleFetchCheckpoint = func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
|
||||||
|
err := json.NewEncoder(w).Encode(checkpoint.CheckpointResponse{
|
||||||
|
Height: "0",
|
||||||
|
Result: checkpoint.Checkpoint{
|
||||||
|
Proposer: common.Address{},
|
||||||
|
StartBlock: big.NewInt(0),
|
||||||
|
EndBlock: big.NewInt(512),
|
||||||
|
RootHash: common.Hash{},
|
||||||
|
BorChainID: "15001",
|
||||||
|
Timestamp: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500) // Return 500 Internal Server Error.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch available port
|
||||||
|
port, listener, err := network.FindAvailablePort()
|
||||||
|
require.NoError(t, err, "expect no error in finding available port")
|
||||||
|
|
||||||
|
// Create mock heimdall server and pass handler instance for setting up the routes
|
||||||
|
srv, err := CreateMockHeimdallServer(wg, port, listener, handler)
|
||||||
|
require.NoError(t, err, "expect no error in starting mock heimdall server")
|
||||||
|
|
||||||
|
// Create a new heimdall client and use same port for connection
|
||||||
|
client := NewHeimdallClient(fmt.Sprintf("http://localhost:%d", port))
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
|
||||||
|
// Expect this to fail due to timeout
|
||||||
|
_, err = client.FetchCheckpoint(ctx, -1)
|
||||||
|
require.Equal(t, "context deadline exceeded", err.Error(), "expect the function error to be a context deadline exeeded error")
|
||||||
|
require.Equal(t, "context deadline exceeded", ctx.Err().Error(), "expect the ctx error to be a context deadline exeeded error")
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
// Case2 - Testing context cancellation. Pass a context with timeout to the request and
|
||||||
|
// cancel it before timeout. This should cause the request to timeout and trigger shutdown
|
||||||
|
// due to `ctx.Done()`. Expect context cancellation error.
|
||||||
|
handler.handleFetchCheckpoint = func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
w.WriteHeader(500) // Return 500 Internal Server Error.
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) // Use some high value for timeout
|
||||||
|
|
||||||
|
// Cancel the context after a delay until we make request
|
||||||
|
go func(cancel context.CancelFunc) {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
cancel()
|
||||||
|
}(cancel)
|
||||||
|
|
||||||
|
// Expect this to fail due to cancellation
|
||||||
|
_, err = client.FetchCheckpoint(ctx, -1)
|
||||||
|
require.Equal(t, "context canceled", err.Error(), "expect the function error to be a context cancelled error")
|
||||||
|
require.Equal(t, "context canceled", ctx.Err().Error(), "expect the ctx error to be a context cancelled error")
|
||||||
|
|
||||||
|
// Case3 - Testing interrupt: Closing the `closeCh` in heimdall client simulating interrupt. This
|
||||||
|
// should cause the request to fail and throw an error due to `<-closeCh` in fetchWithRetry.
|
||||||
|
// Expect shutdown detected error.
|
||||||
|
handler.handleFetchCheckpoint = func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(500) // Return 500 Internal Server Error.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the channel after a delay until we make request
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
close(client.closeCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Expect this to fail due to shutdown
|
||||||
|
_, err = client.FetchCheckpoint(context.Background(), -1)
|
||||||
|
require.Equal(t, ErrShutdownDetected.Error(), err.Error(), "expect the function error to be a shutdown detected error")
|
||||||
|
|
||||||
|
// Shutdown the server
|
||||||
|
err = srv.Shutdown(context.TODO())
|
||||||
|
require.NoError(t, err, "expect no error in shutting down mock heimdall server")
|
||||||
|
|
||||||
|
// Wait for `wg.Done()` to be called in the mock server's routine.
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestContext includes bunch of simple tests to verify the working of timeout
|
||||||
|
// based context and cancellation.
|
||||||
|
func TestContext(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel1 := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
|
||||||
|
// Case1: Done is not yet closed, so Err returns nil.
|
||||||
|
require.NoError(t, ctx.Err(), "expect nil error")
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
|
||||||
|
// Case2: Check if timeout is being handled
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Expect context deadline exceeded error
|
||||||
|
require.Equal(t, "context deadline exceeded", ctx.Err().Error(), "expect the ctx error to be a context deadline exceeded error")
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
// Case for safely exiting the tests
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(ctx, wg)
|
||||||
|
|
||||||
|
// Case3: Check normal case
|
||||||
|
ctx, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
|
||||||
|
go func(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Expect this to never occur, throw explicit error
|
||||||
|
errCh <- errors.New("unexpectecd call to `ctx.Done()`")
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
// Case for safely exiting the tests
|
||||||
|
errCh <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(ctx, wg)
|
||||||
|
|
||||||
|
if err := <-errCh; err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case4: Check if cancellation is being handled
|
||||||
|
ctx, cancel3 := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func(cancel context.CancelFunc) {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
cancel()
|
||||||
|
}(cancel3)
|
||||||
|
|
||||||
|
go func(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Expect context canceled error
|
||||||
|
require.Equal(t, "context canceled", ctx.Err().Error(), "expect the ctx error to be a context canceled error")
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
// Case for safely exiting the tests
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(ctx, wg)
|
||||||
|
|
||||||
|
// Wait for all tests to pass
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Cancel all remaining contexts
|
||||||
|
cancel1()
|
||||||
|
cancel2()
|
||||||
|
}
|
||||||
|
|
||||||
func TestSpanURL(t *testing.T) {
|
func TestSpanURL(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
82
consensus/bor/heimdall/metrics.go
Normal file
82
consensus/bor/heimdall/metrics.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
package heimdall
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
requestTypeKey struct{}
|
||||||
|
requestType string
|
||||||
|
|
||||||
|
meter struct {
|
||||||
|
request map[bool]metrics.Meter // map[isSuccessful]metrics.Meter
|
||||||
|
timer metrics.Timer
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateSyncRequest requestType = "state-sync"
|
||||||
|
spanRequest requestType = "span"
|
||||||
|
checkpointRequest requestType = "checkpoint"
|
||||||
|
checkpointCountRequest requestType = "checkpoint-count"
|
||||||
|
)
|
||||||
|
|
||||||
|
func withRequestType(ctx context.Context, reqType requestType) context.Context {
|
||||||
|
return context.WithValue(ctx, requestTypeKey{}, reqType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRequestType(ctx context.Context) (requestType, bool) {
|
||||||
|
reqType, ok := ctx.Value(requestTypeKey{}).(requestType)
|
||||||
|
return reqType, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
requestMeters = map[requestType]meter{
|
||||||
|
stateSyncRequest: {
|
||||||
|
request: map[bool]metrics.Meter{
|
||||||
|
true: metrics.NewRegisteredMeter("client/requests/statesync/valid", nil),
|
||||||
|
false: metrics.NewRegisteredMeter("client/requests/statesync/invalid", nil),
|
||||||
|
},
|
||||||
|
timer: metrics.NewRegisteredTimer("client/requests/statesync/duration", nil),
|
||||||
|
},
|
||||||
|
spanRequest: {
|
||||||
|
request: map[bool]metrics.Meter{
|
||||||
|
true: metrics.NewRegisteredMeter("client/requests/span/valid", nil),
|
||||||
|
false: metrics.NewRegisteredMeter("client/requests/span/invalid", nil),
|
||||||
|
},
|
||||||
|
timer: metrics.NewRegisteredTimer("client/requests/span/duration", nil),
|
||||||
|
},
|
||||||
|
checkpointRequest: {
|
||||||
|
request: map[bool]metrics.Meter{
|
||||||
|
true: metrics.NewRegisteredMeter("client/requests/checkpoint/valid", nil),
|
||||||
|
false: metrics.NewRegisteredMeter("client/requests/checkpoint/invalid", nil),
|
||||||
|
},
|
||||||
|
timer: metrics.NewRegisteredTimer("client/requests/checkpoint/duration", nil),
|
||||||
|
},
|
||||||
|
checkpointCountRequest: {
|
||||||
|
request: map[bool]metrics.Meter{
|
||||||
|
true: metrics.NewRegisteredMeter("client/requests/checkpointcount/valid", nil),
|
||||||
|
false: metrics.NewRegisteredMeter("client/requests/checkpointcount/invalid", nil),
|
||||||
|
},
|
||||||
|
timer: metrics.NewRegisteredTimer("client/requests/checkpointcount/duration", nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func sendMetrics(ctx context.Context, start time.Time, isSuccessful bool) {
|
||||||
|
reqType, ok := getRequestType(ctx)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meters, ok := requestMeters[reqType]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meters.request[isSuccessful].Mark(1)
|
||||||
|
meters.timer.Update(time.Since(start))
|
||||||
|
}
|
||||||
|
|
@ -39,12 +39,12 @@ func NewChainSpanner(ethAPI api.Caller, validatorSet abi.ABI, chainConfig *param
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentSpan get current span from contract
|
// GetCurrentSpan get current span from contract
|
||||||
func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
|
func (c *ChainSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*Span, error) {
|
||||||
// block
|
// block
|
||||||
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
|
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
|
||||||
|
|
||||||
// method
|
// method
|
||||||
method := "getCurrentSpan"
|
const method = "getCurrentSpan"
|
||||||
|
|
||||||
data, err := c.validatorSet.Pack(method)
|
data, err := c.validatorSet.Pack(method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -58,7 +58,7 @@ func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
|
||||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||||
|
|
||||||
// todo: would we like to have a timeout here?
|
// todo: would we like to have a timeout here?
|
||||||
result, err := c.ethAPI.Call(context.Background(), ethapi.TransactionArgs{
|
result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{
|
||||||
Gas: &gas,
|
Gas: &gas,
|
||||||
To: &toAddress,
|
To: &toAddress,
|
||||||
Data: &msgData,
|
Data: &msgData,
|
||||||
|
|
@ -89,8 +89,8 @@ func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidators get current validators
|
// GetCurrentValidators get current validators
|
||||||
func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
|
func (c *ChainSpanner) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// method
|
// method
|
||||||
|
|
@ -146,7 +146,7 @@ func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber
|
||||||
|
|
||||||
const method = "commitSpan"
|
const method = "commitSpan"
|
||||||
|
|
||||||
func (c *ChainSpanner) CommitSpan(heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error {
|
func (c *ChainSpanner) CommitSpan(ctx context.Context, heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error {
|
||||||
// get validators bytes
|
// get validators bytes
|
||||||
validators := make([]valset.MinimalVal, 0, len(heimdallSpan.ValidatorSet.Validators))
|
validators := make([]valset.MinimalVal, 0, len(heimdallSpan.ValidatorSet.Validators))
|
||||||
for _, val := range heimdallSpan.ValidatorSet.Validators {
|
for _, val := range heimdallSpan.ValidatorSet.Validators {
|
||||||
|
|
@ -194,7 +194,7 @@ func (c *ChainSpanner) CommitSpan(heimdallSpan HeimdallSpan, state *state.StateD
|
||||||
msg := statefull.GetSystemMessage(c.validatorContractAddress, data)
|
msg := statefull.GetSystemMessage(c.validatorContractAddress, data)
|
||||||
|
|
||||||
// apply message
|
// apply message
|
||||||
_, err = statefull.ApplyMessage(msg, state, header, c.chainConfig, chainContext)
|
_, err = statefull.ApplyMessage(ctx, msg, state, header, c.chainConfig, chainContext)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
consensus/bor/heimdallgrpc/checkpoint.go
Normal file
51
consensus/bor/heimdallgrpc/checkpoint.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package heimdallgrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
|
proto "github.com/maticnetwork/polyproto/heimdall"
|
||||||
|
protoutils "github.com/maticnetwork/polyproto/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *HeimdallGRPCClient) FetchCheckpointCount(ctx context.Context) (int64, error) {
|
||||||
|
log.Info("Fetching checkpoint count")
|
||||||
|
|
||||||
|
res, err := h.client.FetchCheckpointCount(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Fetched checkpoint count")
|
||||||
|
|
||||||
|
return res.Result.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeimdallGRPCClient) FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error) {
|
||||||
|
req := &proto.FetchCheckpointRequest{
|
||||||
|
ID: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Fetching checkpoint", "number", number)
|
||||||
|
|
||||||
|
res, err := h.client.FetchCheckpoint(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Fetched checkpoint", "number", number)
|
||||||
|
|
||||||
|
checkpoint := &checkpoint.Checkpoint{
|
||||||
|
StartBlock: new(big.Int).SetUint64(res.Result.StartBlock),
|
||||||
|
EndBlock: new(big.Int).SetUint64(res.Result.EndBlock),
|
||||||
|
RootHash: protoutils.ConvertH256ToHash(res.Result.RootHash),
|
||||||
|
Proposer: protoutils.ConvertH160toAddress(res.Result.Proposer),
|
||||||
|
BorChainID: res.Result.BorChainID,
|
||||||
|
Timestamp: uint64(res.Result.Timestamp.GetSeconds()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkpoint, nil
|
||||||
|
}
|
||||||
52
consensus/bor/heimdallgrpc/client.go
Normal file
52
consensus/bor/heimdallgrpc/client.go
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
package heimdallgrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
|
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||||
|
proto "github.com/maticnetwork/polyproto/heimdall"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateFetchLimit = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
type HeimdallGRPCClient struct {
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
client proto.HeimdallClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHeimdallGRPCClient(address string) *HeimdallGRPCClient {
|
||||||
|
opts := []grpc_retry.CallOption{
|
||||||
|
grpc_retry.WithMax(10000),
|
||||||
|
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(5 * time.Second)),
|
||||||
|
grpc_retry.WithCodes(codes.Internal, codes.Unavailable, codes.Aborted, codes.NotFound),
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := grpc.Dial(address,
|
||||||
|
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(opts...)),
|
||||||
|
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(opts...)),
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to connect to Heimdall gRPC", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Connected to Heimdall gRPC server", "address", address)
|
||||||
|
|
||||||
|
return &HeimdallGRPCClient{
|
||||||
|
conn: conn,
|
||||||
|
client: proto.NewHeimdallClient(conn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HeimdallGRPCClient) Close() {
|
||||||
|
log.Debug("Shutdown detected, Closing Heimdall gRPC client")
|
||||||
|
h.conn.Close()
|
||||||
|
}
|
||||||
63
consensus/bor/heimdallgrpc/span.go
Normal file
63
consensus/bor/heimdallgrpc/span.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package heimdallgrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/valset"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
|
proto "github.com/maticnetwork/polyproto/heimdall"
|
||||||
|
protoutils "github.com/maticnetwork/polyproto/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *HeimdallGRPCClient) Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error) {
|
||||||
|
req := &proto.SpanRequest{
|
||||||
|
ID: spanID,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Fetching span", "spanID", spanID)
|
||||||
|
|
||||||
|
res, err := h.client.Span(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Fetched span", "spanID", spanID)
|
||||||
|
|
||||||
|
return parseSpan(res.Result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSpan(protoSpan *proto.Span) *span.HeimdallSpan {
|
||||||
|
resp := &span.HeimdallSpan{
|
||||||
|
Span: span.Span{
|
||||||
|
ID: protoSpan.ID,
|
||||||
|
StartBlock: protoSpan.StartBlock,
|
||||||
|
EndBlock: protoSpan.EndBlock,
|
||||||
|
},
|
||||||
|
ValidatorSet: valset.ValidatorSet{},
|
||||||
|
SelectedProducers: []valset.Validator{},
|
||||||
|
ChainID: protoSpan.ChainID,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, validator := range protoSpan.ValidatorSet.Validators {
|
||||||
|
resp.ValidatorSet.Validators = append(resp.ValidatorSet.Validators, parseValidator(validator))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.ValidatorSet.Proposer = parseValidator(protoSpan.ValidatorSet.Proposer)
|
||||||
|
|
||||||
|
for _, validator := range protoSpan.SelectedProducers {
|
||||||
|
resp.SelectedProducers = append(resp.SelectedProducers, *parseValidator(validator))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseValidator(validator *proto.Validator) *valset.Validator {
|
||||||
|
return &valset.Validator{
|
||||||
|
ID: validator.ID,
|
||||||
|
Address: protoutils.ConvertH160toAddress(validator.Address),
|
||||||
|
VotingPower: validator.VotingPower,
|
||||||
|
ProposerPriority: validator.ProposerPriority,
|
||||||
|
}
|
||||||
|
}
|
||||||
59
consensus/bor/heimdallgrpc/state_sync.go
Normal file
59
consensus/bor/heimdallgrpc/state_sync.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
package heimdallgrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||||
|
|
||||||
|
proto "github.com/maticnetwork/polyproto/heimdall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *HeimdallGRPCClient) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
|
||||||
|
eventRecords := make([]*clerk.EventRecordWithTime, 0)
|
||||||
|
|
||||||
|
req := &proto.StateSyncEventsRequest{
|
||||||
|
FromID: fromID,
|
||||||
|
ToTime: uint64(to),
|
||||||
|
Limit: uint64(stateFetchLimit),
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
res proto.Heimdall_StateSyncEventsClient
|
||||||
|
events *proto.StateSyncEventsResponse
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
res, err = h.client.StateSyncEvents(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
events, err = res.Recv()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return eventRecords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, event := range events.Result {
|
||||||
|
eventRecord := &clerk.EventRecordWithTime{
|
||||||
|
EventRecord: clerk.EventRecord{
|
||||||
|
ID: event.ID,
|
||||||
|
Contract: common.HexToAddress(event.Contract),
|
||||||
|
Data: common.Hex2Bytes(event.Data[2:]),
|
||||||
|
TxHash: common.HexToHash(event.TxHash),
|
||||||
|
LogIndex: event.LogIndex,
|
||||||
|
ChainID: event.ChainID,
|
||||||
|
},
|
||||||
|
Time: event.Time.AsTime(),
|
||||||
|
}
|
||||||
|
eventRecords = append(eventRecords, eventRecord)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ func convertTo32(input []byte) (output [32]byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func convert(input []([32]byte)) [][]byte {
|
func convert(input [][32]byte) [][]byte {
|
||||||
output := make([][]byte, 0, len(input))
|
output := make([][]byte, 0, len(input))
|
||||||
|
|
||||||
for _, in := range input {
|
for _, in := range input {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snap.ValidatorSet.UpdateValidatorMap()
|
||||||
|
|
||||||
snap.config = config
|
snap.config = config
|
||||||
snap.sigcache = sigcache
|
snap.sigcache = sigcache
|
||||||
|
|
||||||
|
|
@ -120,8 +122,8 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
|
|
||||||
// Delete the oldest signer from the recent list to allow it signing again
|
// Delete the oldest signer from the recent list to allow it signing again
|
||||||
if number >= s.config.Sprint {
|
if number >= s.config.CalculateSprint(number) {
|
||||||
delete(snap.Recents, number-s.config.Sprint)
|
delete(snap.Recents, number-s.config.CalculateSprint(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the authorization key and check against signers
|
// Resolve the authorization key and check against signers
|
||||||
|
|
@ -131,7 +133,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if signer is in validator set
|
// check if signer is in validator set
|
||||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
if !snap.ValidatorSet.HasAddress(signer) {
|
||||||
return nil, &UnauthorizedSignerError{number, signer.Bytes()}
|
return nil, &UnauthorizedSignerError{number, signer.Bytes()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +145,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
||||||
snap.Recents[number] = signer
|
snap.Recents[number] = signer
|
||||||
|
|
||||||
// change validator set and change proposer
|
// change validator set and change proposer
|
||||||
if number > 0 && (number+1)%s.config.Sprint == 0 {
|
if number > 0 && (number+1)%s.config.CalculateSprint(number) == 0 {
|
||||||
if err := validateHeaderExtraField(header.Extra); err != nil {
|
if err := validateHeaderExtraField(header.Extra); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -201,18 +203,18 @@ func (s *Snapshot) signers() []common.Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Difficulty returns the difficulty for a particular signer at the current snapshot number
|
// Difficulty returns the difficulty for a particular signer at the current snapshot number
|
||||||
func (s *Snapshot) Difficulty(signer common.Address) uint64 {
|
func Difficulty(validatorSet *valset.ValidatorSet, signer common.Address) uint64 {
|
||||||
// if signer is empty
|
// if signer is empty
|
||||||
if signer == (common.Address{}) {
|
if signer == (common.Address{}) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
validators := s.ValidatorSet.Validators
|
validators := validatorSet.Validators
|
||||||
proposer := s.ValidatorSet.GetProposer().Address
|
proposer := validatorSet.GetProposer().Address
|
||||||
totalValidators := len(validators)
|
totalValidators := len(validators)
|
||||||
|
|
||||||
proposerIndex, _ := s.ValidatorSet.GetByAddress(proposer)
|
proposerIndex, _ := validatorSet.GetByAddress(proposer)
|
||||||
signerIndex, _ := s.ValidatorSet.GetByAddress(signer)
|
signerIndex, _ := validatorSet.GetByAddress(signer)
|
||||||
|
|
||||||
// temp index
|
// temp index
|
||||||
tempIndex := signerIndex
|
tempIndex := signerIndex
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
package bor
|
package bor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/maticnetwork/crand"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"pgregory.net/rapid"
|
"pgregory.net/rapid"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -28,13 +28,13 @@ func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// proposer is signer
|
// proposer is signer
|
||||||
signer := validatorSet.Proposer.Address
|
signerTest := validatorSet.Proposer.Address
|
||||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
successionNumber, err := snap.GetSignerSuccessionNumber(signerTest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s", err)
|
t.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, 0, successionNumber)
|
require.Equal(t, 0, successionNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
|
func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
|
||||||
|
|
@ -54,13 +54,13 @@ func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose a signer at an index greater than proposer index
|
// choose a signer at an index greater than proposer index
|
||||||
signer := snap.ValidatorSet.Validators[signerIndex].Address
|
signerTest := snap.ValidatorSet.Validators[signerIndex].Address
|
||||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
successionNumber, err := snap.GetSignerSuccessionNumber(signerTest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s", err)
|
t.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, signerIndex-proposerIndex, successionNumber)
|
require.Equal(t, signerIndex-proposerIndex, successionNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
|
func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
|
||||||
|
|
@ -76,13 +76,13 @@ func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose a signer at an index greater than proposer index
|
// choose a signer at an index greater than proposer index
|
||||||
signer := snap.ValidatorSet.Validators[signerIndex].Address
|
signerTest := snap.ValidatorSet.Validators[signerIndex].Address
|
||||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
successionNumber, err := snap.GetSignerSuccessionNumber(signerTest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s", err)
|
t.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, signerIndex+numVals-proposerIndex, successionNumber)
|
require.Equal(t, signerIndex+numVals-proposerIndex, successionNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
|
func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
|
||||||
|
|
@ -93,18 +93,20 @@ func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
|
||||||
ValidatorSet: valset.NewValidatorSet(validators),
|
ValidatorSet: valset.NewValidatorSet(validators),
|
||||||
}
|
}
|
||||||
|
|
||||||
dummyProposerAddress := randomAddress()
|
require.Len(t, snap.ValidatorSet.Validators, numVals)
|
||||||
|
|
||||||
|
dummyProposerAddress := randomAddress(toAddresses(validators)...)
|
||||||
snap.ValidatorSet.Proposer = &valset.Validator{Address: dummyProposerAddress}
|
snap.ValidatorSet.Proposer = &valset.Validator{Address: dummyProposerAddress}
|
||||||
|
|
||||||
// choose any signer
|
// choose any signer
|
||||||
signer := snap.ValidatorSet.Validators[3].Address
|
signerTest := snap.ValidatorSet.Validators[3].Address
|
||||||
|
|
||||||
_, err := snap.GetSignerSuccessionNumber(signer)
|
_, err := snap.GetSignerSuccessionNumber(signerTest)
|
||||||
assert.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
e, ok := err.(*UnauthorizedProposerError)
|
e, ok := err.(*UnauthorizedProposerError)
|
||||||
assert.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
|
require.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
|
func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
|
||||||
|
|
@ -114,26 +116,30 @@ func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
|
||||||
snap := Snapshot{
|
snap := Snapshot{
|
||||||
ValidatorSet: valset.NewValidatorSet(validators),
|
ValidatorSet: valset.NewValidatorSet(validators),
|
||||||
}
|
}
|
||||||
dummySignerAddress := randomAddress()
|
|
||||||
|
dummySignerAddress := randomAddress(toAddresses(validators)...)
|
||||||
_, err := snap.GetSignerSuccessionNumber(dummySignerAddress)
|
_, err := snap.GetSignerSuccessionNumber(dummySignerAddress)
|
||||||
assert.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
e, ok := err.(*UnauthorizedSignerError)
|
e, ok := err.(*UnauthorizedSignerError)
|
||||||
assert.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, dummySignerAddress.Bytes(), e.Signer)
|
|
||||||
|
require.Equal(t, dummySignerAddress.Bytes(), e.Signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: unparam
|
// nolint: unparam
|
||||||
func buildRandomValidatorSet(numVals int) []*valset.Validator {
|
func buildRandomValidatorSet(numVals int) []*valset.Validator {
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
|
|
||||||
validators := make([]*valset.Validator, numVals)
|
validators := make([]*valset.Validator, numVals)
|
||||||
valAddrs := randomAddresses(numVals)
|
valAddrs := randomAddresses(numVals)
|
||||||
|
|
||||||
for i := 0; i < numVals; i++ {
|
for i := 0; i < numVals; i++ {
|
||||||
|
power := crand.BigInt(big.NewInt(99))
|
||||||
|
powerN := power.Int64() + 1
|
||||||
|
|
||||||
validators[i] = &valset.Validator{
|
validators[i] = &valset.Validator{
|
||||||
Address: valAddrs[i],
|
Address: valAddrs[i],
|
||||||
// cannot process validators with voting power 0, hence +1
|
// cannot process validators with voting power 0, hence +1
|
||||||
VotingPower: int64(rand.Intn(99) + 1),
|
VotingPower: powerN,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,11 +149,23 @@ func buildRandomValidatorSet(numVals int) []*valset.Validator {
|
||||||
return validators
|
return validators
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomAddress() common.Address {
|
func randomAddress(exclude ...common.Address) common.Address {
|
||||||
bytes := make([]byte, 32)
|
excl := make(map[common.Address]struct{}, len(exclude))
|
||||||
rand.Read(bytes)
|
|
||||||
|
|
||||||
return common.BytesToAddress(bytes)
|
for _, addr := range exclude {
|
||||||
|
excl[addr] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := crand.NewRand()
|
||||||
|
|
||||||
|
for {
|
||||||
|
addr := r.Address()
|
||||||
|
if _, ok := excl[addr]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomAddresses(n int) []common.Address {
|
func randomAddresses(n int) []common.Address {
|
||||||
|
|
@ -158,17 +176,12 @@ func randomAddresses(n int) []common.Address {
|
||||||
addrs := make([]common.Address, 0, n)
|
addrs := make([]common.Address, 0, n)
|
||||||
addrsSet := make(map[common.Address]struct{}, n)
|
addrsSet := make(map[common.Address]struct{}, n)
|
||||||
|
|
||||||
var (
|
var exist bool
|
||||||
addr common.Address
|
|
||||||
exist bool
|
|
||||||
)
|
|
||||||
|
|
||||||
bytes := make([]byte, 32)
|
r := crand.NewRand()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
rand.Read(bytes)
|
addr := r.Address()
|
||||||
|
|
||||||
addr = common.BytesToAddress(bytes)
|
|
||||||
|
|
||||||
_, exist = addrsSet[addr]
|
_, exist = addrsSet[addr]
|
||||||
if !exist {
|
if !exist {
|
||||||
|
|
@ -187,7 +200,7 @@ func TestRandomAddresses(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
rapid.Check(t, func(t *rapid.T) {
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
length := rapid.IntMax(100).Draw(t, "length").(int)
|
length := rapid.IntMax(300).Draw(t, "length").(int)
|
||||||
|
|
||||||
addrs := randomAddresses(length)
|
addrs := randomAddresses(length)
|
||||||
addressSet := unique.New(addrs)
|
addressSet := unique.New(addrs)
|
||||||
|
|
@ -197,3 +210,13 @@ func TestRandomAddresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toAddresses(vals []*valset.Validator) []common.Address {
|
||||||
|
addrs := make([]common.Address, len(vals))
|
||||||
|
|
||||||
|
for i, val := range vals {
|
||||||
|
addrs[i] = val.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package bor
|
package bor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/valset"
|
"github.com/ethereum/go-ethereum/consensus/bor/valset"
|
||||||
|
|
@ -11,7 +13,7 @@ import (
|
||||||
|
|
||||||
//go:generate mockgen -destination=./span_mock.go -package=bor . Spanner
|
//go:generate mockgen -destination=./span_mock.go -package=bor . Spanner
|
||||||
type Spanner interface {
|
type Spanner interface {
|
||||||
GetCurrentSpan(headerHash common.Hash) (*span.Span, error)
|
GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error)
|
||||||
GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
|
GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
|
||||||
CommitSpan(heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error
|
CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package bor
|
package bor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
context "context"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
common "github.com/ethereum/go-ethereum/common"
|
common "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -40,45 +41,45 @@ func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitSpan mocks base method.
|
// CommitSpan mocks base method.
|
||||||
func (m *MockSpanner) CommitSpan(arg0 span.HeimdallSpan, arg1 *state.StateDB, arg2 *types.Header, arg3 core.ChainContext) error {
|
func (m *MockSpanner) CommitSpan(arg0 context.Context, arg1 span.HeimdallSpan, arg2 *state.StateDB, arg3 *types.Header, arg4 core.ChainContext) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3)
|
ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3, arg4)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitSpan indicates an expected call of CommitSpan.
|
// CommitSpan indicates an expected call of CommitSpan.
|
||||||
func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3, arg4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentSpan mocks base method.
|
// GetCurrentSpan mocks base method.
|
||||||
func (m *MockSpanner) GetCurrentSpan(arg0 common.Hash) (*span.Span, error) {
|
func (m *MockSpanner) GetCurrentSpan(arg0 context.Context, arg1 common.Hash) (*span.Span, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0)
|
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0, arg1)
|
||||||
ret0, _ := ret[0].(*span.Span)
|
ret0, _ := ret[0].(*span.Span)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentSpan indicates an expected call of GetCurrentSpan.
|
// GetCurrentSpan indicates an expected call of GetCurrentSpan.
|
||||||
func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0 interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidators mocks base method.
|
// GetCurrentValidators mocks base method.
|
||||||
func (m *MockSpanner) GetCurrentValidators(arg0 common.Hash, arg1 uint64) ([]*valset.Validator, error) {
|
func (m *MockSpanner) GetCurrentValidators(arg0 context.Context, arg1 common.Hash, arg2 uint64) ([]*valset.Validator, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1)
|
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1, arg2)
|
||||||
ret0, _ := ret[0].([]*valset.Validator)
|
ret0, _ := ret[0].([]*valset.Validator)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidators indicates an expected call of GetCurrentValidators.
|
// GetCurrentValidators indicates an expected call of GetCurrentValidators.
|
||||||
func (mr *MockSpannerMockRecorder) GetCurrentValidators(arg0, arg1 interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) GetCurrentValidators(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidators), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidators), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package statefull
|
package statefull
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -59,6 +60,7 @@ func GetSystemMessage(toAddress common.Address, data []byte) Callmsg {
|
||||||
|
|
||||||
// apply message
|
// apply message
|
||||||
func ApplyMessage(
|
func ApplyMessage(
|
||||||
|
_ context.Context,
|
||||||
msg Callmsg,
|
msg Callmsg,
|
||||||
state *state.StateDB,
|
state *state.StateDB,
|
||||||
header *types.Header,
|
header *types.Header,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package bor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/valset"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate mockgen -destination=./validators_getter_mock.go -package=bor . ValidatorsGetter
|
|
||||||
type ValidatorsGetter interface {
|
|
||||||
GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: ValidatorsGetter)
|
|
||||||
|
|
||||||
// Package bor is a generated GoMock package.
|
|
||||||
package bor
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
common "github.com/ethereum/go-ethereum/common"
|
|
||||||
valset "github.com/ethereum/go-ethereum/consensus/bor/valset"
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockValidatorsGetter is a mock of ValidatorsGetter interface.
|
|
||||||
type MockValidatorsGetter struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockValidatorsGetterMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockValidatorsGetterMockRecorder is the mock recorder for MockValidatorsGetter.
|
|
||||||
type MockValidatorsGetterMockRecorder struct {
|
|
||||||
mock *MockValidatorsGetter
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockValidatorsGetter creates a new mock instance.
|
|
||||||
func NewMockValidatorsGetter(ctrl *gomock.Controller) *MockValidatorsGetter {
|
|
||||||
mock := &MockValidatorsGetter{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockValidatorsGetterMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockValidatorsGetter) EXPECT() *MockValidatorsGetterMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCurrentValidators mocks base method.
|
|
||||||
func (m *MockValidatorsGetter) GetCurrentValidators(arg0 common.Hash, arg1 uint64) ([]*valset.Validator, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1)
|
|
||||||
ret0, _ := ret[0].([]*valset.Validator)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCurrentValidators indicates an expected call of GetCurrentValidators.
|
|
||||||
func (mr *MockValidatorsGetterMockRecorder) GetCurrentValidators(arg0, arg1 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockValidatorsGetter)(nil).GetCurrentValidators), arg0, arg1)
|
|
||||||
}
|
|
||||||
|
|
@ -47,21 +47,26 @@ func (v *Validator) Cmp(other *Validator) *Validator {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:nestif
|
|
||||||
if v.ProposerPriority > other.ProposerPriority {
|
if v.ProposerPriority > other.ProposerPriority {
|
||||||
return v
|
return v
|
||||||
} else if v.ProposerPriority < other.ProposerPriority {
|
|
||||||
return other
|
|
||||||
} else {
|
|
||||||
result := bytes.Compare(v.Address.Bytes(), other.Address.Bytes())
|
|
||||||
if result < 0 {
|
|
||||||
return v
|
|
||||||
} else if result > 0 {
|
|
||||||
return other
|
|
||||||
} else {
|
|
||||||
panic("Cannot compare identical validators")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.ProposerPriority < other.ProposerPriority {
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
result := bytes.Compare(v.Address.Bytes(), other.Address.Bytes())
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
panic("Cannot compare identical validators")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result < 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// result > 0
|
||||||
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validator) String() string {
|
func (v *Validator) String() string {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ type ValidatorSet struct {
|
||||||
|
|
||||||
// cached (unexported)
|
// cached (unexported)
|
||||||
totalVotingPower int64
|
totalVotingPower int64
|
||||||
|
validatorsMap map[common.Address]int // address -> index
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidatorSet initializes a ValidatorSet by copying over the
|
// NewValidatorSet initializes a ValidatorSet by copying over the
|
||||||
|
|
@ -54,7 +55,7 @@ type ValidatorSet struct {
|
||||||
// The addresses of validators in `valz` must be unique otherwise the
|
// The addresses of validators in `valz` must be unique otherwise the
|
||||||
// function panics.
|
// function panics.
|
||||||
func NewValidatorSet(valz []*Validator) *ValidatorSet {
|
func NewValidatorSet(valz []*Validator) *ValidatorSet {
|
||||||
vals := &ValidatorSet{}
|
vals := &ValidatorSet{validatorsMap: make(map[common.Address]int)}
|
||||||
|
|
||||||
err := vals.updateWithChangeSet(valz, false)
|
err := vals.updateWithChangeSet(valz, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -232,30 +233,34 @@ func validatorListCopy(valsList []*Validator) []*Validator {
|
||||||
|
|
||||||
// Copy each validator into a new ValidatorSet.
|
// Copy each validator into a new ValidatorSet.
|
||||||
func (vals *ValidatorSet) Copy() *ValidatorSet {
|
func (vals *ValidatorSet) Copy() *ValidatorSet {
|
||||||
|
valCopy := validatorListCopy(vals.Validators)
|
||||||
|
validatorsMap := make(map[common.Address]int, len(vals.Validators))
|
||||||
|
|
||||||
|
for i, val := range valCopy {
|
||||||
|
validatorsMap[val.Address] = i
|
||||||
|
}
|
||||||
|
|
||||||
return &ValidatorSet{
|
return &ValidatorSet{
|
||||||
Validators: validatorListCopy(vals.Validators),
|
Validators: validatorListCopy(vals.Validators),
|
||||||
Proposer: vals.Proposer,
|
Proposer: vals.Proposer,
|
||||||
totalVotingPower: vals.totalVotingPower,
|
totalVotingPower: vals.totalVotingPower,
|
||||||
|
validatorsMap: validatorsMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAddress returns true if address given is in the validator set, false -
|
// HasAddress returns true if address given is in the validator set, false -
|
||||||
// otherwise.
|
// otherwise.
|
||||||
func (vals *ValidatorSet) HasAddress(address []byte) bool {
|
func (vals *ValidatorSet) HasAddress(address common.Address) bool {
|
||||||
idx := sort.Search(len(vals.Validators), func(i int) bool {
|
_, ok := vals.validatorsMap[address]
|
||||||
return bytes.Compare(address, vals.Validators[i].Address.Bytes()) <= 0
|
|
||||||
})
|
|
||||||
|
|
||||||
return idx < len(vals.Validators) && bytes.Equal(vals.Validators[idx].Address.Bytes(), address)
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByAddress returns an index of the validator with address and validator
|
// GetByAddress returns an index of the validator with address and validator
|
||||||
// itself if found. Otherwise, -1 and nil are returned.
|
// itself if found. Otherwise, -1 and nil are returned.
|
||||||
func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *Validator) {
|
func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *Validator) {
|
||||||
idx := sort.Search(len(vals.Validators), func(i int) bool {
|
idx, ok := vals.validatorsMap[address]
|
||||||
return bytes.Compare(address.Bytes(), vals.Validators[i].Address.Bytes()) <= 0
|
if ok {
|
||||||
})
|
|
||||||
if idx < len(vals.Validators) && vals.Validators[idx].Address == address {
|
|
||||||
return idx, vals.Validators[idx].Copy()
|
return idx, vals.Validators[idx].Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,14 +270,14 @@ func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *
|
||||||
// GetByIndex returns the validator's address and validator itself by index.
|
// GetByIndex returns the validator's address and validator itself by index.
|
||||||
// It returns nil values if index is less than 0 or greater or equal to
|
// It returns nil values if index is less than 0 or greater or equal to
|
||||||
// len(ValidatorSet.Validators).
|
// len(ValidatorSet.Validators).
|
||||||
func (vals *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) {
|
func (vals *ValidatorSet) GetByIndex(index int) (address common.Address, val *Validator) {
|
||||||
if index < 0 || index >= len(vals.Validators) {
|
if index < 0 || index >= len(vals.Validators) {
|
||||||
return nil, nil
|
return common.Address{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
val = vals.Validators[index]
|
val = vals.Validators[index]
|
||||||
|
|
||||||
return val.Address.Bytes(), val.Copy()
|
return val.Address, val.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the length of the validator set.
|
// Size returns the length of the validator set.
|
||||||
|
|
@ -328,7 +333,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) {
|
||||||
func (vals *ValidatorSet) findProposer() *Validator {
|
func (vals *ValidatorSet) findProposer() *Validator {
|
||||||
var proposer *Validator
|
var proposer *Validator
|
||||||
for _, val := range vals.Validators {
|
for _, val := range vals.Validators {
|
||||||
if proposer == nil || !bytes.Equal(val.Address.Bytes(), proposer.Address.Bytes()) {
|
if proposer == nil || val.Address != proposer.Address {
|
||||||
proposer = proposer.Cmp(val)
|
proposer = proposer.Cmp(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -371,14 +376,19 @@ func processChanges(origChanges []*Validator) (updates, removals []*Validator, e
|
||||||
changes := validatorListCopy(origChanges)
|
changes := validatorListCopy(origChanges)
|
||||||
sort.Sort(ValidatorsByAddress(changes))
|
sort.Sort(ValidatorsByAddress(changes))
|
||||||
|
|
||||||
removals = make([]*Validator, 0, len(changes))
|
sliceCap := len(changes) / 2
|
||||||
updates = make([]*Validator, 0, len(changes))
|
if sliceCap == 0 {
|
||||||
|
sliceCap = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
removals = make([]*Validator, 0, sliceCap)
|
||||||
|
updates = make([]*Validator, 0, sliceCap)
|
||||||
|
|
||||||
var prevAddr common.Address
|
var prevAddr common.Address
|
||||||
|
|
||||||
// Scan changes by address and append valid validators to updates or removals lists.
|
// Scan changes by address and append valid validators to updates or removals lists.
|
||||||
for _, valUpdate := range changes {
|
for _, valUpdate := range changes {
|
||||||
if bytes.Equal(valUpdate.Address.Bytes(), prevAddr.Bytes()) {
|
if valUpdate.Address == prevAddr {
|
||||||
err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes)
|
err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -489,10 +499,11 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
|
||||||
} else {
|
} else {
|
||||||
// Apply add or update.
|
// Apply add or update.
|
||||||
merged[i] = updates[0]
|
merged[i] = updates[0]
|
||||||
if bytes.Equal(existing[0].Address.Bytes(), updates[0].Address.Bytes()) {
|
if existing[0].Address == updates[0].Address {
|
||||||
// Validator is present in both, advance existing.
|
// Validator is present in both, advance existing.
|
||||||
existing = existing[1:]
|
existing = existing[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
updates = updates[1:]
|
updates = updates[1:]
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
|
|
@ -503,6 +514,7 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
|
||||||
merged[i] = existing[j]
|
merged[i] = existing[j]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
// OR add updates which are left.
|
// OR add updates which are left.
|
||||||
for j := 0; j < len(updates); j++ {
|
for j := 0; j < len(updates); j++ {
|
||||||
merged[i] = updates[j]
|
merged[i] = updates[j]
|
||||||
|
|
@ -541,7 +553,7 @@ func (vals *ValidatorSet) applyRemovals(deletes []*Validator) {
|
||||||
|
|
||||||
// Loop over deletes until we removed all of them.
|
// Loop over deletes until we removed all of them.
|
||||||
for len(deletes) > 0 {
|
for len(deletes) > 0 {
|
||||||
if bytes.Equal(existing[0].Address.Bytes(), deletes[0].Address.Bytes()) {
|
if existing[0].Address == deletes[0].Address {
|
||||||
deletes = deletes[1:]
|
deletes = deletes[1:]
|
||||||
} else { // Leave it in the resulting slice.
|
} else { // Leave it in the resulting slice.
|
||||||
merged[i] = existing[0]
|
merged[i] = existing[0]
|
||||||
|
|
@ -599,8 +611,7 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
|
||||||
computeNewPriorities(updates, vals, updatedTotalVotingPower)
|
computeNewPriorities(updates, vals, updatedTotalVotingPower)
|
||||||
|
|
||||||
// Apply updates and removals.
|
// Apply updates and removals.
|
||||||
vals.applyUpdates(updates)
|
vals.updateValidators(updates, deletes)
|
||||||
vals.applyRemovals(deletes)
|
|
||||||
|
|
||||||
if err := vals.UpdateTotalVotingPower(); err != nil {
|
if err := vals.UpdateTotalVotingPower(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -613,6 +624,21 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vals *ValidatorSet) updateValidators(updates []*Validator, deletes []*Validator) {
|
||||||
|
vals.applyUpdates(updates)
|
||||||
|
vals.applyRemovals(deletes)
|
||||||
|
|
||||||
|
vals.UpdateValidatorMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vals *ValidatorSet) UpdateValidatorMap() {
|
||||||
|
vals.validatorsMap = make(map[common.Address]int, len(vals.Validators))
|
||||||
|
|
||||||
|
for i, val := range vals.Validators {
|
||||||
|
vals.validatorsMap[val.Address] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateWithChangeSet attempts to update the validator set with 'changes'.
|
// UpdateWithChangeSet attempts to update the validator set with 'changes'.
|
||||||
// It performs the following steps:
|
// It performs the following steps:
|
||||||
// - validates the changes making sure there are no duplicates and splits them in updates and deletes
|
// - validates the changes making sure there are no duplicates and splits them in updates and deletes
|
||||||
|
|
@ -661,7 +687,7 @@ func (vals *ValidatorSet) StringIndented(indent string) string {
|
||||||
return "nil-ValidatorSet"
|
return "nil-ValidatorSet"
|
||||||
}
|
}
|
||||||
|
|
||||||
var valStrings []string
|
valStrings := make([]string, 0, len(vals.Validators))
|
||||||
|
|
||||||
vals.Iterate(func(index int, val *Validator) bool {
|
vals.Iterate(func(index int, val *Validator) bool {
|
||||||
valStrings = append(valStrings, val.String())
|
valStrings = append(valStrings, val.String())
|
||||||
|
|
|
||||||
199
consensus/bor/valset/validator_set_test.go
Normal file
199
consensus/bor/valset/validator_set_test.go
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
package valset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gotest.tools/assert"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewValidatorFromKey(key string, votingPower int64) *Validator {
|
||||||
|
privKey, _ := crypto.HexToECDSA(key)
|
||||||
|
|
||||||
|
return NewValidator(crypto.PubkeyToAddress(privKey.PublicKey), votingPower)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetValidators() [4]*Validator {
|
||||||
|
const (
|
||||||
|
// addr0 = 0x96C42C56fdb78294F96B0cFa33c92bed7D75F96a
|
||||||
|
signer0 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd9"
|
||||||
|
|
||||||
|
// addr1 = 0x98925BE497f6dFF6A5a33dDA8B5933cA35262d69
|
||||||
|
signer1 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd8"
|
||||||
|
|
||||||
|
//addr2 = 0x648Cf2A5b119E2c04061021834F8f75735B1D36b
|
||||||
|
signer2 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd7"
|
||||||
|
|
||||||
|
//addr3 = 0x168f220B3b313D456eD4797520eFdFA9c57E6C45
|
||||||
|
signer3 = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd6"
|
||||||
|
)
|
||||||
|
|
||||||
|
return [4]*Validator{
|
||||||
|
NewValidatorFromKey(signer0, 100),
|
||||||
|
NewValidatorFromKey(signer1, 200),
|
||||||
|
NewValidatorFromKey(signer2, 300),
|
||||||
|
NewValidatorFromKey(signer3, 400),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncrementProposerPriority(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
vals := GetValidators()
|
||||||
|
|
||||||
|
// Validator set length = 1
|
||||||
|
valSet := NewValidatorSet(vals[:1])
|
||||||
|
|
||||||
|
expectedPropsers := []*Validator{vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0], vals[0]}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
valSet.IncrementProposerPriority(1)
|
||||||
|
|
||||||
|
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set length = 2
|
||||||
|
valSet = NewValidatorSet(vals[:2])
|
||||||
|
|
||||||
|
expectedPropsers = []*Validator{vals[0], vals[1], vals[1], vals[0], vals[1], vals[1], vals[0], vals[1], vals[1], vals[0]}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
valSet.IncrementProposerPriority(1)
|
||||||
|
|
||||||
|
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set length = 3
|
||||||
|
valSet = NewValidatorSet(vals[:3])
|
||||||
|
|
||||||
|
expectedPropsers = []*Validator{vals[1], vals[2], vals[0], vals[1], vals[2], vals[2], vals[1], vals[2], vals[0], vals[1]}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
valSet.IncrementProposerPriority(1)
|
||||||
|
|
||||||
|
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set length = 4
|
||||||
|
valSet = NewValidatorSet(vals[:4])
|
||||||
|
|
||||||
|
expectedPropsers = []*Validator{vals[2], vals[1], vals[3], vals[2], vals[0], vals[3], vals[1], vals[2], vals[3], vals[3]}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
valSet.IncrementProposerPriority(1)
|
||||||
|
|
||||||
|
require.Equal(t, expectedPropsers[i].Address, valSet.GetProposer().Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRescalePriorities(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
vals := GetValidators()
|
||||||
|
|
||||||
|
// Validator set length = 1
|
||||||
|
valSet := NewValidatorSet(vals[:1])
|
||||||
|
|
||||||
|
valSet.RescalePriorities(10)
|
||||||
|
|
||||||
|
expectedPriorities := []int64{0}
|
||||||
|
for i, val := range valSet.Validators {
|
||||||
|
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set length = 2
|
||||||
|
|
||||||
|
valSet = NewValidatorSet(vals[:2])
|
||||||
|
|
||||||
|
valSet.RescalePriorities(100)
|
||||||
|
|
||||||
|
expectedPriorities = []int64{50, -50}
|
||||||
|
for i, val := range valSet.Validators {
|
||||||
|
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set length = 3
|
||||||
|
|
||||||
|
valSet = NewValidatorSet(vals[:3])
|
||||||
|
|
||||||
|
valSet.RescalePriorities(30)
|
||||||
|
|
||||||
|
expectedPriorities = []int64{-17, 5, 11}
|
||||||
|
for i, val := range valSet.Validators {
|
||||||
|
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set length = 4
|
||||||
|
|
||||||
|
valSet = NewValidatorSet(vals[:4])
|
||||||
|
|
||||||
|
valSet.RescalePriorities(10)
|
||||||
|
|
||||||
|
expectedPriorities = []int64{-6, 3, 1, 2}
|
||||||
|
for i, val := range valSet.Validators {
|
||||||
|
require.Equal(t, expectedPriorities[i], val.ProposerPriority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetValidatorByAddressAndIndex(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
vals := GetValidators()
|
||||||
|
valSet := NewValidatorSet(vals[:4])
|
||||||
|
|
||||||
|
for _, val := range valSet.Validators {
|
||||||
|
idx, valByAddress := valSet.GetByAddress(val.Address)
|
||||||
|
addr, valByIndex := valSet.GetByIndex(idx)
|
||||||
|
|
||||||
|
assert.DeepEqual(t, val, valByIndex)
|
||||||
|
assert.DeepEqual(t, val, valByAddress)
|
||||||
|
assert.DeepEqual(t, val.Address, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
tempAddress := common.HexToAddress("0x12345")
|
||||||
|
|
||||||
|
// Negative Testcase
|
||||||
|
idx, _ := valSet.GetByAddress(tempAddress)
|
||||||
|
require.Equal(t, idx, -1)
|
||||||
|
|
||||||
|
// checking for validator index out of range
|
||||||
|
addr, _ := valSet.GetByIndex(100)
|
||||||
|
require.Equal(t, addr, common.Address{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateWithChangeSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
vals := GetValidators()
|
||||||
|
valSet := NewValidatorSet(vals[:4])
|
||||||
|
|
||||||
|
// halved the power of vals[2] and doubled the power of vals[3]
|
||||||
|
vals[2].VotingPower = 150
|
||||||
|
vals[3].VotingPower = 800
|
||||||
|
|
||||||
|
// Adding new temp validator in the set
|
||||||
|
const tempSigner = "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd5"
|
||||||
|
|
||||||
|
tempVal := NewValidatorFromKey(tempSigner, 250)
|
||||||
|
|
||||||
|
// check totalVotingPower before updating validator set
|
||||||
|
require.Equal(t, int64(1000), valSet.TotalVotingPower())
|
||||||
|
|
||||||
|
err := valSet.UpdateWithChangeSet([]*Validator{vals[2], vals[3], tempVal})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// check totalVotingPower after updating validator set
|
||||||
|
require.Equal(t, int64(1500), valSet.TotalVotingPower())
|
||||||
|
|
||||||
|
_, updatedVal2 := valSet.GetByAddress(vals[2].Address)
|
||||||
|
require.Equal(t, int64(150), updatedVal2.VotingPower)
|
||||||
|
|
||||||
|
_, updatedVal3 := valSet.GetByAddress(vals[3].Address)
|
||||||
|
require.Equal(t, int64(800), updatedVal3.VotingPower)
|
||||||
|
|
||||||
|
_, updatedTempVal := valSet.GetByAddress(tempVal.Address)
|
||||||
|
require.Equal(t, int64(250), updatedTempVal.VotingPower)
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ package clique
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -569,7 +570,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
|
||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
||||||
// nor block rewards given, and returns the final block.
|
// nor block rewards given, and returns the final block.
|
||||||
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (c *Clique) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
// Finalize block
|
// Finalize block
|
||||||
c.Finalize(chain, header, state, txs, uncles)
|
c.Finalize(chain, header, state, txs, uncles)
|
||||||
|
|
||||||
|
|
@ -589,7 +590,7 @@ func (c *Clique) Authorize(signer common.Address, signFn SignerFn) {
|
||||||
|
|
||||||
// Seal implements consensus.Engine, attempting to create a sealed block using
|
// Seal implements consensus.Engine, attempting to create a sealed block using
|
||||||
// the local signing credentials.
|
// the local signing credentials.
|
||||||
func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
func (c *Clique) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
|
|
||||||
// Sealing the genesis block is not supported
|
// Sealing the genesis block is not supported
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||||
genesis := genspec.MustCommit(db)
|
genesis := genspec.MustCommit(db)
|
||||||
|
|
||||||
// Generate a batch of blocks, each properly signed
|
// Generate a batch of blocks, each properly signed
|
||||||
chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) {
|
||||||
|
|
@ -89,7 +89,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
genspec.MustCommit(db)
|
genspec.MustCommit(db)
|
||||||
|
|
||||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
if _, err := chain.InsertChain(blocks[:2]); err != nil {
|
if _, err := chain.InsertChain(blocks[:2]); err != nil {
|
||||||
|
|
@ -102,7 +102,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||||
// Simulate a crash by creating a new chain on top of the database, without
|
// Simulate a crash by creating a new chain on top of the database, without
|
||||||
// flushing the dirty states out. Insert the last block, triggering a sidechain
|
// flushing the dirty states out. Insert the last block, triggering a sidechain
|
||||||
// reimport.
|
// reimport.
|
||||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
if _, err := chain.InsertChain(blocks[2:]); err != nil {
|
if _, err := chain.InsertChain(blocks[2:]); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -450,7 +450,7 @@ func TestClique(t *testing.T) {
|
||||||
batches[len(batches)-1] = append(batches[len(batches)-1], block)
|
batches[len(batches)-1] = append(batches[len(batches)-1], block)
|
||||||
}
|
}
|
||||||
// Pass all the headers through clique and ensure tallying succeeds
|
// Pass all the headers through clique and ensure tallying succeeds
|
||||||
chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("test %d: failed to create test chain: %v", i, err)
|
t.Errorf("test %d: failed to create test chain: %v", i, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -97,7 +98,7 @@ type Engine interface {
|
||||||
//
|
//
|
||||||
// Note: The block header and state database might be updated to reflect any
|
// Note: The block header and state database might be updated to reflect any
|
||||||
// consensus rules that happen at finalization (e.g. block rewards).
|
// consensus rules that happen at finalization (e.g. block rewards).
|
||||||
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
FinalizeAndAssemble(ctx context.Context, chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
||||||
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
|
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
|
||||||
|
|
||||||
// Seal generates a new sealing request for the given input block and pushes
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
|
|
@ -105,7 +106,7 @@ type Engine interface {
|
||||||
//
|
//
|
||||||
// Note, the method returns immediately and will send the result async. More
|
// Note, the method returns immediately and will send the result async. More
|
||||||
// than one result may also be returned depending on the consensus algorithm.
|
// than one result may also be returned depending on the consensus algorithm.
|
||||||
Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
|
Seal(ctx context.Context, chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
|
||||||
|
|
||||||
// SealHash returns the hash of a block prior to it being sealed.
|
// SealHash returns the hash of a block prior to it being sealed.
|
||||||
SealHash(header *types.Header) common.Hash
|
SealHash(header *types.Header) common.Hash
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package ethash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -598,7 +599,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
|
||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
||||||
// uncle rewards, setting the final state and assembling the block.
|
// uncle rewards, setting the final state and assembling the block.
|
||||||
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (ethash *Ethash) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
// Finalize block
|
// Finalize block
|
||||||
ethash.Finalize(chain, header, state, txs, uncles)
|
ethash.Finalize(chain, header, state, txs, uncles)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package ethash
|
package ethash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
@ -38,7 +39,7 @@ func TestTestMode(t *testing.T) {
|
||||||
defer ethash.Close()
|
defer ethash.Close()
|
||||||
|
|
||||||
results := make(chan *types.Block)
|
results := make(chan *types.Block)
|
||||||
err := ethash.Seal(nil, types.NewBlockWithHeader(header), results, nil)
|
err := ethash.Seal(context.Background(), nil, types.NewBlockWithHeader(header), results, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to seal block: %v", err)
|
t.Fatalf("failed to seal block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -111,12 +112,13 @@ func TestRemoteSealer(t *testing.T) {
|
||||||
|
|
||||||
// Push new work.
|
// Push new work.
|
||||||
results := make(chan *types.Block)
|
results := make(chan *types.Block)
|
||||||
ethash.Seal(nil, block, results, nil)
|
err := ethash.Seal(context.Background(), nil, block, results, nil)
|
||||||
|
|
||||||
var (
|
if err != nil {
|
||||||
work [4]string
|
t.Error("error in sealing block")
|
||||||
err error
|
}
|
||||||
)
|
|
||||||
|
var work [4]string
|
||||||
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
|
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
|
||||||
t.Error("expect to return a mining work has same hash")
|
t.Error("expect to return a mining work has same hash")
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +130,11 @@ func TestRemoteSealer(t *testing.T) {
|
||||||
header = &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(1000)}
|
header = &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(1000)}
|
||||||
block = types.NewBlockWithHeader(header)
|
block = types.NewBlockWithHeader(header)
|
||||||
sealhash = ethash.SealHash(header)
|
sealhash = ethash.SealHash(header)
|
||||||
ethash.Seal(nil, block, results, nil)
|
err = ethash.Seal(context.Background(), nil, block, results, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in sealing block")
|
||||||
|
}
|
||||||
|
|
||||||
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
|
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
|
||||||
t.Error("expect to return the latest pushed work")
|
t.Error("expect to return the latest pushed work")
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ var (
|
||||||
|
|
||||||
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
|
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
|
||||||
// the block's difficulty requirements.
|
// the block's difficulty requirements.
|
||||||
func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
func (ethash *Ethash) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
||||||
// If we're running a fake PoW, simply return a 0 nonce immediately
|
// If we're running a fake PoW, simply return a 0 nonce immediately
|
||||||
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
|
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
|
|
@ -62,7 +62,7 @@ func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block
|
||||||
}
|
}
|
||||||
// If we're running a shared PoW, delegate sealing to it
|
// If we're running a shared PoW, delegate sealing to it
|
||||||
if ethash.shared != nil {
|
if ethash.shared != nil {
|
||||||
return ethash.shared.Seal(chain, block, results, stop)
|
return ethash.shared.Seal(ctx, chain, block, results, stop)
|
||||||
}
|
}
|
||||||
// Create a runner and the multiple search threads it directs
|
// Create a runner and the multiple search threads it directs
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
|
|
@ -117,7 +117,8 @@ func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block
|
||||||
case <-ethash.update:
|
case <-ethash.update:
|
||||||
// Thread count was changed on user request, restart
|
// Thread count was changed on user request, restart
|
||||||
close(abort)
|
close(abort)
|
||||||
if err := ethash.Seal(chain, block, results, stop); err != nil {
|
|
||||||
|
if err := ethash.Seal(ctx, chain, block, results, stop); err != nil {
|
||||||
ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
|
ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package ethash
|
package ethash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -57,7 +58,11 @@ func TestRemoteNotify(t *testing.T) {
|
||||||
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(header)
|
block := types.NewBlockWithHeader(header)
|
||||||
|
|
||||||
ethash.Seal(nil, block, nil, nil)
|
err := ethash.Seal(context.Background(), nil, block, nil, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in sealing block")
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case work := <-sink:
|
case work := <-sink:
|
||||||
if want := ethash.SealHash(header).Hex(); work[0] != want {
|
if want := ethash.SealHash(header).Hex(); work[0] != want {
|
||||||
|
|
@ -105,7 +110,11 @@ func TestRemoteNotifyFull(t *testing.T) {
|
||||||
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(header)
|
block := types.NewBlockWithHeader(header)
|
||||||
|
|
||||||
ethash.Seal(nil, block, nil, nil)
|
err := ethash.Seal(context.Background(), nil, block, nil, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in sealing block")
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case work := <-sink:
|
case work := <-sink:
|
||||||
if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want {
|
if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want {
|
||||||
|
|
@ -151,7 +160,11 @@ func TestRemoteMultiNotify(t *testing.T) {
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
|
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(header)
|
block := types.NewBlockWithHeader(header)
|
||||||
ethash.Seal(nil, block, results, nil)
|
err := ethash.Seal(context.Background(), nil, block, results, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in sealing block")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
|
|
@ -180,6 +193,9 @@ func TestRemoteMultiNotifyFull(t *testing.T) {
|
||||||
}
|
}
|
||||||
sink <- work
|
sink <- work
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// Allowing the server to start listening.
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
// Create the custom ethash engine.
|
// Create the custom ethash engine.
|
||||||
|
|
@ -200,7 +216,11 @@ func TestRemoteMultiNotifyFull(t *testing.T) {
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
|
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(header)
|
block := types.NewBlockWithHeader(header)
|
||||||
ethash.Seal(nil, block, results, nil)
|
err := ethash.Seal(context.Background(), nil, block, results, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in sealing block")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
|
|
@ -266,7 +286,11 @@ func TestStaleSubmission(t *testing.T) {
|
||||||
|
|
||||||
for id, c := range testcases {
|
for id, c := range testcases {
|
||||||
for _, h := range c.headers {
|
for _, h := range c.headers {
|
||||||
ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil)
|
err := ethash.Seal(context.Background(), nil, types.NewBlockWithHeader(h), results, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in sealing block")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes {
|
if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes {
|
||||||
t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)
|
t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)
|
||||||
|
|
|
||||||
|
|
@ -59,9 +59,10 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
|
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
|
||||||
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
|
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
|
||||||
baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator)
|
baseFeeChangeDenominatorUint64 = params.BaseFeeChangeDenominator(config.Bor, parent.Number)
|
||||||
|
baseFeeChangeDenominator = new(big.Int).SetUint64(baseFeeChangeDenominatorUint64)
|
||||||
)
|
)
|
||||||
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
|
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
|
||||||
if parent.GasUsed == parentGasTarget {
|
if parent.GasUsed == parentGasTarget {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"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/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
@ -47,12 +46,14 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig {
|
||||||
TerminalTotalDifficulty: original.TerminalTotalDifficulty,
|
TerminalTotalDifficulty: original.TerminalTotalDifficulty,
|
||||||
Ethash: original.Ethash,
|
Ethash: original.Ethash,
|
||||||
Clique: original.Clique,
|
Clique: original.Clique,
|
||||||
|
Bor: original.Bor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func config() *params.ChainConfig {
|
func config() *params.ChainConfig {
|
||||||
config := copyConfig(params.TestChainConfig)
|
config := copyConfig(params.TestChainConfig)
|
||||||
config.LondonBlock = big.NewInt(5)
|
config.LondonBlock = big.NewInt(5)
|
||||||
|
config.Bor.DelhiBlock = big.NewInt(8)
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,6 +109,8 @@ func TestBlockGasLimits(t *testing.T) {
|
||||||
|
|
||||||
// TestCalcBaseFee assumes all blocks are 1559-blocks
|
// TestCalcBaseFee assumes all blocks are 1559-blocks
|
||||||
func TestCalcBaseFee(t *testing.T) {
|
func TestCalcBaseFee(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
parentBaseFee int64
|
parentBaseFee int64
|
||||||
parentGasLimit uint64
|
parentGasLimit uint64
|
||||||
|
|
@ -117,10 +120,12 @@ func TestCalcBaseFee(t *testing.T) {
|
||||||
{params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
|
{params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
|
||||||
{params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target
|
{params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target
|
||||||
{params.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target
|
{params.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target
|
||||||
|
{params.InitialBaseFee, 20000000, 20000000, 1125000000}, // usage full
|
||||||
|
{params.InitialBaseFee, 20000000, 0, 875000000}, // usage 0
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
parent := &types.Header{
|
parent := &types.Header{
|
||||||
Number: common.Big32,
|
Number: big.NewInt(6),
|
||||||
GasLimit: test.parentGasLimit,
|
GasLimit: test.parentGasLimit,
|
||||||
GasUsed: test.parentGasUsed,
|
GasUsed: test.parentGasUsed,
|
||||||
BaseFee: big.NewInt(test.parentBaseFee),
|
BaseFee: big.NewInt(test.parentBaseFee),
|
||||||
|
|
@ -130,3 +135,38 @@ func TestCalcBaseFee(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCalcBaseFee assumes all blocks are 1559-blocks post Delhi Hard Fork
|
||||||
|
func TestCalcBaseFeeDelhi(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testConfig := copyConfig(config())
|
||||||
|
|
||||||
|
// Test Delhi Hard Fork
|
||||||
|
// Hard fork kicks in at block 8
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
parentBaseFee int64
|
||||||
|
parentGasLimit uint64
|
||||||
|
parentGasUsed uint64
|
||||||
|
expectedBaseFee int64
|
||||||
|
}{
|
||||||
|
{params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
|
||||||
|
{params.InitialBaseFee, 20000000, 9000000, 993750000}, // usage below target
|
||||||
|
{params.InitialBaseFee, 20000000, 11000000, 1006250000}, // usage above target
|
||||||
|
{params.InitialBaseFee, 20000000, 20000000, 1062500000}, // usage full
|
||||||
|
{params.InitialBaseFee, 20000000, 0, 937500000}, // usage 0
|
||||||
|
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
parent := &types.Header{
|
||||||
|
Number: big.NewInt(8),
|
||||||
|
GasLimit: test.parentGasLimit,
|
||||||
|
GasUsed: test.parentGasUsed,
|
||||||
|
BaseFee: big.NewInt(test.parentBaseFee),
|
||||||
|
}
|
||||||
|
if have, want := CalcBaseFee(testConfig, parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 {
|
||||||
|
t.Errorf("test %d: have %d want %d, ", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||||
|
|
||||||
// genUncles generates blocks with two uncle headers.
|
// genUncles generates blocks with two uncle headers.
|
||||||
func genUncles(i int, gen *BlockGen) {
|
func genUncles(i int, gen *BlockGen) {
|
||||||
if i >= 6 {
|
if i >= 7 {
|
||||||
b2 := gen.PrevBlock(i - 6).Header()
|
b2 := gen.PrevBlock(i - 6).Header()
|
||||||
b2.Extra = []byte("foo")
|
b2.Extra = []byte("foo")
|
||||||
gen.AddUncle(b2)
|
gen.AddUncle(b2)
|
||||||
|
|
@ -201,7 +201,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
|
|
||||||
// Time the insertion of the new chain.
|
// Time the insertion of the new chain.
|
||||||
// State and blocks are stored in the same DB.
|
// State and blocks are stored in the same DB.
|
||||||
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer chainman.Stop()
|
defer chainman.Stop()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
@ -317,7 +317,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
|
chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error creating chain: %v", err)
|
b.Fatalf("error creating chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ func TestHeaderVerification(t *testing.T) {
|
||||||
headers[i] = block.Header()
|
headers[i] = block.Header()
|
||||||
}
|
}
|
||||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i := 0; i < len(blocks); i++ {
|
||||||
|
|
@ -168,7 +168,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||||
t.Logf("Log header after the merging %d: %v", block.NumberU64(), string(blob))
|
t.Logf("Log header after the merging %d: %v", block.NumberU64(), string(blob))
|
||||||
}
|
}
|
||||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||||
chain, _ := NewBlockChain(testdb, nil, chainConfig, runEngine, vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, chainConfig, runEngine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
// Verify the blocks before the merging
|
// Verify the blocks before the merging
|
||||||
|
|
@ -279,11 +279,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||||
var results <-chan error
|
var results <-chan error
|
||||||
|
|
||||||
if valid {
|
if valid {
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
} else {
|
} else {
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil, nil)
|
||||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +346,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||||
defer runtime.GOMAXPROCS(old)
|
defer runtime.GOMAXPROCS(old)
|
||||||
|
|
||||||
// Start the verifications and immediately abort
|
// Start the verifications and immediately abort
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/common/prque"
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
|
|
@ -38,6 +39,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader/whitelist"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/internal/syncx"
|
"github.com/ethereum/go-ethereum/internal/syncx"
|
||||||
|
|
@ -90,7 +92,6 @@ const (
|
||||||
txLookupCacheLimit = 1024
|
txLookupCacheLimit = 1024
|
||||||
maxFutureBlocks = 256
|
maxFutureBlocks = 256
|
||||||
maxTimeFutureBlocks = 30
|
maxTimeFutureBlocks = 30
|
||||||
TriesInMemory = 128
|
|
||||||
|
|
||||||
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
||||||
//
|
//
|
||||||
|
|
@ -130,6 +131,7 @@ type CacheConfig struct {
|
||||||
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
|
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
|
||||||
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
|
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
|
||||||
Preimages bool // Whether to store preimage of trie key to the disk
|
Preimages bool // Whether to store preimage of trie key to the disk
|
||||||
|
TriesInMemory uint64 // Number of recent tries to keep in memory
|
||||||
|
|
||||||
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
||||||
}
|
}
|
||||||
|
|
@ -142,6 +144,7 @@ var DefaultCacheConfig = &CacheConfig{
|
||||||
TrieTimeLimit: 5 * time.Minute,
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
SnapshotLimit: 256,
|
SnapshotLimit: 256,
|
||||||
SnapshotWait: true,
|
SnapshotWait: true,
|
||||||
|
TriesInMemory: 128,
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockChain represents the canonical chain given a database with a genesis
|
// BlockChain represents the canonical chain given a database with a genesis
|
||||||
|
|
@ -221,10 +224,16 @@ type BlockChain struct {
|
||||||
// NewBlockChain returns a fully initialised block chain using information
|
// NewBlockChain returns a fully initialised block chain using information
|
||||||
// available in the database. It initialises the default Ethereum Validator
|
// available in the database. It initialises the default Ethereum Validator
|
||||||
// and Processor.
|
// and Processor.
|
||||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64) (*BlockChain, error) {
|
//
|
||||||
|
//nolint:gocognit
|
||||||
|
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64, checker ethereum.ChainValidator) (*BlockChain, error) {
|
||||||
if cacheConfig == nil {
|
if cacheConfig == nil {
|
||||||
cacheConfig = DefaultCacheConfig
|
cacheConfig = DefaultCacheConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cacheConfig.TriesInMemory <= 0 {
|
||||||
|
cacheConfig.TriesInMemory = DefaultCacheConfig.TriesInMemory
|
||||||
|
}
|
||||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||||
receiptsCache, _ := lru.New(receiptsCacheLimit)
|
receiptsCache, _ := lru.New(receiptsCacheLimit)
|
||||||
|
|
@ -257,7 +266,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
|
|
||||||
borReceiptsCache: borReceiptsCache,
|
borReceiptsCache: borReceiptsCache,
|
||||||
}
|
}
|
||||||
bc.forker = NewForkChoice(bc, shouldPreserve)
|
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
|
||||||
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
||||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
||||||
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
||||||
|
|
@ -825,7 +834,7 @@ func (bc *BlockChain) Stop() {
|
||||||
if !bc.cacheConfig.TrieDirtyDisabled {
|
if !bc.cacheConfig.TrieDirtyDisabled {
|
||||||
triedb := bc.stateCache.TrieDB()
|
triedb := bc.stateCache.TrieDB()
|
||||||
|
|
||||||
for _, offset := range []uint64{0, 1, TriesInMemory - 1} {
|
for _, offset := range []uint64{0, 1, bc.cacheConfig.TriesInMemory - 1} {
|
||||||
if number := bc.CurrentBlock().NumberU64(); number > offset {
|
if number := bc.CurrentBlock().NumberU64(); number > offset {
|
||||||
recent := bc.GetBlockByNumber(number - offset)
|
recent := bc.GetBlockByNumber(number - offset)
|
||||||
|
|
||||||
|
|
@ -908,6 +917,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
ancientBlocks, liveBlocks types.Blocks
|
ancientBlocks, liveBlocks types.Blocks
|
||||||
ancientReceipts, liveReceipts []types.Receipts
|
ancientReceipts, liveReceipts []types.Receipts
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do a sanity check that the provided chain is actually ordered and linked
|
// Do a sanity check that the provided chain is actually ordered and linked
|
||||||
for i := 0; i < len(blockChain); i++ {
|
for i := 0; i < len(blockChain); i++ {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
|
|
@ -933,7 +943,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
|
|
||||||
// updateHead updates the head fast sync block if the inserted blocks are better
|
// updateHead updates the head fast sync block if the inserted blocks are better
|
||||||
// and returns an indicator whether the inserted blocks are canonical.
|
// and returns an indicator whether the inserted blocks are canonical.
|
||||||
updateHead := func(head *types.Block) bool {
|
updateHead := func(head *types.Block, headers []*types.Header) bool {
|
||||||
if !bc.chainmu.TryLock() {
|
if !bc.chainmu.TryLock() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -948,6 +958,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
} else if !reorg {
|
} else if !reorg {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid, err := bc.forker.ValidateReorg(bc.CurrentFastBlock().Header(), headers)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Reorg failed", "err", err)
|
||||||
|
return false
|
||||||
|
} else if !isValid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
|
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
|
||||||
bc.currentFastBlock.Store(head)
|
bc.currentFastBlock.Store(head)
|
||||||
headFastBlockGauge.Update(int64(head.NumberU64()))
|
headFastBlockGauge.Update(int64(head.NumberU64()))
|
||||||
|
|
@ -985,10 +1003,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return 0, fmt.Errorf("containing header #%d [%x..] unknown", last.Number(), last.Hash().Bytes()[:4])
|
return 0, fmt.Errorf("containing header #%d [%x..] unknown", last.Number(), last.Hash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOR: Retrieve all the bor receipts.
|
// BOR: Retrieve all the bor receipts and also maintain the array of headers
|
||||||
|
// for bor specific reorg check.
|
||||||
borReceipts := []types.Receipts{}
|
borReceipts := []types.Receipts{}
|
||||||
|
|
||||||
|
var headers []*types.Header
|
||||||
for _, block := range blockChain {
|
for _, block := range blockChain {
|
||||||
borReceipts = append(borReceipts, []*types.Receipt{bc.GetBorReceiptByHash(block.Hash())})
|
borReceipts = append(borReceipts, []*types.Receipt{bc.GetBorReceiptByHash(block.Hash())})
|
||||||
|
headers = append(headers, block.Header())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write all chain data to ancients.
|
// Write all chain data to ancients.
|
||||||
|
|
@ -1039,7 +1061,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
}
|
}
|
||||||
// Update the current fast block because all block data is now present in DB.
|
// Update the current fast block because all block data is now present in DB.
|
||||||
previousFastBlock := bc.CurrentFastBlock().NumberU64()
|
previousFastBlock := bc.CurrentFastBlock().NumberU64()
|
||||||
if !updateHead(blockChain[len(blockChain)-1]) {
|
if !updateHead(blockChain[len(blockChain)-1], headers) {
|
||||||
// We end up here if the header chain has reorg'ed, and the blocks/receipts
|
// We end up here if the header chain has reorg'ed, and the blocks/receipts
|
||||||
// don't match the canonical chain.
|
// don't match the canonical chain.
|
||||||
if err := bc.db.TruncateHead(previousFastBlock + 1); err != nil {
|
if err := bc.db.TruncateHead(previousFastBlock + 1); err != nil {
|
||||||
|
|
@ -1075,7 +1097,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||||
skipPresenceCheck := false
|
skipPresenceCheck := false
|
||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
|
headers := make([]*types.Header, 0, len(blockChain))
|
||||||
for i, block := range blockChain {
|
for i, block := range blockChain {
|
||||||
|
// Update the headers for bor specific reorg check
|
||||||
|
headers = append(headers, block.Header())
|
||||||
|
|
||||||
// Short circuit insertion if shutting down or processing failed
|
// Short circuit insertion if shutting down or processing failed
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
return 0, errInsertionInterrupted
|
return 0, errInsertionInterrupted
|
||||||
|
|
@ -1122,7 +1148,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateHead(blockChain[len(blockChain)-1])
|
|
||||||
|
updateHead(blockChain[len(blockChain)-1], headers)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1275,7 +1302,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||||
bc.triegc.Push(root, -int64(block.NumberU64()))
|
bc.triegc.Push(root, -int64(block.NumberU64()))
|
||||||
|
|
||||||
if current := block.NumberU64(); current > TriesInMemory {
|
if current := block.NumberU64(); current > bc.cacheConfig.TriesInMemory {
|
||||||
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
// If we exceeded our memory allowance, flush matured singleton nodes to disk
|
||||||
var (
|
var (
|
||||||
nodes, imgs = triedb.Size()
|
nodes, imgs = triedb.Size()
|
||||||
|
|
@ -1285,7 +1312,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
triedb.Cap(limit - ethdb.IdealBatchSize)
|
triedb.Cap(limit - ethdb.IdealBatchSize)
|
||||||
}
|
}
|
||||||
// Find the next state trie we need to commit
|
// Find the next state trie we need to commit
|
||||||
chosen := current - TriesInMemory
|
chosen := current - bc.cacheConfig.TriesInMemory
|
||||||
|
|
||||||
// If we exceeded out time allowance, flush an entire trie to disk
|
// If we exceeded out time allowance, flush an entire trie to disk
|
||||||
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
|
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
|
||||||
|
|
@ -1297,8 +1324,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
} else {
|
} else {
|
||||||
// If we're exceeding limits but haven't reached a large enough memory gap,
|
// If we're exceeding limits but haven't reached a large enough memory gap,
|
||||||
// warn the user that the system is becoming unstable.
|
// warn the user that the system is becoming unstable.
|
||||||
if chosen < lastWrite+TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
if chosen < lastWrite+bc.cacheConfig.TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
||||||
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/TriesInMemory)
|
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TriesInMemory, "optimum", float64(chosen-lastWrite)/float64((bc.cacheConfig.TriesInMemory)))
|
||||||
}
|
}
|
||||||
// Flush an entire trie and restart the counters
|
// Flush an entire trie and restart the counters
|
||||||
triedb.Commit(header.Root, true, nil)
|
triedb.Commit(header.Root, true, nil)
|
||||||
|
|
@ -1491,6 +1518,18 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
|
||||||
it := newInsertIterator(chain, results, bc.validator)
|
it := newInsertIterator(chain, results, bc.validator)
|
||||||
block, err := it.next()
|
block, err := it.next()
|
||||||
|
|
||||||
|
// Check the validity of incoming chain
|
||||||
|
isValid, err1 := bc.forker.ValidateReorg(bc.CurrentBlock().Header(), headers)
|
||||||
|
if err1 != nil {
|
||||||
|
return it.index, err1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValid {
|
||||||
|
// The chain to be imported is invalid as the blocks doesn't match with
|
||||||
|
// the whitelisted checkpoints.
|
||||||
|
return it.index, whitelist.ErrCheckpointMismatch
|
||||||
|
}
|
||||||
|
|
||||||
// Left-trim all the known blocks that don't need to build snapshot
|
// Left-trim all the known blocks that don't need to build snapshot
|
||||||
if bc.skipBlock(err, it) {
|
if bc.skipBlock(err, it) {
|
||||||
// First block (and state) is known
|
// First block (and state) is known
|
||||||
|
|
@ -1833,6 +1872,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
externTd *big.Int
|
externTd *big.Int
|
||||||
lastBlock = block
|
lastBlock = block
|
||||||
current = bc.CurrentBlock()
|
current = bc.CurrentBlock()
|
||||||
|
headers []*types.Header
|
||||||
)
|
)
|
||||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||||
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
||||||
|
|
@ -1840,6 +1880,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// to disk.
|
// to disk.
|
||||||
err := consensus.ErrPrunedAncestor
|
err := consensus.ErrPrunedAncestor
|
||||||
for ; block != nil && errors.Is(err, consensus.ErrPrunedAncestor); block, err = it.next() {
|
for ; block != nil && errors.Is(err, consensus.ErrPrunedAncestor); block, err = it.next() {
|
||||||
|
headers = append(headers, block.Header())
|
||||||
// Check the canonical state root for that number
|
// Check the canonical state root for that number
|
||||||
if number := block.NumberU64(); current.NumberU64() >= number {
|
if number := block.NumberU64(); current.NumberU64() >= number {
|
||||||
canonical := bc.GetBlockByNumber(number)
|
canonical := bc.GetBlockByNumber(number)
|
||||||
|
|
@ -1895,7 +1936,13 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
if !reorg {
|
|
||||||
|
isValid, err := bc.forker.ValidateReorg(current.Header(), headers)
|
||||||
|
if err != nil {
|
||||||
|
return it.index, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reorg || !isValid {
|
||||||
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
||||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
||||||
return it.index, err
|
return it.index, err
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ func TestChain2HeadEvent(t *testing.T) {
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
chain2HeadCh := make(chan Chain2HeadEvent, 64)
|
chain2HeadCh := make(chan Chain2HeadEvent, 64)
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize a fresh chain with only a genesis block
|
// Initialize a fresh chain with only a genesis block
|
||||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
// Create and inject the requested chain
|
// Create and inject the requested chain
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return db, blockchain, nil
|
return db, blockchain, nil
|
||||||
|
|
@ -655,7 +655,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
|
||||||
blockchain.Stop()
|
blockchain.Stop()
|
||||||
|
|
||||||
// Create a new BlockChain and check that it rolled back the state.
|
// Create a new BlockChain and check that it rolled back the state.
|
||||||
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new chain manager: %v", err)
|
t.Fatalf("failed to create new chain manager: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -768,7 +768,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
// Import the chain as an archive node for the comparison baseline
|
// Import the chain as an archive node for the comparison baseline
|
||||||
archiveDb := rawdb.NewMemoryDatabase()
|
archiveDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(archiveDb)
|
gspec.MustCommit(archiveDb)
|
||||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer archive.Stop()
|
defer archive.Stop()
|
||||||
|
|
||||||
if n, err := archive.InsertChain(blocks); err != nil {
|
if n, err := archive.InsertChain(blocks); err != nil {
|
||||||
|
|
@ -777,7 +777,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
// Fast import the chain as a non-archive node to test
|
// Fast import the chain as a non-archive node to test
|
||||||
fastDb := rawdb.NewMemoryDatabase()
|
fastDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(fastDb)
|
gspec.MustCommit(fastDb)
|
||||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
|
|
@ -801,7 +801,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
gspec.MustCommit(ancientDb)
|
gspec.MustCommit(ancientDb)
|
||||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
||||||
|
|
@ -923,7 +923,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
archiveCaching := *DefaultCacheConfig
|
archiveCaching := *DefaultCacheConfig
|
||||||
archiveCaching.TrieDirtyDisabled = true
|
archiveCaching.TrieDirtyDisabled = true
|
||||||
|
|
||||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if n, err := archive.InsertChain(blocks); err != nil {
|
if n, err := archive.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to process block %d: %v", n, err)
|
t.Fatalf("failed to process block %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -936,7 +936,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||||
fastDb, delfn := makeDb()
|
fastDb, delfn := makeDb()
|
||||||
defer delfn()
|
defer delfn()
|
||||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
|
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
|
|
@ -956,7 +957,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||||
ancientDb, delfn := makeDb()
|
ancientDb, delfn := makeDb()
|
||||||
defer delfn()
|
defer delfn()
|
||||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
|
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
||||||
|
|
@ -975,7 +977,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
// Import the chain as a light node and ensure all pointers are updated
|
// Import the chain as a light node and ensure all pointers are updated
|
||||||
lightDb, delfn := makeDb()
|
lightDb, delfn := makeDb()
|
||||||
defer delfn()
|
defer delfn()
|
||||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
|
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
@ -1044,7 +1047,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -1114,7 +1117,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
rmLogsCh := make(chan RemovedLogsEvent)
|
rmLogsCh := make(chan RemovedLogsEvent)
|
||||||
|
|
@ -1167,7 +1170,7 @@ func TestLogRebirth(t *testing.T) {
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
@ -1230,7 +1233,7 @@ func TestSideLogRebirth(t *testing.T) {
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
@ -1305,7 +1308,7 @@ func TestReorgSideEvent(t *testing.T) {
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
||||||
|
|
@ -1437,7 +1440,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
)
|
)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||||
|
|
@ -1545,7 +1548,8 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
)
|
)
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
|
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
|
||||||
|
|
@ -1620,7 +1624,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1648,7 +1652,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||||
|
|
||||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||||
forks := make([]*types.Block, len(blocks))
|
forks := make([]*types.Block, len(blocks))
|
||||||
|
|
@ -1664,7 +1668,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1677,7 +1681,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dereference all the recent tries and ensure no past trie is left in
|
// Dereference all the recent tries and ensure no past trie is left in
|
||||||
for i := 0; i < TriesInMemory; i++ {
|
for i := 0; i < int(chain.cacheConfig.TriesInMemory); i++ {
|
||||||
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
|
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
|
||||||
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
|
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
|
||||||
}
|
}
|
||||||
|
|
@ -1696,14 +1700,14 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||||
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||||
|
|
||||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||||
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||||
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*int(DefaultCacheConfig.TriesInMemory)+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||||
|
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1732,7 +1736,8 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||||
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
|
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
|
||||||
t.Fatalf("failed to finalize competitor chain: %v", err)
|
t.Fatalf("failed to finalize competitor chain: %v", err)
|
||||||
}
|
}
|
||||||
for i, block := range competitor[:len(competitor)-TriesInMemory] {
|
|
||||||
|
for i, block := range competitor[:len(competitor)-int(chain.cacheConfig.TriesInMemory)] {
|
||||||
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
|
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
|
||||||
t.Fatalf("competitor %d: competing chain state missing", i)
|
t.Fatalf("competitor %d: competing chain state missing", i)
|
||||||
}
|
}
|
||||||
|
|
@ -1764,7 +1769,7 @@ func TestBlockchainRecovery(t *testing.T) {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
gspec.MustCommit(ancientDb)
|
gspec.MustCommit(ancientDb)
|
||||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
@ -1784,7 +1789,7 @@ func TestBlockchainRecovery(t *testing.T) {
|
||||||
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
|
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
|
||||||
|
|
||||||
// Reopen broken blockchain again
|
// Reopen broken blockchain again
|
||||||
ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
if num := ancient.CurrentBlock().NumberU64(); num != 0 {
|
if num := ancient.CurrentBlock().NumberU64(); num != 0 {
|
||||||
t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
|
t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
|
||||||
|
|
@ -1836,7 +1841,7 @@ func TestInsertReceiptChainRollback(t *testing.T) {
|
||||||
}
|
}
|
||||||
gspec := Genesis{Config: params.AllEthashProtocolChanges}
|
gspec := Genesis{Config: params.AllEthashProtocolChanges}
|
||||||
gspec.MustCommit(ancientDb)
|
gspec.MustCommit(ancientDb)
|
||||||
ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer ancientChain.Stop()
|
defer ancientChain.Stop()
|
||||||
|
|
||||||
// Import the canonical header chain.
|
// Import the canonical header chain.
|
||||||
|
|
@ -1878,8 +1883,8 @@ func TestInsertReceiptChainRollback(t *testing.T) {
|
||||||
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
||||||
//
|
//
|
||||||
// Details at:
|
// Details at:
|
||||||
// - https://github.com/ethereum/go-ethereum/issues/18977
|
// - https://github.com/ethereum/go-ethereum/issues/18977
|
||||||
// - https://github.com/ethereum/go-ethereum/pull/18988
|
// - https://github.com/ethereum/go-ethereum/pull/18988
|
||||||
func TestLowDiffLongChain(t *testing.T) {
|
func TestLowDiffLongChain(t *testing.T) {
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
@ -1888,7 +1893,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||||
|
|
||||||
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
||||||
// until after at least 128 blocks post tip
|
// until after at least 128 blocks post tip
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) {
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{1})
|
b.SetCoinbase(common.Address{1})
|
||||||
b.OffsetTime(-9)
|
b.OffsetTime(-9)
|
||||||
})
|
})
|
||||||
|
|
@ -1897,7 +1902,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1906,7 +1911,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Generate fork chain, starting from an early block
|
// Generate fork chain, starting from an early block
|
||||||
parent := blocks[10]
|
parent := blocks[10]
|
||||||
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) {
|
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{2})
|
b.SetCoinbase(common.Address{2})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -1963,7 +1968,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// Generate and import the canonical chain
|
// Generate and import the canonical chain
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(diskdb)
|
gspec.MustCommit(diskdb)
|
||||||
chain, err := NewBlockChain(diskdb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1975,7 +1980,8 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// Set the terminal total difficulty in the config
|
// Set the terminal total difficulty in the config
|
||||||
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
|
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
|
||||||
}
|
}
|
||||||
blocks, _ := GenerateChain(&chainConfig, genesis, genEngine, db, 2*TriesInMemory, func(i int, gen *BlockGen) {
|
|
||||||
|
blocks, _ := GenerateChain(&chainConfig, genesis, genEngine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) {
|
||||||
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
|
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tx: %v", err)
|
t.Fatalf("failed to create tx: %v", err)
|
||||||
|
|
@ -1987,9 +1993,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPrunedIndex := len(blocks) - TriesInMemory - 1
|
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
|
||||||
lastPrunedBlock := blocks[lastPrunedIndex]
|
lastPrunedBlock := blocks[lastPrunedIndex]
|
||||||
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
|
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
|
||||||
|
|
||||||
// Verify pruning of lastPrunedBlock
|
// Verify pruning of lastPrunedBlock
|
||||||
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
||||||
|
|
@ -2015,7 +2021,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// Generate fork chain, make it longer than canon
|
// Generate fork chain, make it longer than canon
|
||||||
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
|
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
|
||||||
parent := blocks[parentIndex]
|
parent := blocks[parentIndex]
|
||||||
fork, _ := GenerateChain(&chainConfig, parent, genEngine, db, 2*TriesInMemory, func(i int, b *BlockGen) {
|
fork, _ := GenerateChain(&chainConfig, parent, genEngine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{2})
|
b.SetCoinbase(common.Address{2})
|
||||||
})
|
})
|
||||||
// Prepend the parent(s)
|
// Prepend the parent(s)
|
||||||
|
|
@ -2042,7 +2048,8 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// That is: the sidechain for import contains some blocks already present in canon chain.
|
// That is: the sidechain for import contains some blocks already present in canon chain.
|
||||||
// So the blocks are
|
// So the blocks are
|
||||||
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
||||||
// ^ ^ ^ pruned
|
//
|
||||||
|
// ^ ^ ^ pruned
|
||||||
func TestPrunedImportSide(t *testing.T) {
|
func TestPrunedImportSide(t *testing.T) {
|
||||||
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
||||||
//glogger.Verbosity(3)
|
//glogger.Verbosity(3)
|
||||||
|
|
@ -2102,7 +2109,7 @@ func testInsertKnownChainData(t *testing.T, typ string) {
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2266,7 +2273,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
chain, err := NewBlockChain(chaindb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(chaindb, nil, &chainConfig, runEngine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2384,7 +2391,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
|
return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2577,7 +2584,7 @@ func TestTransactionIndices(t *testing.T) {
|
||||||
|
|
||||||
// Import all blocks into ancient db
|
// Import all blocks into ancient db
|
||||||
l := uint64(0)
|
l := uint64(0)
|
||||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2602,7 +2609,7 @@ func TestTransactionIndices(t *testing.T) {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
gspec.MustCommit(ancientDb)
|
gspec.MustCommit(ancientDb)
|
||||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2626,7 +2633,7 @@ func TestTransactionIndices(t *testing.T) {
|
||||||
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
|
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
|
||||||
tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0}
|
tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0}
|
||||||
for i, l := range limit {
|
for i, l := range limit {
|
||||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2704,7 +2711,7 @@ func TestSkipStaleTxIndicesInSnapSync(t *testing.T) {
|
||||||
|
|
||||||
// Import all blocks into ancient db, only HEAD-32 indices are kept.
|
// Import all blocks into ancient db, only HEAD-32 indices are kept.
|
||||||
l := uint64(32)
|
l := uint64(32)
|
||||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2768,7 +2775,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(diskdb)
|
gspec.MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create tester chain: %v", err)
|
b.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2837,9 +2844,9 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
||||||
// This internally leads to a sidechain import, since the blocks trigger an
|
// This internally leads to a sidechain import, since the blocks trigger an
|
||||||
// ErrPrunedAncestor error.
|
// ErrPrunedAncestor error.
|
||||||
// This may e.g. happen if
|
// This may e.g. happen if
|
||||||
// 1. Downloader rollbacks a batch of inserted blocks and exits
|
// 1. Downloader rollbacks a batch of inserted blocks and exits
|
||||||
// 2. Downloader starts to sync again
|
// 2. Downloader starts to sync again
|
||||||
// 3. The blocks fetched are all known and canonical blocks
|
// 3. The blocks fetched are all known and canonical blocks
|
||||||
func TestSideImportPrunedBlocks(t *testing.T) {
|
func TestSideImportPrunedBlocks(t *testing.T) {
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
@ -2847,11 +2854,11 @@ func TestSideImportPrunedBlocks(t *testing.T) {
|
||||||
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||||
|
|
||||||
// Generate and import the canonical chain
|
// Generate and import the canonical chain
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil)
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*int(DefaultCacheConfig.TriesInMemory), nil)
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
|
|
||||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2859,14 +2866,15 @@ func TestSideImportPrunedBlocks(t *testing.T) {
|
||||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPrunedIndex := len(blocks) - TriesInMemory - 1
|
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
|
||||||
lastPrunedBlock := blocks[lastPrunedIndex]
|
lastPrunedBlock := blocks[lastPrunedIndex]
|
||||||
|
|
||||||
// Verify pruning of lastPrunedBlock
|
// Verify pruning of lastPrunedBlock
|
||||||
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
||||||
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
|
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
|
||||||
}
|
}
|
||||||
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
|
|
||||||
|
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
|
||||||
// Verify firstNonPrunedBlock is not pruned
|
// Verify firstNonPrunedBlock is not pruned
|
||||||
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
||||||
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
||||||
|
|
@ -2945,7 +2953,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(diskdb)
|
gspec.MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3059,7 +3067,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||||
}, nil, nil)
|
}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3139,7 +3147,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
||||||
}, nil, nil)
|
}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3312,7 +3320,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||||
//Debug: true,
|
//Debug: true,
|
||||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||||
}, nil, nil)
|
}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3352,20 +3360,19 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
|
||||||
|
|
||||||
// TestInitThenFailCreateContract tests a pretty notorious case that happened
|
// TestInitThenFailCreateContract tests a pretty notorious case that happened
|
||||||
// on mainnet over blocks 7338108, 7338110 and 7338115.
|
// on mainnet over blocks 7338108, 7338110 and 7338115.
|
||||||
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
|
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
|
||||||
// with 0.001 ether (thus created but no code)
|
// with 0.001 ether (thus created but no code)
|
||||||
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
|
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
|
||||||
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
|
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
|
||||||
// deployment fails due to OOG during initcode execution
|
// deployment fails due to OOG during initcode execution
|
||||||
// - Block 7338115: another tx checks the balance of
|
// - Block 7338115: another tx checks the balance of
|
||||||
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
|
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
|
||||||
// zero.
|
// zero.
|
||||||
//
|
//
|
||||||
// The problem being that the snapshotter maintains a destructset, and adds items
|
// The problem being that the snapshotter maintains a destructset, and adds items
|
||||||
// to the destructset in case something is created "onto" an existing item.
|
// to the destructset in case something is created "onto" an existing item.
|
||||||
// We need to either roll back the snapDestructs, or not place it into snapDestructs
|
// We need to either roll back the snapDestructs, or not place it into snapDestructs
|
||||||
// in the first place.
|
// in the first place.
|
||||||
//
|
|
||||||
func TestInitThenFailCreateContract(t *testing.T) {
|
func TestInitThenFailCreateContract(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
|
|
@ -3446,7 +3453,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||||
//Debug: true,
|
//Debug: true,
|
||||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||||
}, nil, nil)
|
}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3533,7 +3540,7 @@ func TestEIP2718Transition(t *testing.T) {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(diskdb)
|
gspec.MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3554,13 +3561,13 @@ func TestEIP2718Transition(t *testing.T) {
|
||||||
|
|
||||||
// TestEIP1559Transition tests the following:
|
// TestEIP1559Transition tests the following:
|
||||||
//
|
//
|
||||||
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
|
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
|
||||||
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
|
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
|
||||||
// 3. Only the transaction's tip will be received by the coinbase.
|
// 3. Only the transaction's tip will be received by the coinbase.
|
||||||
// 4. The transaction sender pays for both the tip and baseFee.
|
// 4. The transaction sender pays for both the tip and baseFee.
|
||||||
// 5. The coinbase receives only the partially realized tip when
|
// 5. The coinbase receives only the partially realized tip when
|
||||||
// gasFeeCap - gasTipCap < baseFee.
|
// gasFeeCap - gasTipCap < baseFee.
|
||||||
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
|
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
|
||||||
func TestEIP1559Transition(t *testing.T) {
|
func TestEIP1559Transition(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
|
|
@ -3628,7 +3635,7 @@ func TestEIP1559Transition(t *testing.T) {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(diskdb)
|
gspec.MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -258,7 +259,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
}
|
}
|
||||||
if b.engine != nil {
|
if b.engine != nil {
|
||||||
// Finalize and seal the block
|
// Finalize and seal the block
|
||||||
block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
|
block, _ := b.engine.FinalizeAndAssemble(context.Background(), chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
|
||||||
|
|
||||||
// Write state changes to db
|
// Write state changes to db
|
||||||
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ func ExampleGenerateChain() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
proConf.DAOForkBlock = forkBlock
|
proConf.DAOForkBlock = forkBlock
|
||||||
proConf.DAOForkSupport = true
|
proConf.DAOForkSupport = true
|
||||||
|
|
||||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer proBc.Stop()
|
defer proBc.Stop()
|
||||||
|
|
||||||
conDb := rawdb.NewMemoryDatabase()
|
conDb := rawdb.NewMemoryDatabase()
|
||||||
|
|
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
conConf.DAOForkBlock = forkBlock
|
conConf.DAOForkBlock = forkBlock
|
||||||
conConf.DAOForkSupport = false
|
conConf.DAOForkSupport = false
|
||||||
|
|
||||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer conBc.Stop()
|
defer conBc.Stop()
|
||||||
|
|
||||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||||
|
|
@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
// Create a pro-fork block, and try to feed into the no-fork chain
|
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
||||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||||
|
|
@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
// Create a no-fork block, and try to feed into the pro-fork chain
|
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
||||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||||
|
|
@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
||||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||||
|
|
@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
||||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,14 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
crand "crypto/rand"
|
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
mrand "math/rand"
|
|
||||||
|
|
||||||
|
"github.com/maticnetwork/crand"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"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/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -47,25 +46,29 @@ type ChainReader interface {
|
||||||
// for all other proof-of-work networks.
|
// for all other proof-of-work networks.
|
||||||
type ForkChoice struct {
|
type ForkChoice struct {
|
||||||
chain ChainReader
|
chain ChainReader
|
||||||
rand *mrand.Rand
|
rand Floater
|
||||||
|
|
||||||
// preserve is a helper function used in td fork choice.
|
// preserve is a helper function used in td fork choice.
|
||||||
// Miners will prefer to choose the local mined block if the
|
// Miners will prefer to choose the local mined block if the
|
||||||
// local td is equal to the extern one. It can be nil for light
|
// local td is equal to the extern one. It can be nil for light
|
||||||
// client
|
// client
|
||||||
preserve func(header *types.Header) bool
|
preserve func(header *types.Header) bool
|
||||||
|
|
||||||
|
validator ethereum.ChainValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool) *ForkChoice {
|
type Floater interface {
|
||||||
|
Float64() float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool, validator ethereum.ChainValidator) *ForkChoice {
|
||||||
// Seed a fast but crypto originating random generator
|
// Seed a fast but crypto originating random generator
|
||||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
r := crand.NewRand()
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to initialize random seed", "err", err)
|
|
||||||
}
|
|
||||||
return &ForkChoice{
|
return &ForkChoice{
|
||||||
chain: chainReader,
|
chain: chainReader,
|
||||||
rand: mrand.New(mrand.NewSource(seed.Int64())),
|
rand: r,
|
||||||
preserve: preserve,
|
preserve: preserve,
|
||||||
|
validator: validator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,3 +109,15 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, header *types.Header) (b
|
||||||
}
|
}
|
||||||
return reorg, nil
|
return reorg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateReorg calls the chain validator service to check if the reorg is valid or not
|
||||||
|
func (f *ForkChoice) ValidateReorg(current *types.Header, chain []*types.Header) (bool, error) {
|
||||||
|
// Call the bor chain validator service
|
||||||
|
if f.validator != nil {
|
||||||
|
if isValid := f.validator.IsValidChain(current, chain); !isValid {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
240
core/forkchoice_test.go
Normal file
240
core/forkchoice_test.go
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// chainValidatorFake is a mock for the chain validator service
|
||||||
|
type chainValidatorFake struct {
|
||||||
|
validate func(currentHeader *types.Header, chain []*types.Header) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// chainReaderFake is a mock for the chain reader service
|
||||||
|
type chainReaderFake struct {
|
||||||
|
getTd func(hash common.Hash, number uint64) *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChainValidatorFake(validate func(currentHeader *types.Header, chain []*types.Header) bool) *chainValidatorFake {
|
||||||
|
return &chainValidatorFake{validate: validate}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChainReaderFake(getTd func(hash common.Hash, number uint64) *big.Int) *chainReaderFake {
|
||||||
|
return &chainReaderFake{getTd: getTd}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPastChainInsert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||||
|
)
|
||||||
|
|
||||||
|
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mocks for forker
|
||||||
|
getTd := func(hash common.Hash, number uint64) *big.Int {
|
||||||
|
return big.NewInt(int64(number))
|
||||||
|
}
|
||||||
|
validate := func(currentHeader *types.Header, chain []*types.Header) bool {
|
||||||
|
// Put all explicit conditions here
|
||||||
|
// If canonical chain is empty and we're importing a chain of 64 blocks
|
||||||
|
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// If canonical chain is of len 64 and we're importing a past chain from 54-64, then accept it
|
||||||
|
if currentHeader.Number.Uint64() == uint64(64) && chain[0].Number.Uint64() == 55 && len(chain) == 10 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mockChainReader := newChainReaderFake(getTd)
|
||||||
|
mockChainValidator := newChainValidatorFake(validate)
|
||||||
|
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
|
// chain A: G->A1->A2...A64
|
||||||
|
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 64 headers on an empty chain
|
||||||
|
// expecting 1 write status with no error
|
||||||
|
testInsert(t, hc, chainA, CanonStatTy, nil, mockForker)
|
||||||
|
|
||||||
|
// The current chain is: G->A1->A2...A64
|
||||||
|
// chain B: G->A1->A2...A44->B45->B46...B64
|
||||||
|
chainB := makeHeaderChain(chainA[43], 20, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// The current chain is: G->A1->A2...A64
|
||||||
|
// chain C: G->A1->A2...A54->C55->C56...C64
|
||||||
|
chainC := makeHeaderChain(chainA[53], 10, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Update the function to consider chainC with higher difficulty
|
||||||
|
getTd = func(hash common.Hash, number uint64) *big.Int {
|
||||||
|
td := big.NewInt(int64(number))
|
||||||
|
if hash == chainB[len(chainB)-1].Hash() || hash == chainC[len(chainC)-1].Hash() {
|
||||||
|
td = big.NewInt(65)
|
||||||
|
}
|
||||||
|
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
mockChainReader = newChainReaderFake(getTd)
|
||||||
|
mockForker = NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
|
// Inserting 20 blocks from chainC on canonical chain
|
||||||
|
// expecting 2 write status with no error
|
||||||
|
testInsert(t, hc, chainB, SideStatTy, nil, mockForker)
|
||||||
|
|
||||||
|
// Inserting 10 blocks from chainB on canonical chain
|
||||||
|
// expecting 1 write status with no error
|
||||||
|
testInsert(t, hc, chainC, CanonStatTy, nil, mockForker)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFutureChainInsert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||||
|
)
|
||||||
|
|
||||||
|
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mocks for forker
|
||||||
|
getTd := func(hash common.Hash, number uint64) *big.Int {
|
||||||
|
return big.NewInt(int64(number))
|
||||||
|
}
|
||||||
|
validate := func(currentHeader *types.Header, chain []*types.Header) bool {
|
||||||
|
// Put all explicit conditions here
|
||||||
|
// If canonical chain is empty and we're importing a chain of 64 blocks
|
||||||
|
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// If length of future chains > some value, they should not be accepted
|
||||||
|
if currentHeader.Number.Uint64() == uint64(64) && len(chain) <= 10 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mockChainReader := newChainReaderFake(getTd)
|
||||||
|
mockChainValidator := newChainValidatorFake(validate)
|
||||||
|
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
|
// chain A: G->A1->A2...A64
|
||||||
|
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 64 headers on an empty chain
|
||||||
|
// expecting 1 write status with no error
|
||||||
|
testInsert(t, hc, chainA, CanonStatTy, nil, mockForker)
|
||||||
|
|
||||||
|
// The current chain is: G->A1->A2...A64
|
||||||
|
// chain B: G->A1->A2...A64->B65->B66...B84
|
||||||
|
chainB := makeHeaderChain(chainA[63], 20, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 20 headers on the canonical chain
|
||||||
|
// expecting 0 write status with no error
|
||||||
|
testInsert(t, hc, chainB, SideStatTy, nil, mockForker)
|
||||||
|
|
||||||
|
// The current chain is: G->A1->A2...A64
|
||||||
|
// chain C: G->A1->A2...A64->C65->C66...C74
|
||||||
|
chainC := makeHeaderChain(chainA[63], 10, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 10 headers on the canonical chain
|
||||||
|
// expecting 0 write status with no error
|
||||||
|
testInsert(t, hc, chainC, CanonStatTy, nil, mockForker)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverlappingChainInsert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||||
|
)
|
||||||
|
|
||||||
|
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mocks for forker
|
||||||
|
getTd := func(hash common.Hash, number uint64) *big.Int {
|
||||||
|
return big.NewInt(int64(number))
|
||||||
|
}
|
||||||
|
validate := func(currentHeader *types.Header, chain []*types.Header) bool {
|
||||||
|
// Put all explicit conditions here
|
||||||
|
// If canonical chain is empty and we're importing a chain of 64 blocks
|
||||||
|
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// If length of chain is > some fixed value then don't accept it
|
||||||
|
if currentHeader.Number.Uint64() == uint64(64) && len(chain) <= 20 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mockChainReader := newChainReaderFake(getTd)
|
||||||
|
mockChainValidator := newChainValidatorFake(validate)
|
||||||
|
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
|
// chain A: G->A1->A2...A64
|
||||||
|
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 64 headers on an empty chain
|
||||||
|
// expecting 1 write status with no error
|
||||||
|
testInsert(t, hc, chainA, CanonStatTy, nil, mockForker)
|
||||||
|
|
||||||
|
// The current chain is: G->A1->A2...A64
|
||||||
|
// chain B: G->A1->A2...A54->B55->B56...B84
|
||||||
|
chainB := makeHeaderChain(chainA[53], 30, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 20 blocks on canonical chain
|
||||||
|
// expecting 2 write status with no error
|
||||||
|
testInsert(t, hc, chainB, SideStatTy, nil, mockForker)
|
||||||
|
|
||||||
|
// The current chain is: G->A1->A2...A64
|
||||||
|
// chain C: G->A1->A2...A54->C55->C56...C74
|
||||||
|
chainC := makeHeaderChain(chainA[53], 20, ethash.NewFaker(), db, 10)
|
||||||
|
|
||||||
|
// Inserting 10 blocks on canonical chain
|
||||||
|
// expecting 1 write status with no error
|
||||||
|
testInsert(t, hc, chainC, CanonStatTy, nil, mockForker)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock chain reader functions
|
||||||
|
func (c *chainReaderFake) Config() *params.ChainConfig {
|
||||||
|
return ¶ms.ChainConfig{TerminalTotalDifficulty: nil}
|
||||||
|
}
|
||||||
|
func (c *chainReaderFake) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
|
return c.getTd(hash, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock chain validator functions
|
||||||
|
func (w *chainValidatorFake) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
func (w *chainValidatorFake) IsValidChain(current *types.Header, headers []*types.Header) bool {
|
||||||
|
return w.validate(current, headers)
|
||||||
|
}
|
||||||
|
func (w *chainValidatorFake) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {}
|
||||||
|
func (w *chainValidatorFake) GetCheckpointWhitelist() map[uint64]common.Hash {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (w *chainValidatorFake) PurgeCheckpointWhitelist() {}
|
||||||
|
func (w *chainValidatorFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
|
||||||
|
return map[uint64]*types.Header{}, nil
|
||||||
|
}
|
||||||
|
|
@ -117,7 +117,7 @@ func TestSetupGenesis(t *testing.T) {
|
||||||
// Advance to block #4, past the homestead transition block of customg.
|
// Advance to block #4, past the homestead transition block of customg.
|
||||||
genesis := oldcustomg.MustCommit(db)
|
genesis := oldcustomg.MustCommit(db)
|
||||||
|
|
||||||
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
||||||
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
|
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
|
||||||
|
|
|
||||||
|
|
@ -283,8 +283,10 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
|
||||||
lastHeader: lastHeader,
|
lastHeader: lastHeader,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ask the fork choicer if the reorg is necessary
|
// Ask the fork choicer if the reorg is necessary
|
||||||
if reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader); err != nil {
|
reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !reorg {
|
} else if !reorg {
|
||||||
if inserted != 0 {
|
if inserted != 0 {
|
||||||
|
|
@ -292,6 +294,16 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid, err := forker.ValidateReorg(hc.CurrentHeader(), headers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !isValid {
|
||||||
|
if inserted != 0 {
|
||||||
|
result.status = SideStatTy
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
// Special case, all the inserted headers are already on the canonical
|
// Special case, all the inserted headers are already on the canonical
|
||||||
// header chain, skip the reorg operation.
|
// header chain, skip the reorg operation.
|
||||||
if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {
|
if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ func TestHeaderInsertion(t *testing.T) {
|
||||||
chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10)
|
chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10)
|
||||||
log.Root().SetHandler(log.StdoutHandler)
|
log.Root().SetHandler(log.StdoutHandler)
|
||||||
|
|
||||||
forker := NewForkChoice(hc, nil)
|
forker := NewForkChoice(hc, nil, nil)
|
||||||
// Inserting 64 headers on an empty chain, expecting
|
// Inserting 64 headers on an empty chain, expecting
|
||||||
// 1 callbacks, 1 canon-status, 0 sidestatus,
|
// 1 callbacks, 1 canon-status, 0 sidestatus,
|
||||||
testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)
|
testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,11 @@ var (
|
||||||
getDerivedBorTxHash = types.GetDerivedBorTxHash
|
getDerivedBorTxHash = types.GetDerivedBorTxHash
|
||||||
|
|
||||||
// borTxLookupPrefix + hash -> transaction/receipt lookup metadata
|
// borTxLookupPrefix + hash -> transaction/receipt lookup metadata
|
||||||
borTxLookupPrefix = []byte("matic-bor-tx-lookup-")
|
borTxLookupPrefix = []byte(borTxLookupPrefixStr)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
borTxLookupPrefixStr = "matic-bor-tx-lookup-"
|
||||||
|
|
||||||
// freezerBorReceiptTable indicates the name of the freezer bor receipts table.
|
// freezerBorReceiptTable indicates the name of the freezer bor receipts table.
|
||||||
freezerBorReceiptTable = "matic-bor-receipts"
|
freezerBorReceiptTable = "matic-bor-receipts"
|
||||||
|
|
|
||||||
|
|
@ -601,8 +601,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
|
||||||
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
|
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
|
||||||
// a contract does the following:
|
// a contract does the following:
|
||||||
//
|
//
|
||||||
// 1. sends funds to sha(account ++ (nonce + 1))
|
// 1. sends funds to sha(account ++ (nonce + 1))
|
||||||
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||||
//
|
//
|
||||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||||
func (s *StateDB) CreateAccount(addr common.Address) {
|
func (s *StateDB) CreateAccount(addr common.Address) {
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||||
|
|
@ -235,7 +235,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
for i, tt := range []struct {
|
for i, tt := range []struct {
|
||||||
|
|
@ -275,7 +275,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
for i, tt := range []struct {
|
for i, tt := range []struct {
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,10 @@ The state transitioning model does all the necessary work to work out a valid ne
|
||||||
3) Create a new state object if the recipient is \0*32
|
3) Create a new state object if the recipient is \0*32
|
||||||
4) Value transfer
|
4) Value transfer
|
||||||
== If contract creation ==
|
== If contract creation ==
|
||||||
4a) Attempt to run transaction data
|
|
||||||
4b) If valid, use result as code for the new state object
|
4a) Attempt to run transaction data
|
||||||
|
4b) If valid, use result as code for the new state object
|
||||||
|
|
||||||
== end ==
|
== end ==
|
||||||
5) Run Script section
|
5) Run Script section
|
||||||
6) Derive new state root
|
6) Derive new state root
|
||||||
|
|
@ -262,13 +264,13 @@ func (st *StateTransition) preCheck() error {
|
||||||
// TransitionDb will transition the state by applying the current message and
|
// TransitionDb will transition the state by applying the current message and
|
||||||
// returning the evm execution result with following fields.
|
// returning the evm execution result with following fields.
|
||||||
//
|
//
|
||||||
// - used gas:
|
// - used gas:
|
||||||
// total gas used (including gas being refunded)
|
// total gas used (including gas being refunded)
|
||||||
// - returndata:
|
// - returndata:
|
||||||
// the returned data from evm
|
// the returned data from evm
|
||||||
// - concrete execution error:
|
// - concrete execution error:
|
||||||
// various **EVM** error which aborts the execution,
|
// various **EVM** error which aborts the execution,
|
||||||
// e.g. ErrOutOfGas, ErrExecutionReverted
|
// e.g. ErrOutOfGas, ErrExecutionReverted
|
||||||
//
|
//
|
||||||
// However if any consensus issue encountered, return the error directly with
|
// However if any consensus issue encountered, return the error directly with
|
||||||
// nil evm execution result.
|
// nil evm execution result.
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import (
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/api"
|
"github.com/ethereum/go-ethereum/consensus/bor/api"
|
||||||
|
|
@ -1788,6 +1787,8 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
}
|
}
|
||||||
defer db.Close() // Might double close, should be fine
|
defer db.Close() // Might double close, should be fine
|
||||||
|
|
||||||
|
chainConfig := params.BorUnittestChainConfig
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
|
@ -1795,10 +1796,10 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||||
|
|
||||||
spanner := bor.NewMockSpanner(ctrl)
|
spanner := bor.NewMockSpanner(ctrl)
|
||||||
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{
|
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{
|
||||||
{
|
{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Address: common.Address{0x1},
|
Address: miner.TestBankAddress,
|
||||||
VotingPower: 100,
|
VotingPower: 100,
|
||||||
ProposerPriority: 0,
|
ProposerPriority: 0,
|
||||||
},
|
},
|
||||||
|
|
@ -1809,44 +1810,15 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
|
|
||||||
contractMock := bor.NewMockGenesisContract(ctrl)
|
contractMock := bor.NewMockGenesisContract(ctrl)
|
||||||
|
|
||||||
// Initialize a fresh chain
|
engine := miner.NewFakeBor(t, db, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
|
||||||
var (
|
|
||||||
gspec = &core.Genesis{
|
|
||||||
Config: params.BorUnittestChainConfig,
|
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
||||||
}
|
|
||||||
config = &core.CacheConfig{
|
|
||||||
TrieCleanLimit: 256,
|
|
||||||
TrieDirtyLimit: 256,
|
|
||||||
TrieTimeLimit: 5 * time.Minute,
|
|
||||||
SnapshotLimit: 0, // Disable snapshot by default
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
engine := miner.NewFakeBor(t, db, params.BorUnittestChainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
|
|
||||||
defer engine.Close()
|
defer engine.Close()
|
||||||
|
|
||||||
engineBorInternal, ok := engine.(*bor.Bor)
|
chainConfig.LondonBlock = big.NewInt(0)
|
||||||
if ok {
|
|
||||||
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
|
|
||||||
copy(gspec.ExtraData[32:32+common.AddressLength], testAddress1.Bytes())
|
|
||||||
|
|
||||||
engineBorInternal.Authorize(testAddress1, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
_, back, closeFn := miner.NewTestWorker(t, chainConfig, engine, db, 0)
|
||||||
return crypto.Sign(crypto.Keccak256(data), testKey1)
|
defer closeFn()
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
genesis := gspec.MustCommit(db)
|
genesis := back.BlockChain().Genesis()
|
||||||
|
|
||||||
if snapshots {
|
|
||||||
config.SnapshotLimit = 256
|
|
||||||
config.SnapshotWait = true
|
|
||||||
}
|
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(db, config, params.BorUnittestChainConfig, engine, vm.Config{}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create chain: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If sidechain blocks are needed, make a light chain and import it
|
// If sidechain blocks are needed, make a light chain and import it
|
||||||
var sideblocks types.Blocks
|
var sideblocks types.Blocks
|
||||||
|
|
@ -1854,56 +1826,60 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
sideblocks, _ = core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *core.BlockGen) {
|
sideblocks, _ = core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *core.BlockGen) {
|
||||||
b.SetCoinbase(testAddress1)
|
b.SetCoinbase(testAddress1)
|
||||||
|
|
||||||
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
|
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.CalculateSprint(b.Number().Uint64())) {
|
||||||
b.SetExtra(gspec.ExtraData)
|
b.SetExtra(back.Genesis.ExtraData)
|
||||||
} else {
|
} else {
|
||||||
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
|
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if _, err := chain.InsertChain(sideblocks); err != nil {
|
if _, err := back.BlockChain().InsertChain(sideblocks); err != nil {
|
||||||
t.Fatalf("Failed to import side chain: %v", err)
|
t.Fatalf("Failed to import side chain: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canonblocks, _ := core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *core.BlockGen) {
|
canonblocks, _ := core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *core.BlockGen) {
|
||||||
b.SetCoinbase(common.Address{0x02})
|
b.SetCoinbase(miner.TestBankAddress)
|
||||||
b.SetDifficulty(big.NewInt(1000000))
|
b.SetDifficulty(big.NewInt(1000000))
|
||||||
|
|
||||||
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
|
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.CalculateSprint(b.Number().Uint64())) {
|
||||||
b.SetExtra(gspec.ExtraData)
|
b.SetExtra(back.Genesis.ExtraData)
|
||||||
} else {
|
} else {
|
||||||
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
|
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil {
|
if _, err := back.BlockChain().InsertChain(canonblocks[:tt.commitBlock]); err != nil {
|
||||||
t.Fatalf("Failed to import canonical chain start: %v", err)
|
t.Fatalf("Failed to import canonical chain start: %v", err)
|
||||||
}
|
}
|
||||||
if tt.commitBlock > 0 {
|
if tt.commitBlock > 0 {
|
||||||
err = chain.StateCache().TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
|
err = back.BlockChain().StateCache().TrieDB().Commit(canonblocks[tt.commitBlock-1].Root(), true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("on trieDB.Commit", err)
|
t.Fatal("on trieDB.Commit", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if snapshots {
|
if snapshots {
|
||||||
if err := chain.Snaps().Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
if err := back.BlockChain().Snaps().Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil {
|
|
||||||
|
if _, err := back.BlockChain().InsertChain(canonblocks[tt.commitBlock:]); err != nil {
|
||||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
Freeze(threshold uint64) error
|
Freeze(threshold uint64) error
|
||||||
Ancients() (uint64, error)
|
Ancients() (uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
db.(freezer).Freeze(tt.freezeThreshold)
|
db.(freezer).Freeze(tt.freezeThreshold)
|
||||||
|
|
||||||
// Set the simulated pivot block
|
// Set the simulated pivot block
|
||||||
if tt.pivotBlock != nil {
|
if tt.pivotBlock != nil {
|
||||||
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
|
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull the plug on the database, simulating a hard crash
|
// Pull the plug on the database, simulating a hard crash
|
||||||
db.Close()
|
db.Close()
|
||||||
|
|
||||||
|
|
@ -1912,12 +1888,14 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent database: %v", err)
|
t.Fatalf("Failed to reopen persistent database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
newChain, err := core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
newChain, err := core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer newChain.Stop()
|
defer newChain.Stop()
|
||||||
|
|
||||||
// Iterate over all the remaining blocks and ensure there are no gaps
|
// Iterate over all the remaining blocks and ensure there are no gaps
|
||||||
|
|
@ -1929,12 +1907,15 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
if head := newChain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader {
|
if head := newChain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader {
|
||||||
t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader)
|
t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
if head := newChain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock {
|
if head := newChain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock {
|
||||||
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock)
|
t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
if head := newChain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock {
|
if head := newChain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock {
|
||||||
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
|
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
if frozen, err := db.(freezer).Ancients(); err != nil {
|
if frozen, err := db.(freezer).Ancients(); err != nil {
|
||||||
t.Errorf("Failed to retrieve ancient count: %v\n", err)
|
t.Errorf("Failed to retrieve ancient count: %v\n", err)
|
||||||
} else if int(frozen) != tt.expFrozen {
|
} else if int(frozen) != tt.expFrozen {
|
||||||
|
|
@ -1988,7 +1969,7 @@ func TestIssue23496(t *testing.T) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create chain: %v", err)
|
t.Fatalf("Failed to create chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2043,7 +2024,7 @@ func TestIssue23496(t *testing.T) {
|
||||||
|
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
chain, err = core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, err = core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1987,7 +1987,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
config.SnapshotWait = true
|
config.SnapshotWait = true
|
||||||
}
|
}
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create chain: %v", err)
|
t.Fatalf("Failed to create chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2082,6 +2082,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
|
|
||||||
// verifyNoGaps checks that there are no gaps after the initial set of blocks in
|
// verifyNoGaps checks that there are no gaps after the initial set of blocks in
|
||||||
// the database and errors if found.
|
// the database and errors if found.
|
||||||
|
//
|
||||||
//nolint:gocognit
|
//nolint:gocognit
|
||||||
func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks) {
|
func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
@ -2135,6 +2136,7 @@ func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted
|
||||||
|
|
||||||
// verifyCutoff checks that there are no chain data available in the chain after
|
// verifyCutoff checks that there are no chain data available in the chain after
|
||||||
// the specified limit, but that it is available before.
|
// the specified limit, but that it is available before.
|
||||||
|
//
|
||||||
//nolint:gocognit
|
//nolint:gocognit
|
||||||
func verifyCutoff(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks, head int) {
|
func verifyCutoff(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks, head int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*core.BlockChain, []*type
|
||||||
cacheConfig = core.DefaultCacheConfig
|
cacheConfig = core.DefaultCacheConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create chain: %v", err)
|
t.Fatalf("Failed to create chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +246,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
|
||||||
// Restart the chain normally
|
// Restart the chain normally
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
|
|
||||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -283,13 +283,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||||
// the crash, we do restart twice here: one after the crash and one
|
// the crash, we do restart twice here: one after the crash and one
|
||||||
// after the normal stop. It's used to ensure the broken snapshot
|
// after the normal stop. It's used to ensure the broken snapshot
|
||||||
// can be detected all the time.
|
// can be detected all the time.
|
||||||
newchain, err := core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err := core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
newchain.Stop()
|
newchain.Stop()
|
||||||
|
|
||||||
newchain, err = core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err = core.NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -327,7 +327,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||||
SnapshotLimit: 0,
|
SnapshotLimit: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
newchain, err := core.NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err := core.NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +336,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||||
newchain.Stop()
|
newchain.Stop()
|
||||||
|
|
||||||
// Restart the chain with enabling the snapshot
|
// Restart the chain with enabling the snapshot
|
||||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -365,7 +365,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
|
||||||
chain.SetHead(snaptest.setHead)
|
chain.SetHead(snaptest.setHead)
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
|
|
||||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +396,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
||||||
// and state committed.
|
// and state committed.
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
|
|
||||||
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err := core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -414,7 +414,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
||||||
// journal and latest state will be committed
|
// journal and latest state will be committed
|
||||||
|
|
||||||
// Restart the chain after the crash
|
// Restart the chain after the crash
|
||||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -449,8 +449,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||||
TrieTimeLimit: 5 * time.Minute,
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
SnapshotLimit: 0,
|
SnapshotLimit: 0,
|
||||||
}
|
}
|
||||||
|
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -467,14 +466,13 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||||
SnapshotLimit: 256,
|
SnapshotLimit: 256,
|
||||||
SnapshotWait: false, // Don't wait rebuild
|
SnapshotWait: false, // Don't wait rebuild
|
||||||
}
|
}
|
||||||
|
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
// Simulate the blockchain crash.
|
// Simulate the blockchain crash.
|
||||||
|
|
||||||
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
newchain, err = core.NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to recreate chain: %v", err)
|
t.Fatalf("Failed to recreate chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,8 @@ type TxPool struct {
|
||||||
initDoneCh chan struct{} // is closed once the pool is initialized (for tests)
|
initDoneCh chan struct{} // is closed once the pool is initialized (for tests)
|
||||||
|
|
||||||
changesSinceReorg int // A counter for how many drops we've performed in-between reorg.
|
changesSinceReorg int // A counter for how many drops we've performed in-between reorg.
|
||||||
|
|
||||||
|
promoteTxCh chan struct{} // should be used only for tests
|
||||||
}
|
}
|
||||||
|
|
||||||
type txpoolResetRequest struct {
|
type txpoolResetRequest struct {
|
||||||
|
|
@ -276,7 +278,7 @@ type txpoolResetRequest struct {
|
||||||
|
|
||||||
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
||||||
// transactions from the network.
|
// transactions from the network.
|
||||||
func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
|
func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain, options ...func(pool *TxPool)) *TxPool {
|
||||||
// Sanitize the input to ensure no vulnerable gas prices are set
|
// Sanitize the input to ensure no vulnerable gas prices are set
|
||||||
config = (&config).sanitize()
|
config = (&config).sanitize()
|
||||||
|
|
||||||
|
|
@ -299,6 +301,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||||
initDoneCh: make(chan struct{}),
|
initDoneCh: make(chan struct{}),
|
||||||
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.locals = newAccountSet(pool.signer)
|
pool.locals = newAccountSet(pool.signer)
|
||||||
for _, addr := range config.Locals {
|
for _, addr := range config.Locals {
|
||||||
log.Info("Setting new local account", "address", addr)
|
log.Info("Setting new local account", "address", addr)
|
||||||
|
|
@ -307,6 +310,11 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||||
pool.priced = newTxPricedList(pool.all)
|
pool.priced = newTxPricedList(pool.all)
|
||||||
pool.reset(nil, chain.CurrentBlock().Header())
|
pool.reset(nil, chain.CurrentBlock().Header())
|
||||||
|
|
||||||
|
// apply options
|
||||||
|
for _, fn := range options {
|
||||||
|
fn(pool)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the reorg loop early so it can handle requests generated during journal loading.
|
// Start the reorg loop early so it can handle requests generated during journal loading.
|
||||||
pool.wg.Add(1)
|
pool.wg.Add(1)
|
||||||
go pool.scheduleReorgLoop()
|
go pool.scheduleReorgLoop()
|
||||||
|
|
@ -809,6 +817,17 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
|
||||||
//
|
//
|
||||||
// Note, this method assumes the pool lock is held!
|
// Note, this method assumes the pool lock is held!
|
||||||
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
|
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
|
||||||
|
defer func() {
|
||||||
|
if pool.promoteTxCh == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case pool.promoteTxCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Try to insert the transaction into the pending queue
|
// Try to insert the transaction into the pending queue
|
||||||
if pool.pending[addr] == nil {
|
if pool.pending[addr] == nil {
|
||||||
pool.pending[addr] = newTxList(true)
|
pool.pending[addr] = newTxList(true)
|
||||||
|
|
@ -1080,6 +1099,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
||||||
dirtyAccounts *accountSet
|
dirtyAccounts *accountSet
|
||||||
queuedEvents = make(map[common.Address]*txSortedMap)
|
queuedEvents = make(map[common.Address]*txSortedMap)
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Launch next background reorg if needed
|
// Launch next background reorg if needed
|
||||||
if curDone == nil && launchNextRun {
|
if curDone == nil && launchNextRun {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -24,10 +25,17 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gonum.org/v1/gonum/floats"
|
||||||
|
"gonum.org/v1/gonum/stat"
|
||||||
|
"pgregory.net/rapid"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -47,6 +55,10 @@ var (
|
||||||
eip1559Config *params.ChainConfig
|
eip1559Config *params.ChainConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
txPoolGasLimit = 10_000_000
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
testTxPoolConfig = DefaultTxPoolConfig
|
testTxPoolConfig = DefaultTxPoolConfig
|
||||||
testTxPoolConfig.Journal = ""
|
testTxPoolConfig.Journal = ""
|
||||||
|
|
@ -114,15 +126,17 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int,
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||||
return setupTxPoolWithConfig(params.TestChainConfig)
|
return setupTxPoolWithConfig(params.TestChainConfig, testTxPoolConfig, txPoolGasLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) {
|
func setupTxPoolWithConfig(config *params.ChainConfig, txPoolConfig TxPoolConfig, gasLimit uint64, options ...func(pool *TxPool)) (*TxPool, *ecdsa.PrivateKey) {
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
blockchain := &testBlockChain{10000000, statedb, new(event.Feed)}
|
|
||||||
|
blockchain := &testBlockChain{gasLimit, statedb, new(event.Feed)}
|
||||||
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
pool := NewTxPool(testTxPoolConfig, config, blockchain)
|
|
||||||
|
pool := NewTxPool(txPoolConfig, config, blockchain, options...)
|
||||||
|
|
||||||
// wait for the pool to initialize
|
// wait for the pool to initialize
|
||||||
<-pool.initDoneCh
|
<-pool.initDoneCh
|
||||||
|
|
@ -273,6 +287,16 @@ func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) {
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBalance(pool *TxPool, addr common.Address) *big.Int {
|
||||||
|
bal := big.NewInt(0)
|
||||||
|
|
||||||
|
pool.mu.Lock()
|
||||||
|
bal.Set(pool.currentState.GetBalance(addr))
|
||||||
|
pool.mu.Unlock()
|
||||||
|
|
||||||
|
return bal
|
||||||
|
}
|
||||||
|
|
||||||
func TestInvalidTransactions(t *testing.T) {
|
func TestInvalidTransactions(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
@ -384,7 +408,7 @@ func TestTransactionNegativeValue(t *testing.T) {
|
||||||
func TestTransactionTipAboveFeeCap(t *testing.T) {
|
func TestTransactionTipAboveFeeCap(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pool, key := setupTxPoolWithConfig(eip1559Config)
|
pool, key := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key)
|
tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key)
|
||||||
|
|
@ -397,7 +421,7 @@ func TestTransactionTipAboveFeeCap(t *testing.T) {
|
||||||
func TestTransactionVeryHighValues(t *testing.T) {
|
func TestTransactionVeryHighValues(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pool, key := setupTxPoolWithConfig(eip1559Config)
|
pool, key := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
veryBigNumber := big.NewInt(1)
|
veryBigNumber := big.NewInt(1)
|
||||||
|
|
@ -1449,7 +1473,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
pool, _ := setupTxPoolWithConfig(eip1559Config)
|
pool, _ := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
|
|
@ -1820,7 +1844,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
||||||
func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
|
func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pool, _ := setupTxPoolWithConfig(eip1559Config)
|
pool, _ := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
pool.config.GlobalSlots = 2
|
pool.config.GlobalSlots = 2
|
||||||
|
|
@ -1927,7 +1951,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
|
||||||
func TestDualHeapEviction(t *testing.T) {
|
func TestDualHeapEviction(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pool, _ := setupTxPoolWithConfig(eip1559Config)
|
pool, _ := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
pool.config.GlobalSlots = 10
|
pool.config.GlobalSlots = 10
|
||||||
|
|
@ -2130,7 +2154,7 @@ func TestTransactionReplacementDynamicFee(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
pool, key := setupTxPoolWithConfig(eip1559Config)
|
pool, key := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
|
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
|
||||||
|
|
||||||
|
|
@ -2561,3 +2585,516 @@ func BenchmarkPoolMultiAccountBatchInsert(b *testing.B) {
|
||||||
pool.AddRemotesSync([]*types.Transaction{tx})
|
pool.AddRemotesSync([]*types.Transaction{tx})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type acc struct {
|
||||||
|
nonce uint64
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
account common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
type testTx struct {
|
||||||
|
tx *types.Transaction
|
||||||
|
idx int
|
||||||
|
isLocal bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const localIdx = 0
|
||||||
|
|
||||||
|
func getTransactionGen(t *rapid.T, keys []*acc, nonces []uint64, localKey *acc, gasPriceMin, gasPriceMax, gasLimitMin, gasLimitMax uint64) *testTx {
|
||||||
|
idx := rapid.IntRange(0, len(keys)-1).Draw(t, "accIdx").(int)
|
||||||
|
|
||||||
|
var (
|
||||||
|
isLocal bool
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
)
|
||||||
|
|
||||||
|
if idx == localIdx {
|
||||||
|
isLocal = true
|
||||||
|
key = localKey.key
|
||||||
|
} else {
|
||||||
|
key = keys[idx].key
|
||||||
|
}
|
||||||
|
|
||||||
|
nonces[idx]++
|
||||||
|
|
||||||
|
gasPriceUint := rapid.Uint64Range(gasPriceMin, gasPriceMax).Draw(t, "gasPrice").(uint64)
|
||||||
|
gasPrice := big.NewInt(0).SetUint64(gasPriceUint)
|
||||||
|
gasLimit := rapid.Uint64Range(gasLimitMin, gasLimitMax).Draw(t, "gasLimit").(uint64)
|
||||||
|
|
||||||
|
return &testTx{
|
||||||
|
tx: pricedTransaction(nonces[idx]-1, gasLimit, gasPrice, key),
|
||||||
|
idx: idx,
|
||||||
|
isLocal: isLocal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type transactionBatches struct {
|
||||||
|
txs []*testTx
|
||||||
|
totalTxs int
|
||||||
|
}
|
||||||
|
|
||||||
|
func transactionsGen(keys []*acc, nonces []uint64, localKey *acc, minTxs int, maxTxs int, gasPriceMin, gasPriceMax, gasLimitMin, gasLimitMax uint64, caseParams *strings.Builder) func(t *rapid.T) *transactionBatches {
|
||||||
|
return func(t *rapid.T) *transactionBatches {
|
||||||
|
totalTxs := rapid.IntRange(minTxs, maxTxs).Draw(t, "totalTxs").(int)
|
||||||
|
txs := make([]*testTx, totalTxs)
|
||||||
|
|
||||||
|
gasValues := make([]float64, totalTxs)
|
||||||
|
|
||||||
|
fmt.Fprintf(caseParams, " totalTxs = %d;", totalTxs)
|
||||||
|
|
||||||
|
keys = keys[:len(nonces)]
|
||||||
|
|
||||||
|
for i := 0; i < totalTxs; i++ {
|
||||||
|
txs[i] = getTransactionGen(t, keys, nonces, localKey, gasPriceMin, gasPriceMax, gasLimitMin, gasLimitMax)
|
||||||
|
|
||||||
|
gasValues[i] = float64(txs[i].tx.Gas())
|
||||||
|
}
|
||||||
|
|
||||||
|
mean, stddev := stat.MeanStdDev(gasValues, nil)
|
||||||
|
fmt.Fprintf(caseParams, " gasValues mean %d, stdev %d, %d-%d);", int64(mean), int64(stddev), int64(floats.Min(gasValues)), int64(floats.Max(gasValues)))
|
||||||
|
|
||||||
|
return &transactionBatches{txs, totalTxs}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type txPoolRapidConfig struct {
|
||||||
|
gasLimit uint64
|
||||||
|
avgBlockTxs uint64
|
||||||
|
|
||||||
|
minTxs int
|
||||||
|
maxTxs int
|
||||||
|
|
||||||
|
minAccs int
|
||||||
|
maxAccs int
|
||||||
|
|
||||||
|
// less tweakable, more like constants
|
||||||
|
gasPriceMin uint64
|
||||||
|
gasPriceMax uint64
|
||||||
|
|
||||||
|
gasLimitMin uint64
|
||||||
|
gasLimitMax uint64
|
||||||
|
|
||||||
|
balance int64
|
||||||
|
|
||||||
|
blockTime time.Duration
|
||||||
|
maxEmptyBlocks int
|
||||||
|
maxStuckBlocks int
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultTxPoolRapidConfig() txPoolRapidConfig {
|
||||||
|
gasLimit := uint64(30_000_000)
|
||||||
|
avgBlockTxs := gasLimit/params.TxGas + 1
|
||||||
|
maxTxs := int(25 * avgBlockTxs)
|
||||||
|
|
||||||
|
return txPoolRapidConfig{
|
||||||
|
gasLimit: gasLimit,
|
||||||
|
|
||||||
|
avgBlockTxs: avgBlockTxs,
|
||||||
|
|
||||||
|
minTxs: 1,
|
||||||
|
maxTxs: maxTxs,
|
||||||
|
|
||||||
|
minAccs: 1,
|
||||||
|
maxAccs: maxTxs,
|
||||||
|
|
||||||
|
// less tweakable, more like constants
|
||||||
|
gasPriceMin: 1,
|
||||||
|
gasPriceMax: 1_000,
|
||||||
|
|
||||||
|
gasLimitMin: params.TxGas,
|
||||||
|
gasLimitMax: gasLimit / 2,
|
||||||
|
|
||||||
|
balance: 0xffffffffffffff,
|
||||||
|
|
||||||
|
blockTime: 2 * time.Second,
|
||||||
|
maxEmptyBlocks: 10,
|
||||||
|
maxStuckBlocks: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSmallTxPool is not something to run in parallel as far it uses all CPUs
|
||||||
|
// nolint:paralleltest
|
||||||
|
func TestSmallTxPool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Skip("a red test to be fixed")
|
||||||
|
|
||||||
|
cfg := defaultTxPoolRapidConfig()
|
||||||
|
|
||||||
|
cfg.maxEmptyBlocks = 10
|
||||||
|
cfg.maxStuckBlocks = 10
|
||||||
|
|
||||||
|
cfg.minTxs = 1
|
||||||
|
cfg.maxTxs = 2
|
||||||
|
|
||||||
|
cfg.minAccs = 1
|
||||||
|
cfg.maxAccs = 2
|
||||||
|
|
||||||
|
testPoolBatchInsert(t, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is not something to run in parallel as far it uses all CPUs
|
||||||
|
// nolint:paralleltest
|
||||||
|
func TestBigTxPool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Skip("a red test to be fixed")
|
||||||
|
|
||||||
|
cfg := defaultTxPoolRapidConfig()
|
||||||
|
|
||||||
|
testPoolBatchInsert(t, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit,thelper
|
||||||
|
func testPoolBatchInsert(t *testing.T, cfg txPoolRapidConfig) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const debug = false
|
||||||
|
|
||||||
|
initialBalance := big.NewInt(cfg.balance)
|
||||||
|
|
||||||
|
keys := make([]*acc, cfg.maxAccs)
|
||||||
|
|
||||||
|
var key *ecdsa.PrivateKey
|
||||||
|
|
||||||
|
// prealloc keys
|
||||||
|
for idx := 0; idx < cfg.maxAccs; idx++ {
|
||||||
|
key, _ = crypto.GenerateKey()
|
||||||
|
|
||||||
|
keys[idx] = &acc{
|
||||||
|
key: key,
|
||||||
|
nonce: 0,
|
||||||
|
account: crypto.PubkeyToAddress(key.PublicKey),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var threads = runtime.NumCPU()
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
// 1 is set only for debug
|
||||||
|
threads = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
testsDone := new(uint64)
|
||||||
|
|
||||||
|
for i := 0; i < threads; i++ {
|
||||||
|
t.Run(fmt.Sprintf("thread %d", i), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rapid.Check(t, func(rt *rapid.T) {
|
||||||
|
caseParams := new(strings.Builder)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
res := atomic.AddUint64(testsDone, 1)
|
||||||
|
|
||||||
|
if res%100 == 0 {
|
||||||
|
fmt.Println("case-done", res)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Generate a batch of transactions to enqueue into the pool
|
||||||
|
testTxPoolConfig := testTxPoolConfig
|
||||||
|
|
||||||
|
// from sentry config
|
||||||
|
testTxPoolConfig.AccountQueue = 16
|
||||||
|
testTxPoolConfig.AccountSlots = 16
|
||||||
|
testTxPoolConfig.GlobalQueue = 32768
|
||||||
|
testTxPoolConfig.GlobalSlots = 32768
|
||||||
|
testTxPoolConfig.Lifetime = time.Hour + 30*time.Minute //"1h30m0s"
|
||||||
|
testTxPoolConfig.PriceLimit = 1
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
pendingAddedCh := make(chan struct{}, 1024)
|
||||||
|
pool, key := setupTxPoolWithConfig(params.TestChainConfig, testTxPoolConfig, cfg.gasLimit, MakeWithPromoteTxCh(pendingAddedCh))
|
||||||
|
defer pool.Stop()
|
||||||
|
|
||||||
|
totalAccs := rapid.IntRange(cfg.minAccs, cfg.maxAccs).Draw(rt, "totalAccs").(int)
|
||||||
|
|
||||||
|
fmt.Fprintf(caseParams, "Case params: totalAccs = %d;", totalAccs)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
pending, queued := pool.Content()
|
||||||
|
|
||||||
|
if len(pending) != 0 {
|
||||||
|
pendingGas := make([]float64, 0, len(pending))
|
||||||
|
|
||||||
|
for _, txs := range pending {
|
||||||
|
for _, tx := range txs {
|
||||||
|
pendingGas = append(pendingGas, float64(tx.Gas()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mean, stddev := stat.MeanStdDev(pendingGas, nil)
|
||||||
|
fmt.Fprintf(caseParams, "\tpending mean %d, stdev %d, %d-%d;\n", int64(mean), int64(stddev), int64(floats.Min(pendingGas)), int64(floats.Max(pendingGas)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(queued) != 0 {
|
||||||
|
queuedGas := make([]float64, 0, len(queued))
|
||||||
|
|
||||||
|
for _, txs := range queued {
|
||||||
|
for _, tx := range txs {
|
||||||
|
queuedGas = append(queuedGas, float64(tx.Gas()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mean, stddev := stat.MeanStdDev(queuedGas, nil)
|
||||||
|
fmt.Fprintf(caseParams, "\tqueued mean %d, stdev %d, %d-%d);\n\n", int64(mean), int64(stddev), int64(floats.Min(queuedGas)), int64(floats.Max(queuedGas)))
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.Log(caseParams)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// regenerate only local key
|
||||||
|
localKey := &acc{
|
||||||
|
key: key,
|
||||||
|
account: crypto.PubkeyToAddress(key.PublicKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateTxPoolInternals(pool); err != nil {
|
||||||
|
rt.Fatalf("pool internal state corrupted: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
now = time.Now()
|
||||||
|
|
||||||
|
testAddBalance(pool, localKey.account, initialBalance)
|
||||||
|
|
||||||
|
for idx := 0; idx < totalAccs; idx++ {
|
||||||
|
testAddBalance(pool, keys[idx].account, initialBalance)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
nonces := make([]uint64, totalAccs)
|
||||||
|
gen := rapid.Custom(transactionsGen(keys, nonces, localKey, cfg.minTxs, cfg.maxTxs, cfg.gasPriceMin, cfg.gasPriceMax, cfg.gasLimitMin, cfg.gasLimitMax, caseParams))
|
||||||
|
|
||||||
|
txs := gen.Draw(rt, "batches").(*transactionBatches)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
var (
|
||||||
|
addIntoTxPool func(tx []*types.Transaction) []error
|
||||||
|
totalInBatch int
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, tx := range txs.txs {
|
||||||
|
addIntoTxPool = pool.AddRemotesSync
|
||||||
|
|
||||||
|
if tx.isLocal {
|
||||||
|
addIntoTxPool = pool.AddLocals
|
||||||
|
}
|
||||||
|
|
||||||
|
err := addIntoTxPool([]*types.Transaction{tx.tx})
|
||||||
|
if len(err) != 0 && err[0] != nil {
|
||||||
|
rt.Log("on adding a transaction to the tx pool", err[0], tx.tx.Gas(), tx.tx.GasPrice(), pool.GasPrice(), getBalance(pool, keys[tx.idx].account))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
block int
|
||||||
|
emptyBlocks int
|
||||||
|
stuckBlocks int
|
||||||
|
lastTxPoolStats int
|
||||||
|
currentTxPoolStats int
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// we'd expect fulfilling block take comparable, but less than blockTime
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.maxStuckBlocks)*cfg.blockTime)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-pendingAddedCh:
|
||||||
|
case <-ctx.Done():
|
||||||
|
pendingStat, queuedStat := pool.Stats()
|
||||||
|
if pendingStat+queuedStat == 0 {
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.Fatalf("got %ds block timeout (expected less then %s): total accounts %d. Pending %d, queued %d)",
|
||||||
|
block, 5*cfg.blockTime, txs.totalTxs, pendingStat, queuedStat)
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingStat, queuedStat := pool.Stats()
|
||||||
|
currentTxPoolStats = pendingStat + queuedStat
|
||||||
|
if currentTxPoolStats == 0 {
|
||||||
|
cancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if txPool got stuck
|
||||||
|
if currentTxPoolStats == lastTxPoolStats {
|
||||||
|
stuckBlocks++ //todo: переписать
|
||||||
|
} else {
|
||||||
|
stuckBlocks = 0
|
||||||
|
lastTxPoolStats = currentTxPoolStats
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy-paste
|
||||||
|
start := time.Now()
|
||||||
|
pending := pool.Pending(true)
|
||||||
|
locals := pool.Locals()
|
||||||
|
|
||||||
|
// from fillTransactions
|
||||||
|
removedFromPool, blockGasLeft, err := fillTransactions(ctx, pool, locals, pending, cfg.gasLimit)
|
||||||
|
|
||||||
|
done := time.Since(start)
|
||||||
|
|
||||||
|
if removedFromPool > 0 {
|
||||||
|
emptyBlocks = 0
|
||||||
|
} else {
|
||||||
|
emptyBlocks++
|
||||||
|
}
|
||||||
|
|
||||||
|
if emptyBlocks >= cfg.maxEmptyBlocks || stuckBlocks >= cfg.maxStuckBlocks {
|
||||||
|
// check for nonce gaps
|
||||||
|
var lastNonce, currentNonce int
|
||||||
|
|
||||||
|
pending = pool.Pending(true)
|
||||||
|
|
||||||
|
for txAcc, pendingTxs := range pending {
|
||||||
|
lastNonce = int(pool.Nonce(txAcc)) - len(pendingTxs) - 1
|
||||||
|
|
||||||
|
isFirst := true
|
||||||
|
|
||||||
|
for _, tx := range pendingTxs {
|
||||||
|
currentNonce = int(tx.Nonce())
|
||||||
|
if currentNonce-lastNonce != 1 {
|
||||||
|
rt.Fatalf("got a nonce gap for account %q. Current pending nonce %d, previous %d %v; emptyBlocks - %v; stuckBlocks - %v",
|
||||||
|
txAcc, currentNonce, lastNonce, isFirst, emptyBlocks >= cfg.maxEmptyBlocks, stuckBlocks >= cfg.maxStuckBlocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastNonce = currentNonce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if emptyBlocks >= cfg.maxEmptyBlocks {
|
||||||
|
rt.Fatalf("got %d empty blocks in a row(expected less then %d): total time %s, total accounts %d. Pending %d, locals %d)",
|
||||||
|
emptyBlocks, cfg.maxEmptyBlocks, done, txs.totalTxs, len(pending), len(locals))
|
||||||
|
}
|
||||||
|
|
||||||
|
if stuckBlocks >= cfg.maxStuckBlocks {
|
||||||
|
rt.Fatalf("got %d empty blocks in a row(expected less then %d): total time %s, total accounts %d. Pending %d, locals %d)",
|
||||||
|
emptyBlocks, cfg.maxEmptyBlocks, done, txs.totalTxs, len(pending), len(locals))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
rt.Fatalf("took too long: total time %s(expected %s), total accounts %d. Pending %d, locals %d)",
|
||||||
|
done, cfg.blockTime, txs.totalTxs, len(pending), len(locals))
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.Log("current_total", txs.totalTxs, "in_batch", totalInBatch, "removed", removedFromPool, "emptyBlocks", emptyBlocks, "blockGasLeft", blockGasLeft, "pending", len(pending), "locals", len(locals),
|
||||||
|
"locals+pending", done)
|
||||||
|
|
||||||
|
rt.Log("block", block, "pending", pendingStat, "queued", queuedStat, "elapsed", done)
|
||||||
|
|
||||||
|
block++
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
//time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.Logf("case completed totalTxs %d %v\n\n", txs.totalTxs, time.Since(now))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("done test cases", atomic.LoadUint64(testsDone))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillTransactions(ctx context.Context, pool *TxPool, locals []common.Address, pending map[common.Address]types.Transactions, gasLimit uint64) (int, uint64, error) {
|
||||||
|
localTxs := make(map[common.Address]types.Transactions)
|
||||||
|
remoteTxs := pending
|
||||||
|
|
||||||
|
for _, txAcc := range locals {
|
||||||
|
if txs := remoteTxs[txAcc]; len(txs) > 0 {
|
||||||
|
delete(remoteTxs, txAcc)
|
||||||
|
|
||||||
|
localTxs[txAcc] = txs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fake signer
|
||||||
|
signer := types.NewLondonSigner(big.NewInt(1))
|
||||||
|
|
||||||
|
// fake baseFee
|
||||||
|
baseFee := big.NewInt(1)
|
||||||
|
|
||||||
|
blockGasLimit := gasLimit
|
||||||
|
|
||||||
|
var (
|
||||||
|
txLocalCount int
|
||||||
|
txRemoteCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(localTxs) > 0 {
|
||||||
|
txs := types.NewTransactionsByPriceAndNonce(signer, localTxs, baseFee)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return txLocalCount + txRemoteCount, blockGasLimit, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
blockGasLimit, txLocalCount = commitTransactions(pool, txs, blockGasLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return txLocalCount + txRemoteCount, blockGasLimit, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(remoteTxs) > 0 {
|
||||||
|
txs := types.NewTransactionsByPriceAndNonce(signer, remoteTxs, baseFee)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return txLocalCount + txRemoteCount, blockGasLimit, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
blockGasLimit, txRemoteCount = commitTransactions(pool, txs, blockGasLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return txLocalCount + txRemoteCount, blockGasLimit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func commitTransactions(pool *TxPool, txs *types.TransactionsByPriceAndNonce, blockGasLimit uint64) (uint64, int) {
|
||||||
|
var (
|
||||||
|
tx *types.Transaction
|
||||||
|
txCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
tx = txs.Peek()
|
||||||
|
|
||||||
|
if tx == nil {
|
||||||
|
return blockGasLimit, txCount
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx.Gas() <= blockGasLimit {
|
||||||
|
blockGasLimit -= tx.Gas()
|
||||||
|
pool.removeTx(tx.Hash(), false)
|
||||||
|
|
||||||
|
txCount++
|
||||||
|
} else {
|
||||||
|
// we don't maximize fulfilment of the block. just fill somehow
|
||||||
|
return blockGasLimit, txCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeWithPromoteTxCh(ch chan struct{}) func(*TxPool) {
|
||||||
|
return func(pool *TxPool) {
|
||||||
|
pool.promoteTxCh = ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -508,6 +508,10 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
|
||||||
// Initialize a price and received time based heap with the head transactions
|
// Initialize a price and received time based heap with the head transactions
|
||||||
heads := make(TxByPriceAndTime, 0, len(txs))
|
heads := make(TxByPriceAndTime, 0, len(txs))
|
||||||
for from, accTxs := range txs {
|
for from, accTxs := range txs {
|
||||||
|
if len(accTxs) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
acc, _ := Sender(signer, accTxs[0])
|
acc, _ := Sender(signer, accTxs[0])
|
||||||
wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee)
|
wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee)
|
||||||
// Remove transaction if sender doesn't match from, or if wrapping fails.
|
// Remove transaction if sender doesn't match from, or if wrapping fails.
|
||||||
|
|
@ -550,6 +554,10 @@ func (t *TransactionsByPriceAndNonce) Shift() {
|
||||||
heap.Pop(&t.heads)
|
heap.Pop(&t.heads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TransactionsByPriceAndNonce) GetTxs() int {
|
||||||
|
return len(t.txs)
|
||||||
|
}
|
||||||
|
|
||||||
// Pop removes the best transaction, *not* replacing it with the next one from
|
// Pop removes the best transaction, *not* replacing it with the next one from
|
||||||
// the same account. This should be used when a transaction cannot be executed
|
// the same account. This should be used when a transaction cannot be executed
|
||||||
// and hence all subsequent ones should be discarded from the same account.
|
// and hence all subsequent ones should be discarded from the same account.
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,8 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
|
||||||
} else {
|
} else {
|
||||||
blockRlp = fmt.Sprintf("0x%x", rlpBytes)
|
blockRlp = fmt.Sprintf("0x%x", rlpBytes)
|
||||||
}
|
}
|
||||||
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
|
|
||||||
|
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig(), api.eth.chainDb); err != nil {
|
||||||
blockJSON = map[string]interface{}{"error": err.Error()}
|
blockJSON = map[string]interface{}{"error": err.Error()}
|
||||||
}
|
}
|
||||||
results = append(results, &BadBlockArgs{
|
results = append(results, &BadBlockArgs{
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -40,6 +41,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader/whitelist"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
|
|
@ -216,9 +218,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
TrieTimeLimit: config.TrieTimeout,
|
TrieTimeLimit: config.TrieTimeout,
|
||||||
SnapshotLimit: config.SnapshotCache,
|
SnapshotLimit: config.SnapshotCache,
|
||||||
Preimages: config.Preimages,
|
Preimages: config.Preimages,
|
||||||
|
TriesInMemory: config.TriesInMemory,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
|
|
||||||
|
checker := whitelist.NewService(10)
|
||||||
|
|
||||||
|
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -259,6 +265,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
Checkpoint: checkpoint,
|
Checkpoint: checkpoint,
|
||||||
EthAPI: ethAPI,
|
EthAPI: ethAPI,
|
||||||
PeerRequiredBlocks: config.PeerRequiredBlocks,
|
PeerRequiredBlocks: config.PeerRequiredBlocks,
|
||||||
|
checker: checker,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -376,6 +383,10 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
|
||||||
s.blockchain.ResetWithGenesisBlock(gb)
|
s.blockchain.ResetWithGenesisBlock(gb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Ethereum) PublicBlockChainAPI() *ethapi.PublicBlockChainAPI {
|
||||||
|
return s.handler.ethAPI
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
||||||
s.lock.RLock()
|
s.lock.RLock()
|
||||||
etherbase := s.etherbase
|
etherbase := s.etherbase
|
||||||
|
|
@ -621,6 +632,13 @@ func (s *Ethereum) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotBorConsensus = errors.New("not bor consensus was given")
|
||||||
|
ErrBorConsensusWithoutHeimdall = errors.New("bor consensus without heimdall")
|
||||||
|
|
||||||
|
whitelistTimeout = 30 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// StartCheckpointWhitelistService starts the goroutine to fetch checkpoints and update the
|
// StartCheckpointWhitelistService starts the goroutine to fetch checkpoints and update the
|
||||||
// checkpoint whitelist map.
|
// checkpoint whitelist map.
|
||||||
func (s *Ethereum) startCheckpointWhitelistService() {
|
func (s *Ethereum) startCheckpointWhitelistService() {
|
||||||
|
|
@ -632,7 +650,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// first run the checkpoint whitelist
|
// first run the checkpoint whitelist
|
||||||
err := s.handleWhitelistCheckpoint()
|
firstCtx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
|
||||||
|
err := s.handleWhitelistCheckpoint(firstCtx, true)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
|
if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
|
||||||
return
|
return
|
||||||
|
|
@ -647,7 +669,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
err := s.handleWhitelistCheckpoint()
|
ctx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
|
||||||
|
err := s.handleWhitelistCheckpoint(ctx, false)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("unable to whitelist checkpoint", "err", err)
|
log.Warn("unable to whitelist checkpoint", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -657,13 +683,8 @@ func (s *Ethereum) startCheckpointWhitelistService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNotBorConsensus = errors.New("not bor consensus was given")
|
|
||||||
ErrBorConsensusWithoutHeimdall = errors.New("bor consensus without heimdall")
|
|
||||||
)
|
|
||||||
|
|
||||||
// handleWhitelistCheckpoint handles the checkpoint whitelist mechanism.
|
// handleWhitelistCheckpoint handles the checkpoint whitelist mechanism.
|
||||||
func (s *Ethereum) handleWhitelistCheckpoint() error {
|
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, first bool) error {
|
||||||
ethHandler := (*ethHandler)(s.handler)
|
ethHandler := (*ethHandler)(s.handler)
|
||||||
|
|
||||||
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
|
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
|
||||||
|
|
@ -675,13 +696,20 @@ func (s *Ethereum) handleWhitelistCheckpoint() error {
|
||||||
return ErrBorConsensusWithoutHeimdall
|
return ErrBorConsensusWithoutHeimdall
|
||||||
}
|
}
|
||||||
|
|
||||||
endBlockNum, endBlockHash, err := ethHandler.fetchWhitelistCheckpoint(bor)
|
// Create a new checkpoint verifier
|
||||||
if err != nil {
|
verifier := newCheckpointVerifier(nil)
|
||||||
|
blockNums, blockHashes, err := ethHandler.fetchWhitelistCheckpoints(ctx, bor, verifier, first)
|
||||||
|
// If the array is empty, we're bound to receive an error. Non-nill error and non-empty array
|
||||||
|
// means that array has partial elements and it failed for some block. We'll add those partial
|
||||||
|
// elements anyway.
|
||||||
|
if len(blockNums) == 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the checkpoint whitelist map.
|
// Update the checkpoint whitelist map.
|
||||||
ethHandler.downloader.ProcessCheckpoint(endBlockNum, endBlockHash)
|
for i := 0; i < len(blockNums); i++ {
|
||||||
|
ethHandler.downloader.ProcessCheckpoint(blockNums[i], blockHashes[i])
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
eth/bor_checkpoint_verifier.go
Normal file
60
eth/bor_checkpoint_verifier.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package eth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type checkpointVerifier struct {
|
||||||
|
verify func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCheckpointVerifier(verifyFn func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error)) *checkpointVerifier {
|
||||||
|
if verifyFn != nil {
|
||||||
|
return &checkpointVerifier{verifyFn}
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyFn = func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error) {
|
||||||
|
var (
|
||||||
|
startBlock = checkpoint.StartBlock.Uint64()
|
||||||
|
endBlock = checkpoint.EndBlock.Uint64()
|
||||||
|
)
|
||||||
|
|
||||||
|
// check if we have the checkpoint blocks
|
||||||
|
head := handler.ethAPI.BlockNumber()
|
||||||
|
if head < hexutil.Uint64(endBlock) {
|
||||||
|
log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", endBlock)
|
||||||
|
return "", errMissingCheckpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the root hash of checkpoint
|
||||||
|
roothash, err := handler.ethAPI.GetRootHash(ctx, startBlock, endBlock)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get root hash of checkpoint while whitelisting", "err", err)
|
||||||
|
return "", errRootHash
|
||||||
|
}
|
||||||
|
|
||||||
|
if roothash != checkpoint.RootHash.String()[2:] {
|
||||||
|
log.Warn("Checkpoint root hash mismatch while whitelisting", "expected", checkpoint.RootHash.String()[2:], "got", roothash)
|
||||||
|
return "", errCheckpointRootHashMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the end checkpoint block hash
|
||||||
|
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(endBlock), false)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get end block hash of checkpoint while whitelisting", "err", err)
|
||||||
|
return "", errEndBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := fmt.Sprintf("%v", block["hash"])
|
||||||
|
|
||||||
|
return hash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &checkpointVerifier{verifyFn}
|
||||||
|
}
|
||||||
|
|
@ -144,7 +144,7 @@ type Downloader struct {
|
||||||
quitCh chan struct{} // Quit channel to signal termination
|
quitCh chan struct{} // Quit channel to signal termination
|
||||||
quitLock sync.Mutex // Lock to prevent double closes
|
quitLock sync.Mutex // Lock to prevent double closes
|
||||||
|
|
||||||
ChainValidator
|
ethereum.ChainValidator
|
||||||
|
|
||||||
// Testing hooks
|
// Testing hooks
|
||||||
syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run
|
syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run
|
||||||
|
|
@ -153,14 +153,6 @@ type Downloader struct {
|
||||||
chainInsertHook func([]*fetchResult) // Method to call upon inserting a chain of blocks (possibly in multiple invocations)
|
chainInsertHook func([]*fetchResult) // Method to call upon inserting a chain of blocks (possibly in multiple invocations)
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface for whitelist service
|
|
||||||
type ChainValidator interface {
|
|
||||||
IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
|
|
||||||
ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash)
|
|
||||||
GetCheckpointWhitelist() map[uint64]common.Hash
|
|
||||||
PurgeCheckpointWhitelist()
|
|
||||||
}
|
|
||||||
|
|
||||||
// LightChain encapsulates functions required to synchronise a light chain.
|
// LightChain encapsulates functions required to synchronise a light chain.
|
||||||
type LightChain interface {
|
type LightChain interface {
|
||||||
// HasHeader verifies a header's presence in the local chain.
|
// HasHeader verifies a header's presence in the local chain.
|
||||||
|
|
@ -215,8 +207,8 @@ type BlockChain interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
||||||
//nolint: staticcheck
|
// nolint: staticcheck
|
||||||
func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func(), whitelistService ChainValidator) *Downloader {
|
func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func(), whitelistService ethereum.ChainValidator) *Downloader {
|
||||||
if lightchain == nil {
|
if lightchain == nil {
|
||||||
lightchain = chain
|
lightchain = chain
|
||||||
}
|
}
|
||||||
|
|
@ -737,9 +729,11 @@ func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *ty
|
||||||
// calculateRequestSpan calculates what headers to request from a peer when trying to determine the
|
// calculateRequestSpan calculates what headers to request from a peer when trying to determine the
|
||||||
// common ancestor.
|
// common ancestor.
|
||||||
// It returns parameters to be used for peer.RequestHeadersByNumber:
|
// It returns parameters to be used for peer.RequestHeadersByNumber:
|
||||||
// from - starting block number
|
//
|
||||||
// count - number of headers to request
|
// from - starting block number
|
||||||
// skip - number of headers to skip
|
// count - number of headers to request
|
||||||
|
// skip - number of headers to skip
|
||||||
|
//
|
||||||
// and also returns 'max', the last block which is expected to be returned by the remote peers,
|
// and also returns 'max', the last block which is expected to be returned by the remote peers,
|
||||||
// given the (from,count,skip)
|
// given the (from,count,skip)
|
||||||
func calculateRequestSpan(remoteHeight, localHeight uint64) (int64, int, int, uint64) {
|
func calculateRequestSpan(remoteHeight, localHeight uint64) (int64, int, int, uint64) {
|
||||||
|
|
@ -799,9 +793,11 @@ func (d *Downloader) getFetchHeadersByNumber(p *peerConnection) func(number uint
|
||||||
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
|
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
|
||||||
// the head links match), we do a binary search to find the common ancestor.
|
// the head links match), we do a binary search to find the common ancestor.
|
||||||
func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) (uint64, error) {
|
func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) (uint64, error) {
|
||||||
// Check the validity of chain to be downloaded
|
// Check the validity of peer from which the chain is to be downloaded
|
||||||
if _, err := d.IsValidChain(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
|
if d.ChainValidator != nil {
|
||||||
return 0, err
|
if _, err := d.IsValidPeer(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out the valid ancestor range to prevent rewrite attacks
|
// Figure out the valid ancestor range to prevent rewrite attacks
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func newTester() *downloadTester {
|
||||||
|
|
||||||
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000))
|
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000))
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ func newTester() *downloadTester {
|
||||||
return tester
|
return tester
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dl *downloadTester) setWhitelist(w ChainValidator) {
|
func (dl *downloadTester) setWhitelist(w ethereum.ChainValidator) {
|
||||||
dl.downloader.ChainValidator = w
|
dl.downloader.ChainValidator = w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1416,9 +1416,9 @@ func newWhitelistFake(validate func(count int) (bool, error)) *whitelistFake {
|
||||||
return &whitelistFake{0, validate}
|
return &whitelistFake{0, validate}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValidChain is the mock function which the downloader will use to validate the chain
|
// IsValidPeer is the mock function which the downloader will use to validate the chain
|
||||||
// to be received from a peer.
|
// to be received from a peer.
|
||||||
func (w *whitelistFake) IsValidChain(_ *types.Header, _ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
func (w *whitelistFake) IsValidPeer(_ *types.Header, _ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
w.count++
|
w.count++
|
||||||
}()
|
}()
|
||||||
|
|
@ -1426,13 +1426,18 @@ func (w *whitelistFake) IsValidChain(_ *types.Header, _ func(number uint64, amou
|
||||||
return w.validate(w.count)
|
return w.validate(w.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *whitelistFake) IsValidChain(current *types.Header, headers []*types.Header) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
func (w *whitelistFake) ProcessCheckpoint(_ uint64, _ common.Hash) {}
|
func (w *whitelistFake) ProcessCheckpoint(_ uint64, _ common.Hash) {}
|
||||||
|
|
||||||
func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash {
|
func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *whitelistFake) PurgeCheckpointWhitelist() {}
|
func (w *whitelistFake) PurgeCheckpointWhitelist() {}
|
||||||
|
func (w *whitelistFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
|
||||||
|
return map[uint64]*types.Header{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TestFakedSyncProgress66WhitelistMismatch tests if in case of whitelisted
|
// TestFakedSyncProgress66WhitelistMismatch tests if in case of whitelisted
|
||||||
// checkpoint mismatch with opposite peer, the sync should fail.
|
// checkpoint mismatch with opposite peer, the sync should fail.
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ func newTestBlockchain(blocks []*types.Block) *core.BlockChain {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000))
|
core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000))
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ type Service struct {
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, populated by reaching out to heimdall
|
checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, populated by reaching out to heimdall
|
||||||
checkpointOrder []uint64 // Checkpoint order, populated by reaching out to heimdall
|
checkpointOrder []uint64 // Checkpoint order, populated by reaching out to heimdall
|
||||||
maxCapacity uint
|
maxCapacity uint // Max capacity of the whitelist
|
||||||
|
checkpointInterval uint64 // Checkpoint interval, until which we can allow importing
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(maxCapacity uint) *Service {
|
func NewService(maxCapacity uint) *Service {
|
||||||
|
|
@ -23,6 +24,7 @@ func NewService(maxCapacity uint) *Service {
|
||||||
checkpointWhitelist: make(map[uint64]common.Hash),
|
checkpointWhitelist: make(map[uint64]common.Hash),
|
||||||
checkpointOrder: []uint64{},
|
checkpointOrder: []uint64{},
|
||||||
maxCapacity: maxCapacity,
|
maxCapacity: maxCapacity,
|
||||||
|
checkpointInterval: 256, // TODO: make it configurable through params?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,9 +33,9 @@ var (
|
||||||
ErrNoRemoteCheckoint = errors.New("remote peer doesn't have a checkoint")
|
ErrNoRemoteCheckoint = errors.New("remote peer doesn't have a checkoint")
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsValidChain checks if the chain we're about to receive from this peer is valid or not
|
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
|
||||||
// in terms of reorgs. We won't reorg beyond the last bor checkpoint submitted to mainchain.
|
// in terms of reorgs. We won't reorg beyond the last bor checkpoint submitted to mainchain.
|
||||||
func (w *Service) IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
func (w *Service) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
|
||||||
// We want to validate the chain by comparing the last checkpointed block
|
// We want to validate the chain by comparing the last checkpointed block
|
||||||
// we're storing in `checkpointWhitelist` with the peer's block.
|
// we're storing in `checkpointWhitelist` with the peer's block.
|
||||||
//
|
//
|
||||||
|
|
@ -70,6 +72,84 @@ func (w *Service) IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber
|
||||||
return false, ErrCheckpointMismatch
|
return false, ErrCheckpointMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidChain checks the validity of chain by comparing it
|
||||||
|
// against the local checkpoint entries
|
||||||
|
func (w *Service) IsValidChain(currentHeader *types.Header, chain []*types.Header) bool {
|
||||||
|
// Check if we have checkpoints to validate incoming chain in memory
|
||||||
|
if len(w.checkpointWhitelist) == 0 {
|
||||||
|
// We don't have any entries, no additional validation will be possible
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if we've received empty chain
|
||||||
|
if len(chain) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
oldestCheckpointNumber uint64 = w.checkpointOrder[0]
|
||||||
|
current uint64 = currentHeader.Number.Uint64()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check if we have whitelist entries in required range
|
||||||
|
if chain[len(chain)-1].Number.Uint64() < oldestCheckpointNumber {
|
||||||
|
// We have future whitelisted entries, so no additional validation will be possible
|
||||||
|
// This case will occur when bor is in middle of sync, but heimdall is ahead/fully synced.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the chain into past and future chain
|
||||||
|
pastChain, futureChain := splitChain(current, chain)
|
||||||
|
|
||||||
|
// Add an offset to future chain if it's not in continuity
|
||||||
|
offset := 0
|
||||||
|
if len(futureChain) != 0 {
|
||||||
|
offset += int(futureChain[0].Number.Uint64()-currentHeader.Number.Uint64()) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't accept future chain of unacceptable length (from current block)
|
||||||
|
if len(futureChain)+offset > int(w.checkpointInterval) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the chain and validate against the last checkpoint
|
||||||
|
// It will handle all cases where the incoming chain has atleast one checkpoint
|
||||||
|
for i := len(pastChain) - 1; i >= 0; i-- {
|
||||||
|
if _, ok := w.checkpointWhitelist[pastChain[i].Number.Uint64()]; ok {
|
||||||
|
return pastChain[i].Hash() == w.checkpointWhitelist[pastChain[i].Number.Uint64()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitChain(current uint64, chain []*types.Header) ([]*types.Header, []*types.Header) {
|
||||||
|
var (
|
||||||
|
pastChain []*types.Header
|
||||||
|
futureChain []*types.Header
|
||||||
|
first uint64 = chain[0].Number.Uint64()
|
||||||
|
last uint64 = chain[len(chain)-1].Number.Uint64()
|
||||||
|
)
|
||||||
|
|
||||||
|
if current >= first {
|
||||||
|
if len(chain) == 1 || current >= last {
|
||||||
|
pastChain = chain
|
||||||
|
} else {
|
||||||
|
pastChain = chain[:current-first+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current < last {
|
||||||
|
if len(chain) == 1 || current < first {
|
||||||
|
futureChain = chain
|
||||||
|
} else {
|
||||||
|
futureChain = chain[current-first+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pastChain, futureChain
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
|
func (w *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
|
||||||
w.m.Lock()
|
w.m.Lock()
|
||||||
defer w.m.Unlock()
|
defer w.m.Unlock()
|
||||||
|
|
@ -116,7 +196,7 @@ func (w *Service) dequeueCheckpointWhitelist() {
|
||||||
log.Debug("Dequeing checkpoint whitelist", "block number", w.checkpointOrder[0], "block hash", w.checkpointWhitelist[w.checkpointOrder[0]])
|
log.Debug("Dequeing checkpoint whitelist", "block number", w.checkpointOrder[0], "block hash", w.checkpointWhitelist[w.checkpointOrder[0]])
|
||||||
|
|
||||||
delete(w.checkpointWhitelist, w.checkpointOrder[0])
|
delete(w.checkpointWhitelist, w.checkpointOrder[0])
|
||||||
w.checkpointOrder = w.checkpointOrder[1:]
|
w.checkpointOrder = w.checkpointOrder[1:] // fixme: this slice is growing infinitely and never will be released. also a panic is possible if the last element is going to be removed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,26 @@ package whitelist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gotest.tools/assert"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewMockService creates a new mock whitelist service
|
// NewMockService creates a new mock whitelist service
|
||||||
func NewMockService(maxCapacity uint) *Service {
|
func NewMockService(maxCapacity uint, checkpointInterval uint64) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
checkpointWhitelist: make(map[uint64]common.Hash),
|
checkpointWhitelist: make(map[uint64]common.Hash),
|
||||||
checkpointOrder: []uint64{},
|
checkpointOrder: []uint64{},
|
||||||
maxCapacity: maxCapacity,
|
maxCapacity: maxCapacity,
|
||||||
|
checkpointInterval: checkpointInterval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,34 +29,34 @@ func NewMockService(maxCapacity uint) *Service {
|
||||||
func TestWhitelistCheckpoint(t *testing.T) {
|
func TestWhitelistCheckpoint(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
s := NewMockService(10)
|
s := NewMockService(10, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
s.enqueueCheckpointWhitelist(uint64(i), common.Hash{})
|
s.enqueueCheckpointWhitelist(uint64(i), common.Hash{})
|
||||||
}
|
}
|
||||||
assert.Equal(t, s.length(), 10, "expected 10 items in whitelist")
|
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
|
||||||
|
|
||||||
s.enqueueCheckpointWhitelist(11, common.Hash{})
|
s.enqueueCheckpointWhitelist(11, common.Hash{})
|
||||||
s.dequeueCheckpointWhitelist()
|
s.dequeueCheckpointWhitelist()
|
||||||
assert.Equal(t, s.length(), 10, "expected 10 items in whitelist")
|
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestIsValidChain checks che IsValidChain function in isolation
|
// TestIsValidPeer checks the IsValidPeer function in isolation
|
||||||
// for different cases by providing a mock fetchHeadersByNumber function
|
// for different cases by providing a mock fetchHeadersByNumber function
|
||||||
func TestIsValidChain(t *testing.T) {
|
func TestIsValidPeer(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
s := NewMockService(10)
|
s := NewMockService(10, 10)
|
||||||
|
|
||||||
// case1: no checkpoint whitelist, should consider the chain as valid
|
// case1: no checkpoint whitelist, should consider the chain as valid
|
||||||
res, err := s.IsValidChain(nil, nil)
|
res, err := s.IsValidPeer(nil, nil)
|
||||||
assert.NilError(t, err, "expected no error")
|
require.NoError(t, err, "expected no error")
|
||||||
assert.Equal(t, res, true, "expected chain to be valid")
|
require.Equal(t, res, true, "expected chain to be valid")
|
||||||
|
|
||||||
// add checkpoint entries and mock fetchHeadersByNumber function
|
// add checkpoint entries and mock fetchHeadersByNumber function
|
||||||
s.ProcessCheckpoint(uint64(0), common.Hash{})
|
s.ProcessCheckpoint(uint64(0), common.Hash{})
|
||||||
s.ProcessCheckpoint(uint64(1), common.Hash{})
|
s.ProcessCheckpoint(uint64(1), common.Hash{})
|
||||||
|
|
||||||
assert.Equal(t, s.length(), 2, "expected 2 items in whitelist")
|
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
|
||||||
|
|
||||||
// create a false function, returning absolutely nothing
|
// create a false function, returning absolutely nothing
|
||||||
falseFetchHeadersByNumber := func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error) {
|
falseFetchHeadersByNumber := func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error) {
|
||||||
|
|
@ -60,7 +65,7 @@ func TestIsValidChain(t *testing.T) {
|
||||||
|
|
||||||
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
|
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
|
||||||
// and throw `ErrNoRemoteCheckoint` error
|
// and throw `ErrNoRemoteCheckoint` error
|
||||||
res, err = s.IsValidChain(nil, falseFetchHeadersByNumber)
|
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error, got nil")
|
t.Fatal("expected error, got nil")
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +74,7 @@ func TestIsValidChain(t *testing.T) {
|
||||||
t.Fatalf("expected error ErrNoRemoteCheckoint, got %v", err)
|
t.Fatalf("expected error ErrNoRemoteCheckoint, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, res, false, "expected chain to be invalid")
|
require.Equal(t, res, false, "expected chain to be invalid")
|
||||||
|
|
||||||
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
|
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
|
||||||
// create a mock function, returning a the required header
|
// create a mock function, returning a the required header
|
||||||
|
|
@ -91,17 +96,316 @@ func TestIsValidChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = s.IsValidChain(nil, fetchHeadersByNumber)
|
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
|
||||||
assert.NilError(t, err, "expected no error")
|
require.NoError(t, err, "expected no error")
|
||||||
assert.Equal(t, res, true, "expected chain to be valid")
|
require.Equal(t, res, true, "expected chain to be valid")
|
||||||
|
|
||||||
// add one more checkpoint whitelist entry
|
// add one more checkpoint whitelist entry
|
||||||
s.ProcessCheckpoint(uint64(2), common.Hash{})
|
s.ProcessCheckpoint(uint64(2), common.Hash{})
|
||||||
assert.Equal(t, s.length(), 3, "expected 3 items in whitelist")
|
require.Equal(t, s.length(), 3, "expected 3 items in whitelist")
|
||||||
|
|
||||||
// case4: correct fetchHeadersByNumber function provided with wrong header
|
// case4: correct fetchHeadersByNumber function provided with wrong header
|
||||||
// for block number 2. Should consider the chain as invalid and throw an error
|
// for block number 2. Should consider the chain as invalid and throw an error
|
||||||
res, err = s.IsValidChain(nil, fetchHeadersByNumber)
|
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
|
||||||
assert.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
|
require.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
|
||||||
assert.Equal(t, res, false, "expected chain to be invalid")
|
require.Equal(t, res, false, "expected chain to be invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIsValidChain checks the IsValidChain function in isolation
|
||||||
|
// for different cases by providing a mock current header and chain
|
||||||
|
func TestIsValidChain(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
s := NewMockService(10, 10)
|
||||||
|
chainA := createMockChain(1, 20) // A1->A2...A19->A20
|
||||||
|
// case1: no checkpoint whitelist, should consider the chain as valid
|
||||||
|
res := s.IsValidChain(nil, chainA)
|
||||||
|
require.Equal(t, res, true, "expected chain to be valid")
|
||||||
|
|
||||||
|
tempChain := createMockChain(21, 22) // A21->A22
|
||||||
|
|
||||||
|
// add mock checkpoint entries
|
||||||
|
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
|
||||||
|
s.ProcessCheckpoint(tempChain[1].Number.Uint64(), tempChain[1].Hash())
|
||||||
|
|
||||||
|
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
|
||||||
|
|
||||||
|
// case2: We're behind the oldest whitelisted block entry, should consider
|
||||||
|
// the chain as valid as we're still far behind the latest blocks
|
||||||
|
res = s.IsValidChain(chainA[len(chainA)-1], chainA)
|
||||||
|
require.Equal(t, res, true, "expected chain to be valid")
|
||||||
|
|
||||||
|
// Clear checkpoint whitelist and add blocks A5 and A15 in whitelist
|
||||||
|
s.PurgeCheckpointWhitelist()
|
||||||
|
s.ProcessCheckpoint(chainA[5].Number.Uint64(), chainA[5].Hash())
|
||||||
|
s.ProcessCheckpoint(chainA[15].Number.Uint64(), chainA[15].Hash())
|
||||||
|
|
||||||
|
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
|
||||||
|
|
||||||
|
// case3: Try importing a past chain having valid checkpoint, should
|
||||||
|
// consider the chain as valid
|
||||||
|
res = s.IsValidChain(chainA[len(chainA)-1], chainA)
|
||||||
|
require.Equal(t, res, true, "expected chain to be valid")
|
||||||
|
|
||||||
|
// Clear checkpoint whitelist and mock blocks in whitelist
|
||||||
|
tempChain = createMockChain(20, 20) // A20
|
||||||
|
|
||||||
|
s.PurgeCheckpointWhitelist()
|
||||||
|
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
|
||||||
|
|
||||||
|
require.Equal(t, s.length(), 1, "expected 1 items in whitelist")
|
||||||
|
|
||||||
|
// case4: Try importing a past chain having invalid checkpoint
|
||||||
|
res = s.IsValidChain(chainA[len(chainA)-1], chainA)
|
||||||
|
require.Equal(t, res, false, "expected chain to be invalid")
|
||||||
|
|
||||||
|
// create a future chain to be imported of length <= `checkpointInterval`
|
||||||
|
chainB := createMockChain(21, 30) // B21->B22...B29->B30
|
||||||
|
|
||||||
|
// case5: Try importing a future chain of acceptable length
|
||||||
|
res = s.IsValidChain(chainA[len(chainA)-1], chainB)
|
||||||
|
require.Equal(t, res, true, "expected chain to be valid")
|
||||||
|
|
||||||
|
// create a future chain to be imported of length > `checkpointInterval`
|
||||||
|
chainB = createMockChain(21, 40) // C21->C22...C39->C40
|
||||||
|
|
||||||
|
// case5: Try importing a future chain of unacceptable length
|
||||||
|
res = s.IsValidChain(chainA[len(chainA)-1], chainB)
|
||||||
|
require.Equal(t, res, false, "expected chain to be invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSplitChain(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
pastStart uint64
|
||||||
|
pastEnd uint64
|
||||||
|
futureStart uint64
|
||||||
|
futureEnd uint64
|
||||||
|
pastLength int
|
||||||
|
futureLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current chain is at block: X
|
||||||
|
// Incoming chain is represented as [N, M]
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
current uint64
|
||||||
|
chain []*types.Header
|
||||||
|
result Result
|
||||||
|
}{
|
||||||
|
{name: "X = 10, N = 11, M = 20", current: uint64(10), chain: createMockChain(11, 20), result: Result{futureStart: 11, futureEnd: 20, futureLength: 10}},
|
||||||
|
{name: "X = 10, N = 13, M = 20", current: uint64(10), chain: createMockChain(13, 20), result: Result{futureStart: 13, futureEnd: 20, futureLength: 8}},
|
||||||
|
{name: "X = 10, N = 2, M = 10", current: uint64(10), chain: createMockChain(2, 10), result: Result{pastStart: 2, pastEnd: 10, pastLength: 9}},
|
||||||
|
{name: "X = 10, N = 2, M = 9", current: uint64(10), chain: createMockChain(2, 9), result: Result{pastStart: 2, pastEnd: 9, pastLength: 8}},
|
||||||
|
{name: "X = 10, N = 2, M = 8", current: uint64(10), chain: createMockChain(2, 8), result: Result{pastStart: 2, pastEnd: 8, pastLength: 7}},
|
||||||
|
{name: "X = 10, N = 5, M = 15", current: uint64(10), chain: createMockChain(5, 15), result: Result{pastStart: 5, pastEnd: 10, pastLength: 6, futureStart: 11, futureEnd: 15, futureLength: 5}},
|
||||||
|
{name: "X = 10, N = 10, M = 20", current: uint64(10), chain: createMockChain(10, 20), result: Result{pastStart: 10, pastEnd: 10, pastLength: 1, futureStart: 11, futureEnd: 20, futureLength: 10}},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
past, future := splitChain(tc.current, tc.chain)
|
||||||
|
require.Equal(t, len(past), tc.result.pastLength)
|
||||||
|
require.Equal(t, len(future), tc.result.futureLength)
|
||||||
|
|
||||||
|
if len(past) > 0 {
|
||||||
|
// Check if we have expected block/s
|
||||||
|
require.Equal(t, past[0].Number.Uint64(), tc.result.pastStart)
|
||||||
|
require.Equal(t, past[len(past)-1].Number.Uint64(), tc.result.pastEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(future) > 0 {
|
||||||
|
// Check if we have expected block/s
|
||||||
|
require.Equal(t, future[0].Number.Uint64(), tc.result.futureStart)
|
||||||
|
require.Equal(t, future[len(future)-1].Number.Uint64(), tc.result.futureEnd)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit
|
||||||
|
func TestSplitChainProperties(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Current chain is at block: X
|
||||||
|
// Incoming chain is represented as [N, M]
|
||||||
|
|
||||||
|
currentChain := []int{0, 1, 2, 3, 10, 100} // blocks starting from genesis
|
||||||
|
blockDiffs := []int{0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 90, 100, 101, 102}
|
||||||
|
|
||||||
|
caseParams := make(map[int]map[int]map[int]struct{}) // X -> N -> M
|
||||||
|
|
||||||
|
for _, current := range currentChain {
|
||||||
|
// past cases only + past to current
|
||||||
|
for _, diff := range blockDiffs {
|
||||||
|
from := current - diff
|
||||||
|
|
||||||
|
// use int type for everything to not care about underflow
|
||||||
|
if from < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, diff := range blockDiffs {
|
||||||
|
to := current - diff
|
||||||
|
|
||||||
|
if to >= from {
|
||||||
|
addTestCaseParams(caseParams, current, from, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// future only + current to future
|
||||||
|
for _, diff := range blockDiffs {
|
||||||
|
from := current + diff
|
||||||
|
|
||||||
|
if from < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, diff := range blockDiffs {
|
||||||
|
to := current + diff
|
||||||
|
|
||||||
|
if to >= from {
|
||||||
|
addTestCaseParams(caseParams, current, from, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// past-current-future
|
||||||
|
for _, diff := range blockDiffs {
|
||||||
|
from := current - diff
|
||||||
|
|
||||||
|
if from < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, diff := range blockDiffs {
|
||||||
|
to := current + diff
|
||||||
|
|
||||||
|
if to >= from {
|
||||||
|
addTestCaseParams(caseParams, current, from, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
current int
|
||||||
|
remoteStart int
|
||||||
|
remoteEnd int
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts []testCase
|
||||||
|
|
||||||
|
// X -> N -> M
|
||||||
|
for x, nm := range caseParams {
|
||||||
|
for n, mMap := range nm {
|
||||||
|
for m := range mMap {
|
||||||
|
ts = append(ts, testCase{x, n, m})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:paralleltest
|
||||||
|
for i, tc := range ts {
|
||||||
|
tc := tc
|
||||||
|
|
||||||
|
name := fmt.Sprintf("test case: index = %d, X = %d, N = %d, M = %d", i, tc.current, tc.remoteStart, tc.remoteEnd)
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
chain := createMockChain(uint64(tc.remoteStart), uint64(tc.remoteEnd))
|
||||||
|
|
||||||
|
past, future := splitChain(uint64(tc.current), chain)
|
||||||
|
|
||||||
|
// properties
|
||||||
|
if len(past) > 0 {
|
||||||
|
// Check if the chain is ordered
|
||||||
|
isOrdered := sort.SliceIsSorted(past, func(i, j int) bool {
|
||||||
|
return past[i].Number.Uint64() < past[j].Number.Uint64()
|
||||||
|
})
|
||||||
|
|
||||||
|
require.True(t, isOrdered, "an ordered past chain expected: %v", past)
|
||||||
|
|
||||||
|
isSequential := sort.SliceIsSorted(past, func(i, j int) bool {
|
||||||
|
return past[i].Number.Uint64() == past[j].Number.Uint64()-1
|
||||||
|
})
|
||||||
|
|
||||||
|
require.True(t, isSequential, "a sequential past chain expected: %v", past)
|
||||||
|
|
||||||
|
// Check if current block >= past chain's last block
|
||||||
|
require.Equal(t, past[len(past)-1].Number.Uint64() <= uint64(tc.current), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(future) > 0 {
|
||||||
|
// Check if the chain is ordered
|
||||||
|
isOrdered := sort.SliceIsSorted(future, func(i, j int) bool {
|
||||||
|
return future[i].Number.Uint64() < future[j].Number.Uint64()
|
||||||
|
})
|
||||||
|
|
||||||
|
require.True(t, isOrdered, "an ordered future chain expected: %v", future)
|
||||||
|
|
||||||
|
isSequential := sort.SliceIsSorted(future, func(i, j int) bool {
|
||||||
|
return future[i].Number.Uint64() == future[j].Number.Uint64()-1
|
||||||
|
})
|
||||||
|
|
||||||
|
require.True(t, isSequential, "a sequential future chain expected: %v", future)
|
||||||
|
|
||||||
|
// Check if future chain's first block > current block
|
||||||
|
require.Equal(t, future[len(future)-1].Number.Uint64() > uint64(tc.current), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if both chains are continuous
|
||||||
|
if len(past) > 0 && len(future) > 0 {
|
||||||
|
require.Equal(t, past[len(past)-1].Number.Uint64(), future[0].Number.Uint64()-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we get the original chain on appending both
|
||||||
|
gotChain := append(past, future...)
|
||||||
|
require.Equal(t, reflect.DeepEqual(gotChain, chain), true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createMockChain returns a chain with dummy headers
|
||||||
|
// starting from `start` to `end` (inclusive)
|
||||||
|
func createMockChain(start, end uint64) []*types.Header {
|
||||||
|
var (
|
||||||
|
i uint64
|
||||||
|
idx uint64
|
||||||
|
chain []*types.Header = make([]*types.Header, end-start+1)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i = start; i <= end; i++ {
|
||||||
|
header := &types.Header{
|
||||||
|
Number: big.NewInt(int64(i)),
|
||||||
|
Time: uint64(time.Now().UnixMicro()) + i,
|
||||||
|
}
|
||||||
|
chain[idx] = header
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
|
// mXNM should be initialized
|
||||||
|
func addTestCaseParams(mXNM map[int]map[int]map[int]struct{}, x, n, m int) {
|
||||||
|
//nolint:ineffassign
|
||||||
|
mNM, ok := mXNM[x]
|
||||||
|
if !ok {
|
||||||
|
mNM = make(map[int]map[int]struct{})
|
||||||
|
mXNM[x] = mNM
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:ineffassign
|
||||||
|
_, ok = mNM[n]
|
||||||
|
if !ok {
|
||||||
|
mM := make(map[int]struct{})
|
||||||
|
mNM[n] = mM
|
||||||
|
}
|
||||||
|
|
||||||
|
mXNM[x][n][m] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/contract"
|
"github.com/ethereum/go-ethereum/consensus/bor/contract"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall" //nolint:typecheck
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/heimdallgrpc"
|
||||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
|
@ -175,6 +176,7 @@ type Config struct {
|
||||||
TrieTimeout time.Duration
|
TrieTimeout time.Duration
|
||||||
SnapshotCache int
|
SnapshotCache int
|
||||||
Preimages bool
|
Preimages bool
|
||||||
|
TriesInMemory uint64
|
||||||
|
|
||||||
// Mining options
|
// Mining options
|
||||||
Miner miner.Config
|
Miner miner.Config
|
||||||
|
|
@ -216,6 +218,9 @@ type Config struct {
|
||||||
// No heimdall service
|
// No heimdall service
|
||||||
WithoutHeimdall bool
|
WithoutHeimdall bool
|
||||||
|
|
||||||
|
// Address to connect to Heimdall gRPC server
|
||||||
|
HeimdallgRPCAddress string
|
||||||
|
|
||||||
// Bor logs flag
|
// Bor logs flag
|
||||||
BorLogs bool
|
BorLogs bool
|
||||||
|
|
||||||
|
|
@ -246,7 +251,14 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, et
|
||||||
if ethConfig.WithoutHeimdall {
|
if ethConfig.WithoutHeimdall {
|
||||||
return bor.New(chainConfig, db, blockchainAPI, spanner, nil, genesisContractsClient)
|
return bor.New(chainConfig, db, blockchainAPI, spanner, nil, genesisContractsClient)
|
||||||
} else {
|
} else {
|
||||||
return bor.New(chainConfig, db, blockchainAPI, spanner, heimdall.NewHeimdallClient(ethConfig.HeimdallURL), genesisContractsClient)
|
var heimdallClient bor.IHeimdallClient
|
||||||
|
if ethConfig.HeimdallgRPCAddress != "" {
|
||||||
|
heimdallClient = heimdallgrpc.NewHeimdallGRPCClient(ethConfig.HeimdallgRPCAddress)
|
||||||
|
} else {
|
||||||
|
heimdallClient = heimdall.NewHeimdallClient(ethConfig.HeimdallURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bor.New(chainConfig, db, blockchainAPI, spanner, heimdallClient, genesisContractsClient)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch config.PowMode {
|
switch config.PowMode {
|
||||||
|
|
|
||||||
257
eth/filters/IBackend.go
Normal file
257
eth/filters/IBackend.go
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/ethereum/go-ethereum/eth/filters (interfaces: Backend)
|
||||||
|
|
||||||
|
// Package filters is a generated GoMock package.
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
common "github.com/ethereum/go-ethereum/common"
|
||||||
|
core "github.com/ethereum/go-ethereum/core"
|
||||||
|
bloombits "github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
|
types "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
ethdb "github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
event "github.com/ethereum/go-ethereum/event"
|
||||||
|
rpc "github.com/ethereum/go-ethereum/rpc"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockBackend is a mock of Backend interface.
|
||||||
|
type MockBackend struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockBackendMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockBackendMockRecorder is the mock recorder for MockBackend.
|
||||||
|
type MockBackendMockRecorder struct {
|
||||||
|
mock *MockBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockBackend creates a new mock instance.
|
||||||
|
func NewMockBackend(ctrl *gomock.Controller) *MockBackend {
|
||||||
|
mock := &MockBackend{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockBackendMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockBackend) EXPECT() *MockBackendMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// BloomStatus mocks base method.
|
||||||
|
func (m *MockBackend) BloomStatus() (uint64, uint64) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "BloomStatus")
|
||||||
|
ret0, _ := ret[0].(uint64)
|
||||||
|
ret1, _ := ret[1].(uint64)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// BloomStatus indicates an expected call of BloomStatus.
|
||||||
|
func (mr *MockBackendMockRecorder) BloomStatus() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BloomStatus", reflect.TypeOf((*MockBackend)(nil).BloomStatus))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainDb mocks base method.
|
||||||
|
func (m *MockBackend) ChainDb() ethdb.Database {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ChainDb")
|
||||||
|
ret0, _ := ret[0].(ethdb.Database)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainDb indicates an expected call of ChainDb.
|
||||||
|
func (mr *MockBackendMockRecorder) ChainDb() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainDb", reflect.TypeOf((*MockBackend)(nil).ChainDb))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBorBlockLogs mocks base method.
|
||||||
|
func (m *MockBackend) GetBorBlockLogs(arg0 context.Context, arg1 common.Hash) ([]*types.Log, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetBorBlockLogs", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]*types.Log)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBorBlockLogs indicates an expected call of GetBorBlockLogs.
|
||||||
|
func (mr *MockBackendMockRecorder) GetBorBlockLogs(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBorBlockLogs", reflect.TypeOf((*MockBackend)(nil).GetBorBlockLogs), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBorBlockReceipt mocks base method.
|
||||||
|
func (m *MockBackend) GetBorBlockReceipt(arg0 context.Context, arg1 common.Hash) (*types.Receipt, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetBorBlockReceipt", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*types.Receipt)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBorBlockReceipt indicates an expected call of GetBorBlockReceipt.
|
||||||
|
func (mr *MockBackendMockRecorder) GetBorBlockReceipt(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBorBlockReceipt", reflect.TypeOf((*MockBackend)(nil).GetBorBlockReceipt), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogs mocks base method.
|
||||||
|
func (m *MockBackend) GetLogs(arg0 context.Context, arg1 common.Hash) ([][]*types.Log, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetLogs", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([][]*types.Log)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogs indicates an expected call of GetLogs.
|
||||||
|
func (mr *MockBackendMockRecorder) GetLogs(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogs", reflect.TypeOf((*MockBackend)(nil).GetLogs), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceipts mocks base method.
|
||||||
|
func (m *MockBackend) GetReceipts(arg0 context.Context, arg1 common.Hash) (types.Receipts, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetReceipts", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(types.Receipts)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceipts indicates an expected call of GetReceipts.
|
||||||
|
func (mr *MockBackendMockRecorder) GetReceipts(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReceipts", reflect.TypeOf((*MockBackend)(nil).GetReceipts), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByHash mocks base method.
|
||||||
|
func (m *MockBackend) HeaderByHash(arg0 context.Context, arg1 common.Hash) (*types.Header, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "HeaderByHash", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*types.Header)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByHash indicates an expected call of HeaderByHash.
|
||||||
|
func (mr *MockBackendMockRecorder) HeaderByHash(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderByHash", reflect.TypeOf((*MockBackend)(nil).HeaderByHash), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByNumber mocks base method.
|
||||||
|
func (m *MockBackend) HeaderByNumber(arg0 context.Context, arg1 rpc.BlockNumber) (*types.Header, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "HeaderByNumber", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*types.Header)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByNumber indicates an expected call of HeaderByNumber.
|
||||||
|
func (mr *MockBackendMockRecorder) HeaderByNumber(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderByNumber", reflect.TypeOf((*MockBackend)(nil).HeaderByNumber), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceFilter mocks base method.
|
||||||
|
func (m *MockBackend) ServiceFilter(arg0 context.Context, arg1 *bloombits.MatcherSession) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "ServiceFilter", arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceFilter indicates an expected call of ServiceFilter.
|
||||||
|
func (mr *MockBackendMockRecorder) ServiceFilter(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceFilter", reflect.TypeOf((*MockBackend)(nil).ServiceFilter), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeChainEvent mocks base method.
|
||||||
|
func (m *MockBackend) SubscribeChainEvent(arg0 chan<- core.ChainEvent) event.Subscription {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SubscribeChainEvent", arg0)
|
||||||
|
ret0, _ := ret[0].(event.Subscription)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeChainEvent indicates an expected call of SubscribeChainEvent.
|
||||||
|
func (mr *MockBackendMockRecorder) SubscribeChainEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeChainEvent", reflect.TypeOf((*MockBackend)(nil).SubscribeChainEvent), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeLogsEvent mocks base method.
|
||||||
|
func (m *MockBackend) SubscribeLogsEvent(arg0 chan<- []*types.Log) event.Subscription {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SubscribeLogsEvent", arg0)
|
||||||
|
ret0, _ := ret[0].(event.Subscription)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeLogsEvent indicates an expected call of SubscribeLogsEvent.
|
||||||
|
func (mr *MockBackendMockRecorder) SubscribeLogsEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeLogsEvent", reflect.TypeOf((*MockBackend)(nil).SubscribeLogsEvent), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeNewTxsEvent mocks base method.
|
||||||
|
func (m *MockBackend) SubscribeNewTxsEvent(arg0 chan<- core.NewTxsEvent) event.Subscription {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SubscribeNewTxsEvent", arg0)
|
||||||
|
ret0, _ := ret[0].(event.Subscription)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeNewTxsEvent indicates an expected call of SubscribeNewTxsEvent.
|
||||||
|
func (mr *MockBackendMockRecorder) SubscribeNewTxsEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeNewTxsEvent", reflect.TypeOf((*MockBackend)(nil).SubscribeNewTxsEvent), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribePendingLogsEvent mocks base method.
|
||||||
|
func (m *MockBackend) SubscribePendingLogsEvent(arg0 chan<- []*types.Log) event.Subscription {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SubscribePendingLogsEvent", arg0)
|
||||||
|
ret0, _ := ret[0].(event.Subscription)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribePendingLogsEvent indicates an expected call of SubscribePendingLogsEvent.
|
||||||
|
func (mr *MockBackendMockRecorder) SubscribePendingLogsEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribePendingLogsEvent", reflect.TypeOf((*MockBackend)(nil).SubscribePendingLogsEvent), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeRemovedLogsEvent mocks base method.
|
||||||
|
func (m *MockBackend) SubscribeRemovedLogsEvent(arg0 chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SubscribeRemovedLogsEvent", arg0)
|
||||||
|
ret0, _ := ret[0].(event.Subscription)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeRemovedLogsEvent indicates an expected call of SubscribeRemovedLogsEvent.
|
||||||
|
func (mr *MockBackendMockRecorder) SubscribeRemovedLogsEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeRemovedLogsEvent", reflect.TypeOf((*MockBackend)(nil).SubscribeRemovedLogsEvent), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeStateSyncEvent mocks base method.
|
||||||
|
func (m *MockBackend) SubscribeStateSyncEvent(arg0 chan<- core.StateSyncEvent) event.Subscription {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SubscribeStateSyncEvent", arg0)
|
||||||
|
ret0, _ := ret[0].(event.Subscription)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeStateSyncEvent indicates an expected call of SubscribeStateSyncEvent.
|
||||||
|
func (mr *MockBackendMockRecorder) SubscribeStateSyncEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeStateSyncEvent", reflect.TypeOf((*MockBackend)(nil).SubscribeStateSyncEvent), arg0)
|
||||||
|
}
|
||||||
368
eth/filters/IDatabase.go
Normal file
368
eth/filters/IDatabase.go
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/ethereum/go-ethereum/ethdb (interfaces: Database)
|
||||||
|
|
||||||
|
// Package filters is a generated GoMock package.
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
ethdb "github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockDatabase is a mock of Database interface.
|
||||||
|
type MockDatabase struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockDatabaseMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDatabaseMockRecorder is the mock recorder for MockDatabase.
|
||||||
|
type MockDatabaseMockRecorder struct {
|
||||||
|
mock *MockDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockDatabase creates a new mock instance.
|
||||||
|
func NewMockDatabase(ctrl *gomock.Controller) *MockDatabase {
|
||||||
|
mock := &MockDatabase{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockDatabaseMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockDatabase) EXPECT() *MockDatabaseMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ancient mocks base method.
|
||||||
|
func (m *MockDatabase) Ancient(arg0 string, arg1 uint64) ([]byte, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Ancient", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ancient indicates an expected call of Ancient.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Ancient(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ancient", reflect.TypeOf((*MockDatabase)(nil).Ancient), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AncientRange mocks base method.
|
||||||
|
func (m *MockDatabase) AncientRange(arg0 string, arg1, arg2, arg3 uint64) ([][]byte, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AncientRange", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].([][]byte)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AncientRange indicates an expected call of AncientRange.
|
||||||
|
func (mr *MockDatabaseMockRecorder) AncientRange(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AncientRange", reflect.TypeOf((*MockDatabase)(nil).AncientRange), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AncientSize mocks base method.
|
||||||
|
func (m *MockDatabase) AncientSize(arg0 string) (uint64, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AncientSize", arg0)
|
||||||
|
ret0, _ := ret[0].(uint64)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AncientSize indicates an expected call of AncientSize.
|
||||||
|
func (mr *MockDatabaseMockRecorder) AncientSize(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AncientSize", reflect.TypeOf((*MockDatabase)(nil).AncientSize), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ancients mocks base method.
|
||||||
|
func (m *MockDatabase) Ancients() (uint64, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Ancients")
|
||||||
|
ret0, _ := ret[0].(uint64)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ancients indicates an expected call of Ancients.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Ancients() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ancients", reflect.TypeOf((*MockDatabase)(nil).Ancients))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mocks base method.
|
||||||
|
func (m *MockDatabase) Close() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Close")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close indicates an expected call of Close.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Close() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDatabase)(nil).Close))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact mocks base method.
|
||||||
|
func (m *MockDatabase) Compact(arg0, arg1 []byte) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Compact", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact indicates an expected call of Compact.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Compact(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compact", reflect.TypeOf((*MockDatabase)(nil).Compact), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete mocks base method.
|
||||||
|
func (m *MockDatabase) Delete(arg0 []byte) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Delete", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete indicates an expected call of Delete.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Delete(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDatabase)(nil).Delete), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mocks base method.
|
||||||
|
func (m *MockDatabase) Get(arg0 []byte) ([]byte, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Get", arg0)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get indicates an expected call of Get.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Get(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockDatabase)(nil).Get), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has mocks base method.
|
||||||
|
func (m *MockDatabase) Has(arg0 []byte) (bool, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Has", arg0)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has indicates an expected call of Has.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Has(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Has", reflect.TypeOf((*MockDatabase)(nil).Has), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAncient mocks base method.
|
||||||
|
func (m *MockDatabase) HasAncient(arg0 string, arg1 uint64) (bool, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "HasAncient", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAncient indicates an expected call of HasAncient.
|
||||||
|
func (mr *MockDatabaseMockRecorder) HasAncient(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasAncient", reflect.TypeOf((*MockDatabase)(nil).HasAncient), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigrateTable mocks base method.
|
||||||
|
func (m *MockDatabase) MigrateTable(arg0 string, arg1 func([]byte) ([]byte, error)) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "MigrateTable", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigrateTable indicates an expected call of MigrateTable.
|
||||||
|
func (mr *MockDatabaseMockRecorder) MigrateTable(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MigrateTable", reflect.TypeOf((*MockDatabase)(nil).MigrateTable), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyAncients mocks base method.
|
||||||
|
func (m *MockDatabase) ModifyAncients(arg0 func(ethdb.AncientWriteOp) error) (int64, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ModifyAncients", arg0)
|
||||||
|
ret0, _ := ret[0].(int64)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyAncients indicates an expected call of ModifyAncients.
|
||||||
|
func (mr *MockDatabaseMockRecorder) ModifyAncients(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyAncients", reflect.TypeOf((*MockDatabase)(nil).ModifyAncients), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatch mocks base method.
|
||||||
|
func (m *MockDatabase) NewBatch() ethdb.Batch {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "NewBatch")
|
||||||
|
ret0, _ := ret[0].(ethdb.Batch)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatch indicates an expected call of NewBatch.
|
||||||
|
func (mr *MockDatabaseMockRecorder) NewBatch() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewBatch", reflect.TypeOf((*MockDatabase)(nil).NewBatch))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchWithSize mocks base method.
|
||||||
|
func (m *MockDatabase) NewBatchWithSize(arg0 int) ethdb.Batch {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "NewBatchWithSize", arg0)
|
||||||
|
ret0, _ := ret[0].(ethdb.Batch)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchWithSize indicates an expected call of NewBatchWithSize.
|
||||||
|
func (mr *MockDatabaseMockRecorder) NewBatchWithSize(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewBatchWithSize", reflect.TypeOf((*MockDatabase)(nil).NewBatchWithSize), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIterator mocks base method.
|
||||||
|
func (m *MockDatabase) NewIterator(arg0, arg1 []byte) ethdb.Iterator {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "NewIterator", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(ethdb.Iterator)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIterator indicates an expected call of NewIterator.
|
||||||
|
func (mr *MockDatabaseMockRecorder) NewIterator(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewIterator", reflect.TypeOf((*MockDatabase)(nil).NewIterator), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSnapshot mocks base method.
|
||||||
|
func (m *MockDatabase) NewSnapshot() (ethdb.Snapshot, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "NewSnapshot")
|
||||||
|
ret0, _ := ret[0].(ethdb.Snapshot)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSnapshot indicates an expected call of NewSnapshot.
|
||||||
|
func (mr *MockDatabaseMockRecorder) NewSnapshot() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSnapshot", reflect.TypeOf((*MockDatabase)(nil).NewSnapshot))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put mocks base method.
|
||||||
|
func (m *MockDatabase) Put(arg0, arg1 []byte) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Put", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put indicates an expected call of Put.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Put(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockDatabase)(nil).Put), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAncients mocks base method.
|
||||||
|
func (m *MockDatabase) ReadAncients(arg0 func(ethdb.AncientReader) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ReadAncients", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAncients indicates an expected call of ReadAncients.
|
||||||
|
func (mr *MockDatabaseMockRecorder) ReadAncients(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadAncients", reflect.TypeOf((*MockDatabase)(nil).ReadAncients), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat mocks base method.
|
||||||
|
func (m *MockDatabase) Stat(arg0 string) (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Stat", arg0)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat indicates an expected call of Stat.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Stat(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockDatabase)(nil).Stat), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync mocks base method.
|
||||||
|
func (m *MockDatabase) Sync() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Sync")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync indicates an expected call of Sync.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Sync() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sync", reflect.TypeOf((*MockDatabase)(nil).Sync))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail mocks base method.
|
||||||
|
func (m *MockDatabase) Tail() (uint64, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Tail")
|
||||||
|
ret0, _ := ret[0].(uint64)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail indicates an expected call of Tail.
|
||||||
|
func (mr *MockDatabaseMockRecorder) Tail() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tail", reflect.TypeOf((*MockDatabase)(nil).Tail))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruncateHead mocks base method.
|
||||||
|
func (m *MockDatabase) TruncateHead(arg0 uint64) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "TruncateHead", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruncateHead indicates an expected call of TruncateHead.
|
||||||
|
func (mr *MockDatabaseMockRecorder) TruncateHead(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TruncateHead", reflect.TypeOf((*MockDatabase)(nil).TruncateHead), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruncateTail mocks base method.
|
||||||
|
func (m *MockDatabase) TruncateTail(arg0 uint64) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "TruncateTail", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruncateTail indicates an expected call of TruncateTail.
|
||||||
|
func (mr *MockDatabaseMockRecorder) TruncateTail(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TruncateTail", reflect.TypeOf((*MockDatabase)(nil).TruncateTail), arg0)
|
||||||
|
}
|
||||||
|
|
@ -337,8 +337,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
|
||||||
return nil, errors.New("No chain config found. Proper PublicFilterAPI initialization required")
|
return nil, errors.New("No chain config found. Proper PublicFilterAPI initialization required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// get sprint from bor config
|
borConfig := api.chainConfig.Bor
|
||||||
sprint := api.chainConfig.Bor.Sprint
|
|
||||||
|
|
||||||
var filter *Filter
|
var filter *Filter
|
||||||
var borLogsFilter *BorBlockLogsFilter
|
var borLogsFilter *BorBlockLogsFilter
|
||||||
|
|
@ -347,7 +346,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
|
||||||
filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics)
|
filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics)
|
||||||
// Block bor filter
|
// Block bor filter
|
||||||
if api.borLogs {
|
if api.borLogs {
|
||||||
borLogsFilter = NewBorBlockLogsFilter(api.backend, sprint, *crit.BlockHash, crit.Addresses, crit.Topics)
|
borLogsFilter = NewBorBlockLogsFilter(api.backend, borConfig, *crit.BlockHash, crit.Addresses, crit.Topics)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Convert the RPC block numbers into internal representations
|
// Convert the RPC block numbers into internal representations
|
||||||
|
|
@ -363,7 +362,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
|
||||||
filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics)
|
filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics)
|
||||||
// Block bor filter
|
// Block bor filter
|
||||||
if api.borLogs {
|
if api.borLogs {
|
||||||
borLogsFilter = NewBorBlockLogsRangeFilter(api.backend, sprint, begin, end, crit.Addresses, crit.Topics)
|
borLogsFilter = NewBorBlockLogsRangeFilter(api.backend, borConfig, begin, end, crit.Addresses, crit.Topics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,10 +416,20 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty
|
||||||
return nil, fmt.Errorf("filter not found")
|
return nil, fmt.Errorf("filter not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
borConfig := api.chainConfig.Bor
|
||||||
|
|
||||||
var filter *Filter
|
var filter *Filter
|
||||||
|
|
||||||
|
var borLogsFilter *BorBlockLogsFilter
|
||||||
|
|
||||||
if f.crit.BlockHash != nil {
|
if f.crit.BlockHash != nil {
|
||||||
// Block filter requested, construct a single-shot filter
|
// Block filter requested, construct a single-shot filter
|
||||||
filter = NewBlockFilter(api.backend, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics)
|
filter = NewBlockFilter(api.backend, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics)
|
||||||
|
|
||||||
|
// Block bor filter
|
||||||
|
if api.borLogs {
|
||||||
|
borLogsFilter = NewBorBlockLogsFilter(api.backend, borConfig, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Convert the RPC block numbers into internal representations
|
// Convert the RPC block numbers into internal representations
|
||||||
begin := rpc.LatestBlockNumber.Int64()
|
begin := rpc.LatestBlockNumber.Int64()
|
||||||
|
|
@ -433,12 +442,27 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty
|
||||||
}
|
}
|
||||||
// Construct the range filter
|
// Construct the range filter
|
||||||
filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics)
|
filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics)
|
||||||
|
|
||||||
|
if api.borLogs {
|
||||||
|
borLogsFilter = NewBorBlockLogsRangeFilter(api.backend, borConfig, begin, end, f.crit.Addresses, f.crit.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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if borLogsFilter != nil {
|
||||||
|
// Run the filter and return all the logs
|
||||||
|
borBlockLogs, err := borLogsFilter.Logs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnLogs(types.MergeBorLogs(logs, borBlockLogs)), nil
|
||||||
|
}
|
||||||
|
|
||||||
return returnLogs(logs), nil
|
return returnLogs(logs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,13 +122,14 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
|
||||||
|
|
||||||
b.Log("Running filter benchmarks...")
|
b.Log("Running filter benchmarks...")
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
var backend *testBackend
|
|
||||||
|
var backend *TestBackend
|
||||||
|
|
||||||
for i := 0; i < benchFilterCnt; i++ {
|
for i := 0; i < benchFilterCnt; i++ {
|
||||||
if i%20 == 0 {
|
if i%20 == 0 {
|
||||||
db.Close()
|
db.Close()
|
||||||
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
|
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
|
||||||
backend = &testBackend{db: db, sections: cnt}
|
backend = &TestBackend{DB: db, sections: cnt}
|
||||||
}
|
}
|
||||||
var addr common.Address
|
var addr common.Address
|
||||||
addr[0] = byte(i)
|
addr[0] = byte(i)
|
||||||
|
|
@ -173,7 +174,7 @@ func BenchmarkNoBloomBits(b *testing.B) {
|
||||||
|
|
||||||
b.Log("Running filter benchmarks...")
|
b.Log("Running filter benchmarks...")
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
backend := &testBackend{db: db}
|
backend := &TestBackend{DB: db}
|
||||||
filter := NewRangeFilter(backend, 0, int64(*headNum), []common.Address{{}}, nil)
|
filter := NewRangeFilter(backend, 0, int64(*headNum), []common.Address{{}}, nil)
|
||||||
filter.Logs(context.Background())
|
filter.Logs(context.Background())
|
||||||
d := time.Since(start)
|
d := time.Since(start)
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,12 @@ func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCrit
|
||||||
}
|
}
|
||||||
|
|
||||||
// get sprint from bor config
|
// get sprint from bor config
|
||||||
sprint := api.chainConfig.Bor.Sprint
|
borConfig := api.chainConfig.Bor
|
||||||
|
|
||||||
var filter *BorBlockLogsFilter
|
var filter *BorBlockLogsFilter
|
||||||
if crit.BlockHash != nil {
|
if crit.BlockHash != nil {
|
||||||
// Block filter requested, construct a single-shot filter
|
// Block filter requested, construct a single-shot filter
|
||||||
filter = NewBorBlockLogsFilter(api.backend, sprint, *crit.BlockHash, crit.Addresses, crit.Topics)
|
filter = NewBorBlockLogsFilter(api.backend, borConfig, *crit.BlockHash, crit.Addresses, crit.Topics)
|
||||||
} else {
|
} else {
|
||||||
// Convert the RPC block numbers into internal representations
|
// Convert the RPC block numbers into internal representations
|
||||||
begin := rpc.LatestBlockNumber.Int64()
|
begin := rpc.LatestBlockNumber.Int64()
|
||||||
|
|
@ -40,7 +40,7 @@ func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCrit
|
||||||
end = crit.ToBlock.Int64()
|
end = crit.ToBlock.Int64()
|
||||||
}
|
}
|
||||||
// Construct the range filter
|
// Construct the range filter
|
||||||
filter = NewBorBlockLogsRangeFilter(api.backend, sprint, begin, end, crit.Addresses, crit.Topics)
|
filter = NewBorBlockLogsRangeFilter(api.backend, borConfig, begin, end, crit.Addresses, crit.Topics)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the filter and return all the logs
|
// Run the filter and return all the logs
|
||||||
|
|
@ -59,8 +59,9 @@ func (api *PublicFilterAPI) NewDeposits(ctx context.Context, crit ethereum.State
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcSub := notifier.CreateSubscription()
|
rpcSub := notifier.CreateSubscription()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
stateSyncData := make(chan *types.StateSyncData)
|
stateSyncData := make(chan *types.StateSyncData, 10)
|
||||||
stateSyncSub := api.events.SubscribeNewDeposits(stateSyncData)
|
stateSyncSub := api.events.SubscribeNewDeposits(stateSyncData)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue