Merge pull request #603 from maticnetwork/arpit/v0.3.1-mumbai-candidate

v0.3.1 mumbai candidate
This commit is contained in:
Arpit Temani 2022-12-14 00:43:26 +05:30 committed by GitHub
commit b7f37c7ce3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
203 changed files with 8727 additions and 1745 deletions

23
.github/CODEOWNERS vendored
View file

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

View file

@ -1,30 +1,71 @@
---
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: ''
labels: 'type:bug'
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
Commit hash : (if `develop`)
<!--
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).
-->
#### 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)-->
````
[backtrace]
````
#### **Overview of the problem**
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.
-->

View file

@ -8,10 +8,11 @@ assignees: ''
# Rationale
Why should this feature exist?
The motivation behind the feature and why should this feature exist?
What are the use-cases?
# Implementation
Do you have ideas regarding the implementation of this feature?
Are you willing to implement this feature?
Do you have ideas regarding the implementation of this feature? (Mention reference links if any)
Any alternative solutions or features you've considered?
Are you willing to implement this feature?

View file

@ -1,9 +1,11 @@
---
name: Ask a question
about: Something is unclear
name: Question/Support
about: Ask a question or request support
title: ''
labels: 'type:docs'
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.

View file

@ -3,9 +3,13 @@ defaultFee: 2000
borChainId: "15001"
heimdallChainId: heimdall-15001
contractsBranch: jc/v0.3.1-backport
sprintSize: 64
blockNumber: '0'
blockTime: '2'
numOfValidators: 3
numOfNonValidators: 0
ethURL: http://ganache:9545
ethHostUser: ubuntu
devnetType: docker
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
View 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

View file

@ -8,7 +8,7 @@ on:
pull_request:
branches:
- "**"
types: [opened, synchronize, edited]
types: [opened, synchronize]
concurrency:
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)
strategy:
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 }}
steps:
- uses: actions/checkout@v3
@ -29,7 +29,7 @@ jobs:
- uses: actions/setup-go@v3
with:
go-version: 1.18.x
go-version: 1.19.x
- name: Install dependencies on Linux
if: runner.os == 'Linux'
@ -63,7 +63,7 @@ jobs:
- name: test-integration
run: make test-integration
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
@ -104,7 +104,7 @@ jobs:
uses: actions/checkout@v3
with:
repository: maticnetwork/matic-cli
ref: v0.3.0-dev
ref: arpit/pos-655-2
path: matic-cli
- name: Install dependencies on Linux
@ -154,7 +154,7 @@ jobs:
cd matic-cli/devnet/code/contracts
npm run truffle exec scripts/deposit.js -- --network development $(jq -r .root.tokens.MaticToken contractAddresses.json) 100000000000000000000
cd -
bash bor/integration-tests/smoke_test.sh
timeout 20m bash bor/integration-tests/smoke_test.sh
- name: Upload logs
if: always()

View file

@ -21,7 +21,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@master
with:
go-version: 1.17.x
go-version: 1.19.x
- name: Prepare
id: prepare

2
.gitignore vendored
View file

@ -53,3 +53,5 @@ profile.cov
./bor-debug-*
dist
*.csv

3
.gitmodules vendored
View file

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

View file

@ -50,6 +50,7 @@ linters:
- unconvert
- unparam
- wsl
- asasalint
#- errorlint causes stack overflow. TODO: recheck after each golangci update
linters-settings:

View file

@ -34,6 +34,10 @@ bor:
protoc:
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:
$(GORUN) build/ci.go install ./cmd/geth
@echo "Done building."
@ -61,7 +65,7 @@ test-race:
$(GOTEST) --timeout 15m -race -shuffle=on $(TESTALL)
test-integration:
$(GOTEST) --timeout 30m -tags integration $(TESTE2E)
$(GOTEST) --timeout 60m -tags integration $(TESTE2E)
escape:
cd $(path) && go test -gcflags "-m -m" -run none -bench=BenchmarkJumpdest* -benchmem -memprofile mem.out
@ -71,7 +75,7 @@ lint:
lintci-deps:
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 -local "$(PACKAGE)" -w .
@ -93,7 +97,7 @@ devtools:
$(GOBUILD) -o $(GOBIN)/codecgen github.com/ugorji/go/codec/codecgen
$(GOBUILD) -o $(GOBIN)/abigen ./cmd/abigen
$(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 ./core/types
PATH=$(GOBIN):$(PATH) go generate ./consensus/bor
@ -192,7 +196,7 @@ geth-windows-amd64:
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
PACKAGE_NAME := github.com/maticnetwork/bor
GOLANG_CROSS_VERSION ?= v1.18.1
GOLANG_CROSS_VERSION ?= v1.19.1
.PHONY: release-dry-run
release-dry-run:
@ -220,6 +224,7 @@ release:
-e DOCKER_PASSWORD \
-e SLACK_WEBHOOK \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(HOME)/.docker/config.json:/root/.docker/config.json \
-v `pwd`:/go/src/$(PACKAGE_NAME) \
-w /go/src/$(PACKAGE_NAME) \
goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \

View file

@ -1,5 +1,5 @@
# 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.
![Forks](https://img.shields.io/github/forks/maticnetwork/bor?style=social)
![Stars](https://img.shields.io/github/stars/maticnetwork/bor?style=social)

103
RETESTBOR.md Normal file
View 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, youll 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 clients 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`

View file

@ -21,7 +21,6 @@ import (
"crypto/ecdsa"
"errors"
"io"
"io/ioutil"
"math/big"
"github.com/ethereum/go-ethereum/accounts"
@ -45,14 +44,17 @@ var ErrNotAuthorized = errors.New("not authorized to sign this account")
// Deprecated: Use NewTransactorWithChainID instead.
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
json, err := ioutil.ReadAll(keyin)
json, err := io.ReadAll(keyin)
if err != nil {
return nil, err
}
key, err := keystore.DecryptKey(json, passphrase)
if err != nil {
return nil, err
}
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
// an encrypted json key stream and the associated passphrase.
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 {
return nil, err
}

View file

@ -78,7 +78,7 @@ type SimulatedBackend struct {
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
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{
database: database,

View file

@ -18,7 +18,6 @@ package bind
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@ -1966,7 +1965,7 @@ func TestGolangBindings(t *testing.T) {
t.Skip("go sdk not found for testing")
}
// Create a temporary workspace for the test suite
ws, err := ioutil.TempDir("", "binding-test")
ws, err := os.MkdirTemp("", "binding-test")
if err != nil {
t.Fatalf("failed to create temporary workspace: %v", err)
}
@ -1990,7 +1989,7 @@ func TestGolangBindings(t *testing.T) {
if err != nil {
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)
}
// Generate the test file with the injected test code
@ -2006,7 +2005,7 @@ func TestGolangBindings(t *testing.T) {
%s
}
`, 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)
}
})

View file

@ -126,6 +126,7 @@ syncmode = "full"
# noprefetch = false
# preimages = false
# txlookuplimit = 2350000
# triesinmemory = 128
# timeout = "1h0m0s"
[accounts]

View file

@ -18,8 +18,12 @@
"period": {
"0": 2
},
"producerDelay": 6,
"sprint": 64,
"producerDelay": {
"0": 6
},
"sprint": {
"0": 64
},
"backupMultiplier": {
"0": 2
},

View file

@ -15,15 +15,24 @@
"londonBlock": 22640000,
"bor": {
"jaipurBlock": 22770000,
"delhiBlock": 29638656,
"period": {
"0": 2,
"25275000": 5
"25275000": 5,
"29638656": 2
},
"producerDelay": {
"0": 6,
"29638656": 4
},
"sprint": {
"0": 64,
"29638656": 16
},
"producerDelay": 6,
"sprint": 64,
"backupMultiplier": {
"0": 2,
"25275000": 5
"25275000": 5,
"29638656": 2
},
"validatorContract": "0x0000000000000000000000000000000000001000",
"stateReceiverContract": "0x0000000000000000000000000000000000001001",

View file

@ -17,6 +17,7 @@
package t8ntool
import (
"context"
"crypto/ecdsa"
"encoding/json"
"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
// "Sealing result is not read by miner" if it cannot write the result.
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))
}
found := <-results

View file

@ -223,6 +223,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txIndex++
}
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
// Add mining reward?
if miningReward > 0 {

View file

@ -108,6 +108,7 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to
// bor related flags
utils.HeimdallURLFlag,
utils.WithoutHeimdallFlag,
utils.HeimdallgRPCAddressFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `

View file

@ -18,14 +18,12 @@ package main
import (
"fmt"
"io/ioutil"
"math/big"
"os"
"time"
"gopkg.in/urfave/cli.v1"
"github.com/BurntSushi/toml"
"gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/accounts/external"
"github.com/ethereum/go-ethereum/accounts/keystore"
@ -71,7 +69,7 @@ type gethConfig struct {
}
func loadConfig(file string, cfg *gethConfig) error {
data, err := ioutil.ReadFile(file)
data, err := os.ReadFile(file)
if err != nil {
return err
}

View file

@ -5,11 +5,12 @@ import (
"io/ioutil"
"os"
"gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"gopkg.in/urfave/cli.v1"
)
var (
@ -30,10 +31,18 @@ var (
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 = []cli.Flag{
HeimdallURLFlag,
WithoutHeimdallFlag,
HeimdallgRPCAddressFlag,
}
)
@ -56,6 +65,7 @@ func getGenesis(genesisPath string) (*core.Genesis, error) {
func SetBorConfig(ctx *cli.Context, cfg *eth.Config) {
cfg.HeimdallURL = ctx.GlobalString(HeimdallURLFlag.Name)
cfg.WithoutHeimdall = ctx.GlobalBool(WithoutHeimdallFlag.Name)
cfg.HeimdallgRPCAddress = ctx.GlobalString(HeimdallgRPCAddressFlag.Name)
}
// CreateBorEthereum Creates bor ethereum object from eth.Config

View file

@ -33,6 +33,10 @@ import (
"text/template"
"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/keystore"
"github.com/ethereum/go-ethereum/common"
@ -68,9 +72,6 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"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() {
@ -1503,7 +1504,7 @@ func setPeerRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
if peerRequiredBlocks == "" {
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)
} else {
return
@ -2030,9 +2031,10 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
engine = clique.New(config.Clique, chainDb)
} else if config.Bor != nil {
ethereum = CreateBorEthereum(&eth.Config{
Genesis: genesis,
HeimdallURL: ctx.GlobalString(HeimdallURLFlag.Name),
WithoutHeimdall: ctx.GlobalBool(WithoutHeimdallFlag.Name),
Genesis: genesis,
HeimdallURL: ctx.GlobalString(HeimdallURLFlag.Name),
WithoutHeimdall: ctx.GlobalBool(WithoutHeimdallFlag.Name),
HeimdallgRPCAddress: ctx.GlobalString(HeimdallgRPCAddressFlag.Name),
})
engine = ethereum.Engine()
} 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.
// 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 {
Fatalf("Can't create BlockChain: %v", err)
}

36
common/network/port.go Normal file
View 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
View 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...)
}
}

View file

@ -17,6 +17,7 @@
package beacon
import (
"context"
"errors"
"fmt"
"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
// stock Ethereum consensus engine. The difference between the beacon and classic is
// (a) The following fields are expected to be constants:
// - difficulty is expected to be 0
// - nonce is expected to be 0
// - unclehash is expected to be Hash(emptyHeader)
// - difficulty is expected to be 0
// - nonce is expected to be 0
// - unclehash is expected to be Hash(emptyHeader)
// to be the desired constants
//
// (b) the timestamp is not verified anymore
// (c) the extradata is limited to 32 bytes
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
// 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
// generation and verification. So determine the consensus rules by header type.
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
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
// 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()) {
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,
// return directly without pushing any block back. In another word

View file

@ -2,6 +2,7 @@ package bor
import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"errors"
@ -11,13 +12,17 @@ import (
"sort"
"strconv"
"sync"
"sync/atomic"
"time"
lru "github.com/hashicorp/golang-lru"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/accounts"
"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/bor/api"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
@ -45,7 +50,9 @@ const (
// Bor protocol constants.
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
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
// be modified via out-of-range or non-contiguous headers.
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
@ -159,7 +169,7 @@ func encodeSigHeader(w io.Writer, header *types.Header, c *params.BorConfig) {
header.Nonce,
}
if c.IsJaipur(header.Number.Uint64()) {
if c.IsJaipur(header.Number) {
if header.BaseFee != nil {
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`.
// That is to allow time for block propagation in the last sprint
delay := c.CalculatePeriod(number)
if number%c.Sprint == 0 {
delay = c.ProducerDelay
if number%c.CalculateSprint(number) == 0 {
delay = c.CalculateProducerDelay(number)
}
if succession > 0 {
@ -209,9 +219,7 @@ type Bor struct {
recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
signer common.Address // Ethereum address of the signing key
signFn SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields
authorizedSigner atomic.Pointer[signer] // Ethereum address and sign function of the signing key
ethAPI api.Caller
spanner Spanner
@ -224,6 +232,11 @@ type Bor struct {
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.
func New(
chainConfig *params.ChainConfig,
@ -237,7 +250,7 @@ func New(
borConfig := chainConfig.Bor
// Set any missing consensus parameters to their defaults
if borConfig != nil && borConfig.Sprint == 0 {
if borConfig != nil && borConfig.CalculateSprint(0) == 0 {
borConfig.Sprint = defaultSprintLength
}
// Allocate the snapshot caches and create the engine
@ -256,6 +269,14 @@ func New(
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.
for key, genesisAlloc := range c.config.BlockAlloc {
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.
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)
}
// 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
// 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{})
results := make(chan error, len(headers))
@ -320,7 +341,7 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head
}
// 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
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
if IsSprintStart(number, c.config.Sprint) {
if IsSprintStart(number, c.config.CalculateSprint(number)) {
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
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
// up more headers than allowed to be reorged (chain reinit from a freezer),
// consider the checkpoint trusted and snapshot it.
// TODO fix this
// nolint:nestif
if number == 0 {
@ -496,7 +518,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
hash := checkpoint.Hash()
// 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 {
return nil, err
}
@ -537,7 +559,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
// check if snapshot is 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
@ -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
// 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 {
return errors.New("uncles not allowed")
return errUncleDetected
}
return nil
@ -602,7 +624,7 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
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
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)
}
if parent != nil && header.Time < parent.Time+CalcProducerDelay(number, succession, c.config) {
if IsBlockOnTime(parent, header, number, succession, c.config) {
return &BlockTooSoonError{number, succession}
}
// Ensure that the difficulty corresponds to the turn-ness of the signer
if !c.fakeDiff {
difficulty := snap.Difficulty(signer)
difficulty := Difficulty(snap.ValidatorSet, signer)
if header.Difficulty.Uint64() != difficulty {
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
}
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
// header for running the transactions on top.
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
}
currentSigner := *c.authorizedSigner.Load()
// 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
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]
// get validator set if number
if IsSprintStart(number+1, c.config.Sprint) {
newValidators, err := c.spanner.GetCurrentValidators(header.ParentHash, number+1)
if IsSprintStart(number+1, c.config.CalculateSprint(number)) {
newValidators, err := c.spanner.GetCurrentValidators(context.Background(), header.ParentHash, number+1)
if err != nil {
return errors.New("unknown validators")
return errUnknownValidators
}
// sort validator by address
@ -687,8 +715,8 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
var succession int
// if signer is not empty
if c.signer != (common.Address{}) {
succession, err = snap.GetSignerSuccessionNumber(c.signer)
if currentSigner.signer != (common.Address{}) {
succession, err = snap.GetSignerSuccessionNumber(currentSigner.signer)
if err != nil {
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
// rewards given.
func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
stateSyncData := []*types.StateSyncData{}
var err error
func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, _ []*types.Transaction, _ []*types.Header) {
var (
stateSyncData []*types.StateSyncData
err error
)
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}
// 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)
return
}
if c.HeimdallClient != nil {
// commit statees
stateSyncData, err = c.CommitStates(state, header, cx)
// commit states
stateSyncData, err = c.CommitStates(ctx, state, header, cx)
if err != nil {
log.Error("Error while committing states", "error", err)
return
@ -763,7 +793,7 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
if blockNumber == strconv.FormatUint(headerNumber, 10) {
allocs, err := decodeGenesisAlloc(genesisAlloc)
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 {
@ -778,24 +808,35 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// 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{}
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}
// check and commit span
err := c.checkAndCommitSpan(state, header, cx)
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
// check and commit span
err = c.checkAndCommitSpan(finalizeCtx, state, header, cx)
})
if err != nil {
log.Error("Error while committing span", "error", err)
return nil, err
}
if c.HeimdallClient != nil {
// commit states
stateSyncData, err = c.CommitStates(state, header, cx)
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
// commit states
stateSyncData, err = c.CommitStates(finalizeCtx, state, header, cx)
})
if err != nil {
log.Error("Error while committing states", "error", 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)
return nil, err
}
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// No block rewards in PoA, so the state remains as it is
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)
// Assemble block
@ -819,23 +868,41 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
bc := chain.(core.BorStateSyncer)
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 block, nil
}
// Authorize injects a private key into the consensus engine to mint new blocks
// with.
func (c *Bor) Authorize(signer common.Address, signFn SignerFn) {
c.lock.Lock()
defer c.lock.Unlock()
c.signer = signer
c.signFn = signFn
func (c *Bor) Authorize(currentSigner common.Address, signFn SignerFn) {
c.authorizedSigner.Store(&signer{
signer: currentSigner,
signFn: signFn,
})
}
// Seal implements consensus.Engine, attempting to create a sealed block using
// 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()
// Sealing the genesis block is not supported
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")
return nil
}
// Don't hold the signer fields for the entire sealing procedure
c.lock.RLock()
signer, signFn := c.signer, c.signFn
c.lock.RUnlock()
currentSigner := *c.authorizedSigner.Load()
snap, err := c.snapshot(chain, number-1, header.ParentHash, 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
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
if !snap.ValidatorSet.HasAddress(currentSigner.signer) {
// 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 {
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
// Sign all the things!
err = Sign(signFn, signer, header, c.config)
err = Sign(currentSigner.signFn, currentSigner.signer, header, c.config)
if err != nil {
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.
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 {
case <-stop:
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,
"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 {
case results <- block.WithSeal(header):
default:
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
}
@ -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
// that a new block should have based on the previous blocks in the chain and the
// 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)
if err != 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.
@ -967,37 +1047,38 @@ func (c *Bor) Close() error {
}
func (c *Bor) checkAndCommitSpan(
ctx context.Context,
state *state.StateDB,
header *types.Header,
chain core.ChainContext,
) error {
headerNumber := header.Number.Uint64()
span, err := c.spanner.GetCurrentSpan(header.ParentHash)
span, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash)
if err != nil {
return err
}
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
}
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 == nil {
if currentSpan == nil {
return false
}
// check span is not set initially
if span.EndBlock == 0 {
if currentSpan.EndBlock == 0 {
return true
}
// 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
}
@ -1005,6 +1086,7 @@ func (c *Bor) needToCommitSpan(span *span.Span, headerNumber uint64) bool {
}
func (c *Bor) FetchAndCommitSpan(
ctx context.Context,
newSpanID uint64,
state *state.StateDB,
header *types.Header,
@ -1014,14 +1096,14 @@ func (c *Bor) FetchAndCommitSpan(
if c.HeimdallClient == nil {
// 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 {
return err
}
heimdallSpan = *s
} else {
response, err := c.HeimdallClient.Span(newSpanID)
response, err := c.HeimdallClient.Span(ctx, newSpanID)
if err != nil {
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
func (c *Bor) CommitStates(
ctx context.Context,
state *state.StateDB,
header *types.Header,
chain statefull.ChainContext,
) ([]*types.StateSyncData, error) {
stateSyncs := make([]*types.StateSyncData, 0)
fetchStart := time.Now()
number := header.Number.Uint64()
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
@ -1055,7 +1138,7 @@ func (c *Bor) CommitStates(
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()
log.Info(
@ -1063,7 +1146,7 @@ func (c *Bor) CommitStates(
"fromID", lastStateID+1,
"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 {
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
chainID := c.chainConfig.ChainID.String()
stateSyncs := make([]*types.StateSyncData, len(eventRecords))
var gasUsed uint64
for _, eventRecord := range eventRecords {
if eventRecord.ID <= lastStateID {
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())
break
}
@ -1097,7 +1184,10 @@ func (c *Bor) CommitStates(
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 {
return nil, err
}
@ -1107,7 +1197,9 @@ func (c *Bor) CommitStates(
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
}
@ -1125,8 +1217,8 @@ func (c *Bor) SetHeimdallClient(h IHeimdallClient) {
c.HeimdallClient = h
}
func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
return c.spanner.GetCurrentValidators(headerHash, blockNumber)
func (c *Bor) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
return c.spanner.GetCurrentValidators(ctx, headerHash, blockNumber)
}
//
@ -1134,13 +1226,14 @@ func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) (
//
func (c *Bor) getNextHeimdallSpanForTest(
ctx context.Context,
newSpanID uint64,
header *types.Header,
chain core.ChainContext,
) (*span.HeimdallSpan, error) {
headerNumber := header.Number.Uint64()
spanBor, err := c.spanner.GetCurrentSpan(header.ParentHash)
spanBor, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash)
if err != nil {
return nil, err
}
@ -1161,7 +1254,7 @@ func (c *Bor) getNextHeimdallSpanForTest(
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))
for i, v := range snap.ValidatorSet.Validators {

View file

@ -4,10 +4,10 @@ import (
"math/big"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"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/rawdb"
"github.com/ethereum/go-ethereum/core/state"
@ -23,7 +23,9 @@ func TestGenesisContractChange(t *testing.T) {
b := &Bor{
config: &params.BorConfig{
Sprint: 10, // skip sprint transactions in sprint
Sprint: map[string]uint64{
"0": 10,
}, // skip sprint transactions in sprint
BlockAlloc: map[string]interface{}{
// write as interface since that is how it is decoded in genesis
"2": map[string]interface{}{
@ -55,11 +57,11 @@ func TestGenesisContractChange(t *testing.T) {
genesis := genspec.MustCommit(db)
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
assert.NoError(t, err)
require.NoError(t, err)
config := params.ChainConfig{}
chain, err := core.NewBlockChain(db, nil, &config, b, vm.Config{}, nil, nil)
assert.NoError(t, err)
chain, err := core.NewBlockChain(db, nil, &config, b, vm.Config{}, nil, nil, nil)
require.NoError(t, err)
addBlock := func(root common.Hash, num int64) (common.Hash, *state.StateDB) {
h := &types.Header{
@ -70,37 +72,37 @@ func TestGenesisContractChange(t *testing.T) {
// write state to database
root, err := statedb.Commit(false)
assert.NoError(t, err)
assert.NoError(t, statedb.Database().TrieDB().Commit(root, true, nil))
require.NoError(t, err)
require.NoError(t, statedb.Database().TrieDB().Commit(root, true, nil))
statedb, err := state.New(h.Root, state.NewDatabase(db), nil)
assert.NoError(t, err)
require.NoError(t, err)
return root, statedb
}
assert.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
root := genesis.Root()
// code does not change
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
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
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
_, 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
assert.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
}
func TestEncodeSigHeaderJaipur(t *testing.T) {
@ -123,20 +125,20 @@ func TestEncodeSigHeaderJaipur(t *testing.T) {
)
// Jaipur NOT enabled and BaseFee not set
hash := SealHash(h, &params.BorConfig{JaipurBlock: 10})
assert.Equal(t, hash, hashWithoutBaseFee)
hash := SealHash(h, &params.BorConfig{JaipurBlock: big.NewInt(10)})
require.Equal(t, hash, hashWithoutBaseFee)
// Jaipur enabled (Jaipur=0) and BaseFee not set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 0})
assert.Equal(t, hash, hashWithoutBaseFee)
hash = SealHash(h, &params.BorConfig{JaipurBlock: common.Big0})
require.Equal(t, hash, hashWithoutBaseFee)
h.BaseFee = big.NewInt(2)
// Jaipur enabled (Jaipur=Header block) and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 1})
assert.Equal(t, hash, hashWithBaseFee)
hash = SealHash(h, &params.BorConfig{JaipurBlock: common.Big1})
require.Equal(t, hash, hashWithBaseFee)
// Jaipur NOT enabled and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 10})
assert.Equal(t, hash, hashWithoutBaseFee)
hash = SealHash(h, &params.BorConfig{JaipurBlock: big.NewInt(10)})
require.Equal(t, hash, hashWithoutBaseFee)
}

View file

@ -88,7 +88,7 @@ func (gc *GenesisContractsClient) CommitState(
}
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
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) {
blockNr := rpc.BlockNumber(snapshotNumber)
method := "lastStateId"
const method = "lastStateId"
data, err := gc.stateReceiverABI.Pack(method)
if err != nil {

View file

@ -1,6 +1,8 @@
package bor
import (
"context"
"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/span"
@ -8,8 +10,9 @@ import (
//go:generate mockgen -destination=../../tests/bor/mocks/IHeimdallClient.go -package=mocks . IHeimdallClient
type IHeimdallClient interface {
StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
Span(spanID uint64) (*span.HeimdallSpan, error)
FetchLatestCheckpoint() (*checkpoint.Checkpoint, error)
StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error)
Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error)
FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
FetchCheckpointCount(ctx context.Context) (int64, error)
Close()
}

View file

@ -20,3 +20,12 @@ type CheckpointResponse struct {
Height string `json:"height"`
Result Checkpoint `json:"result"`
}
type CheckpointCount struct {
Result int64 `json:"result"`
}
type CheckpointCountResponse struct {
Height string `json:"height"`
Result CheckpointCount `json:"result"`
}

View file

@ -5,7 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"sort"
@ -15,14 +15,20 @@ import (
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)
// errShutdownDetected is returned if a shutdown was detected
var errShutdownDetected = errors.New("shutdown detected")
var (
// 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 (
stateFetchLimit = 50
apiHeimdallTimeout = 5 * time.Second
retryCall = 5 * time.Second
)
type StateSyncEventsResponse struct {
@ -41,6 +47,12 @@ type HeimdallClient struct {
closeCh chan struct{}
}
type Request struct {
client http.Client
url *url.URL
start time.Time
}
func NewHeimdallClient(urlString string) *HeimdallClient {
return &HeimdallClient{
urlString: urlString,
@ -54,12 +66,13 @@ func NewHeimdallClient(urlString string) *HeimdallClient {
const (
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
fetchStateSyncEventsPath = "clerk/event-record/list"
fetchLatestCheckpoint = "/checkpoints/latest"
fetchCheckpoint = "/checkpoints/%s"
fetchCheckpointCount = "/checkpoints/count"
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)
for {
@ -70,7 +83,9 @@ func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.Even
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 {
return nil, err
}
@ -96,13 +111,15 @@ func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.Even
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)
if err != nil {
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 {
return nil, err
}
@ -110,14 +127,16 @@ func (h *HeimdallClient) Span(spanID uint64) (*span.HeimdallSpan, error) {
return &response.Result, nil
}
// FetchLatestCheckpoint fetches the latest bor submitted checkpoint from heimdall
func (h *HeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error) {
url, err := latestCheckpointURL(h.urlString)
// FetchCheckpoint fetches the checkpoint from heimdall
func (h *HeimdallClient) FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error) {
url, err := checkpointURL(h.urlString, number)
if err != nil {
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 {
return nil, err
}
@ -125,59 +144,107 @@ func (h *HeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error)
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
func FetchWithRetry[T any](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)
func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
// request data once
body, err := internalFetch(ctx, client, url)
cancel()
if err == nil && body != nil {
err = json.Unmarshal(body, result)
if err != nil {
return nil, err
}
request := &Request{client: client, url: url, start: time.Now()}
result, err := Fetch[T](ctx, request)
if err == 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
ticker := time.NewTicker(5 * time.Second)
ticker := time.NewTicker(retryCall)
defer ticker.Stop()
const logEach = 5
retryLoop:
for {
log.Info("Retrying again in 5 seconds to fetch data from Heimdall", "path", url.Path, "attempt", attempt)
attempt++
select {
case <-ctx.Done():
log.Debug("Shutdown detected, terminating request by context.Done")
return nil, ctx.Err()
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:
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)
cancel()
if err == nil && body != nil {
err = json.Unmarshal(body, result)
if err != nil {
return nil, err
if err != nil {
if attempt%logEach == 0 {
log.Warn("an error while trying fetching from Heimdall", "attempt", attempt, "error", 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) {
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)
}
func latestCheckpointURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchLatestCheckpoint, "")
func checkpointURL(urlString string, number int64) (*url.URL, error) {
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) {
@ -215,11 +293,12 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
if err != nil {
return nil, err
}
defer res.Body.Close()
// check status code
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
@ -228,7 +307,7 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
}
// get response
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
@ -236,6 +315,14 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte,
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
func (h *HeimdallClient) Close() {
close(h.closeCh)

View file

@ -1,9 +1,306 @@
package heimdall
import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"net"
"net/http"
"sync"
"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) {
t.Parallel()

View 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))
}

View file

@ -39,12 +39,12 @@ func NewChainSpanner(ethAPI api.Caller, validatorSet abi.ABI, chainConfig *param
}
// 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
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
// method
method := "getCurrentSpan"
const method = "getCurrentSpan"
data, err := c.validatorSet.Pack(method)
if err != nil {
@ -58,7 +58,7 @@ func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
// 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,
To: &toAddress,
Data: &msgData,
@ -89,8 +89,8 @@ func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) {
}
// GetCurrentValidators get current validators
func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
ctx, cancel := context.WithCancel(context.Background())
func (c *ChainSpanner) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// method
@ -146,7 +146,7 @@ func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber
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
validators := make([]valset.MinimalVal, 0, len(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)
// apply message
_, err = statefull.ApplyMessage(msg, state, header, c.chainConfig, chainContext)
_, err = statefull.ApplyMessage(ctx, msg, state, header, c.chainConfig, chainContext)
return err
}

View 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
}

View 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()
}

View 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,
}
}

View 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)
}
}
}

View file

@ -39,7 +39,7 @@ func convertTo32(input []byte) (output [32]byte) {
return
}
func convert(input []([32]byte)) [][]byte {
func convert(input [][32]byte) [][]byte {
output := make([][]byte, 0, len(input))
for _, in := range input {

View file

@ -59,6 +59,8 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat
return nil, err
}
snap.ValidatorSet.UpdateValidatorMap()
snap.config = config
snap.sigcache = sigcache
@ -120,8 +122,8 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
number := header.Number.Uint64()
// Delete the oldest signer from the recent list to allow it signing again
if number >= s.config.Sprint {
delete(snap.Recents, number-s.config.Sprint)
if number >= s.config.CalculateSprint(number) {
delete(snap.Recents, number-s.config.CalculateSprint(number))
}
// 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
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
if !snap.ValidatorSet.HasAddress(signer) {
return nil, &UnauthorizedSignerError{number, signer.Bytes()}
}
@ -143,7 +145,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
snap.Recents[number] = signer
// 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 {
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
func (s *Snapshot) Difficulty(signer common.Address) uint64 {
func Difficulty(validatorSet *valset.ValidatorSet, signer common.Address) uint64 {
// if signer is empty
if signer == (common.Address{}) {
return 1
}
validators := s.ValidatorSet.Validators
proposer := s.ValidatorSet.GetProposer().Address
validators := validatorSet.Validators
proposer := validatorSet.GetProposer().Address
totalValidators := len(validators)
proposerIndex, _ := s.ValidatorSet.GetByAddress(proposer)
signerIndex, _ := s.ValidatorSet.GetByAddress(signer)
proposerIndex, _ := validatorSet.GetByAddress(proposer)
signerIndex, _ := validatorSet.GetByAddress(signer)
// temp index
tempIndex := signerIndex

View file

@ -1,12 +1,12 @@
package bor
import (
"math/rand"
"math/big"
"sort"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/maticnetwork/crand"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
"github.com/ethereum/go-ethereum/common"
@ -28,13 +28,13 @@ func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) {
}
// proposer is signer
signer := validatorSet.Proposer.Address
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
signerTest := validatorSet.Proposer.Address
successionNumber, err := snap.GetSignerSuccessionNumber(signerTest)
if err != nil {
t.Fatalf("%s", err)
}
assert.Equal(t, 0, successionNumber)
require.Equal(t, 0, successionNumber)
}
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
signer := snap.ValidatorSet.Validators[signerIndex].Address
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
signerTest := snap.ValidatorSet.Validators[signerIndex].Address
successionNumber, err := snap.GetSignerSuccessionNumber(signerTest)
if err != nil {
t.Fatalf("%s", err)
}
assert.Equal(t, signerIndex-proposerIndex, successionNumber)
require.Equal(t, signerIndex-proposerIndex, successionNumber)
}
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
signer := snap.ValidatorSet.Validators[signerIndex].Address
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
signerTest := snap.ValidatorSet.Validators[signerIndex].Address
successionNumber, err := snap.GetSignerSuccessionNumber(signerTest)
if err != nil {
t.Fatalf("%s", err)
}
assert.Equal(t, signerIndex+numVals-proposerIndex, successionNumber)
require.Equal(t, signerIndex+numVals-proposerIndex, successionNumber)
}
func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
@ -93,18 +93,20 @@ func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
ValidatorSet: valset.NewValidatorSet(validators),
}
dummyProposerAddress := randomAddress()
require.Len(t, snap.ValidatorSet.Validators, numVals)
dummyProposerAddress := randomAddress(toAddresses(validators)...)
snap.ValidatorSet.Proposer = &valset.Validator{Address: dummyProposerAddress}
// choose any signer
signer := snap.ValidatorSet.Validators[3].Address
signerTest := snap.ValidatorSet.Validators[3].Address
_, err := snap.GetSignerSuccessionNumber(signer)
assert.NotNil(t, err)
_, err := snap.GetSignerSuccessionNumber(signerTest)
require.NotNil(t, err)
e, ok := err.(*UnauthorizedProposerError)
assert.True(t, ok)
assert.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
require.True(t, ok)
require.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
}
func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
@ -114,26 +116,30 @@ func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
snap := Snapshot{
ValidatorSet: valset.NewValidatorSet(validators),
}
dummySignerAddress := randomAddress()
dummySignerAddress := randomAddress(toAddresses(validators)...)
_, err := snap.GetSignerSuccessionNumber(dummySignerAddress)
assert.NotNil(t, err)
require.NotNil(t, err)
e, ok := err.(*UnauthorizedSignerError)
assert.True(t, ok)
assert.Equal(t, dummySignerAddress.Bytes(), e.Signer)
require.True(t, ok)
require.Equal(t, dummySignerAddress.Bytes(), e.Signer)
}
// nolint: unparam
func buildRandomValidatorSet(numVals int) []*valset.Validator {
rand.Seed(time.Now().Unix())
validators := make([]*valset.Validator, numVals)
valAddrs := randomAddresses(numVals)
for i := 0; i < numVals; i++ {
power := crand.BigInt(big.NewInt(99))
powerN := power.Int64() + 1
validators[i] = &valset.Validator{
Address: valAddrs[i],
// 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
}
func randomAddress() common.Address {
bytes := make([]byte, 32)
rand.Read(bytes)
func randomAddress(exclude ...common.Address) common.Address {
excl := make(map[common.Address]struct{}, len(exclude))
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 {
@ -158,17 +176,12 @@ func randomAddresses(n int) []common.Address {
addrs := make([]common.Address, 0, n)
addrsSet := make(map[common.Address]struct{}, n)
var (
addr common.Address
exist bool
)
var exist bool
bytes := make([]byte, 32)
r := crand.NewRand()
for {
rand.Read(bytes)
addr = common.BytesToAddress(bytes)
addr := r.Address()
_, exist = addrsSet[addr]
if !exist {
@ -187,7 +200,7 @@ func TestRandomAddresses(t *testing.T) {
t.Parallel()
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)
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
}

View file

@ -1,6 +1,8 @@
package bor
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
@ -11,7 +13,7 @@ import (
//go:generate mockgen -destination=./span_mock.go -package=bor . Spanner
type Spanner interface {
GetCurrentSpan(headerHash common.Hash) (*span.Span, error)
GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
CommitSpan(heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error
GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error)
GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error
}

View file

@ -5,6 +5,7 @@
package bor
import (
context "context"
reflect "reflect"
common "github.com/ethereum/go-ethereum/common"
@ -40,45 +41,45 @@ func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder {
}
// 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()
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)
return ret0
}
// 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()
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.
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()
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0)
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0, arg1)
ret0, _ := ret[0].(*span.Span)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// 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()
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.
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()
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1)
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1, arg2)
ret0, _ := ret[0].([]*valset.Validator)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// 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()
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)
}

View file

@ -1,6 +1,7 @@
package statefull
import (
"context"
"math"
"math/big"
@ -59,6 +60,7 @@ func GetSystemMessage(toAddress common.Address, data []byte) Callmsg {
// apply message
func ApplyMessage(
_ context.Context,
msg Callmsg,
state *state.StateDB,
header *types.Header,

View file

@ -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)
}

View file

@ -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)
}

View file

@ -47,21 +47,26 @@ func (v *Validator) Cmp(other *Validator) *Validator {
return v
}
// nolint:nestif
if v.ProposerPriority > other.ProposerPriority {
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 {

View file

@ -46,6 +46,7 @@ type ValidatorSet struct {
// cached (unexported)
totalVotingPower int64
validatorsMap map[common.Address]int // address -> index
}
// 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
// function panics.
func NewValidatorSet(valz []*Validator) *ValidatorSet {
vals := &ValidatorSet{}
vals := &ValidatorSet{validatorsMap: make(map[common.Address]int)}
err := vals.updateWithChangeSet(valz, false)
if err != nil {
@ -232,30 +233,34 @@ func validatorListCopy(valsList []*Validator) []*Validator {
// Copy each validator into a new 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{
Validators: validatorListCopy(vals.Validators),
Proposer: vals.Proposer,
totalVotingPower: vals.totalVotingPower,
validatorsMap: validatorsMap,
}
}
// HasAddress returns true if address given is in the validator set, false -
// otherwise.
func (vals *ValidatorSet) HasAddress(address []byte) bool {
idx := sort.Search(len(vals.Validators), func(i int) bool {
return bytes.Compare(address, vals.Validators[i].Address.Bytes()) <= 0
})
func (vals *ValidatorSet) HasAddress(address common.Address) bool {
_, ok := vals.validatorsMap[address]
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
// itself if found. Otherwise, -1 and nil are returned.
func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *Validator) {
idx := sort.Search(len(vals.Validators), func(i int) bool {
return bytes.Compare(address.Bytes(), vals.Validators[i].Address.Bytes()) <= 0
})
if idx < len(vals.Validators) && vals.Validators[idx].Address == address {
idx, ok := vals.validatorsMap[address]
if ok {
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.
// It returns nil values if index is less than 0 or greater or equal to
// 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) {
return nil, nil
return common.Address{}, nil
}
val = vals.Validators[index]
return val.Address.Bytes(), val.Copy()
return val.Address, val.Copy()
}
// Size returns the length of the validator set.
@ -328,7 +333,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) {
func (vals *ValidatorSet) findProposer() *Validator {
var proposer *Validator
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)
}
}
@ -371,14 +376,19 @@ func processChanges(origChanges []*Validator) (updates, removals []*Validator, e
changes := validatorListCopy(origChanges)
sort.Sort(ValidatorsByAddress(changes))
removals = make([]*Validator, 0, len(changes))
updates = make([]*Validator, 0, len(changes))
sliceCap := len(changes) / 2
if sliceCap == 0 {
sliceCap = 1
}
removals = make([]*Validator, 0, sliceCap)
updates = make([]*Validator, 0, sliceCap)
var prevAddr common.Address
// Scan changes by address and append valid validators to updates or removals lists.
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)
return nil, nil, err
}
@ -489,10 +499,11 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
} else {
// Apply add or update.
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.
existing = existing[1:]
}
updates = updates[1:]
}
i++
@ -503,6 +514,7 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) {
merged[i] = existing[j]
i++
}
// OR add updates which are left.
for j := 0; j < len(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.
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:]
} else { // Leave it in the resulting slice.
merged[i] = existing[0]
@ -599,8 +611,7 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
computeNewPriorities(updates, vals, updatedTotalVotingPower)
// Apply updates and removals.
vals.applyUpdates(updates)
vals.applyRemovals(deletes)
vals.updateValidators(updates, deletes)
if err := vals.UpdateTotalVotingPower(); err != nil {
return err
@ -613,6 +624,21 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
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'.
// It performs the following steps:
// - 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"
}
var valStrings []string
valStrings := make([]string, 0, len(vals.Validators))
vals.Iterate(func(index int, val *Validator) bool {
valStrings = append(valStrings, val.String())

View 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)
}

View file

@ -19,6 +19,7 @@ package clique
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@ -569,7 +570,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// 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
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
// 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()
// Sealing the genesis block is not supported

View file

@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) {
genesis := genspec.MustCommit(db)
// 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()
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()
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()
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
// flushing the dirty states out. Insert the last block, triggering a sidechain
// 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()
if _, err := chain.InsertChain(blocks[2:]); err != nil {

View file

@ -450,7 +450,7 @@ func TestClique(t *testing.T) {
batches[len(batches)-1] = append(batches[len(batches)-1], block)
}
// 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 {
t.Errorf("test %d: failed to create test chain: %v", i, err)
continue

View file

@ -18,6 +18,7 @@
package consensus
import (
"context"
"math/big"
"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
// 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)
// 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
// 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(header *types.Header) common.Hash

View file

@ -18,6 +18,7 @@ package ethash
import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
@ -598,7 +599,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// 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
ethash.Finalize(chain, header, state, txs, uncles)

View file

@ -17,6 +17,7 @@
package ethash
import (
"context"
"io/ioutil"
"math/big"
"math/rand"
@ -38,7 +39,7 @@ func TestTestMode(t *testing.T) {
defer ethash.Close()
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 {
t.Fatalf("failed to seal block: %v", err)
}
@ -111,12 +112,13 @@ func TestRemoteSealer(t *testing.T) {
// Push new work.
results := make(chan *types.Block)
ethash.Seal(nil, block, results, nil)
err := ethash.Seal(context.Background(), nil, block, results, nil)
var (
work [4]string
err error
)
if err != nil {
t.Error("error in sealing block")
}
var work [4]string
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
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)}
block = types.NewBlockWithHeader(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() {
t.Error("expect to return the latest pushed work")

View file

@ -48,7 +48,7 @@ var (
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
// 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 ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
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 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
abort := make(chan struct{})
@ -117,7 +117,8 @@ func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block
case <-ethash.update:
// Thread count was changed on user request, restart
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)
}
}

View file

@ -17,6 +17,7 @@
package ethash
import (
"context"
"encoding/json"
"io/ioutil"
"math/big"
@ -57,7 +58,11 @@ func TestRemoteNotify(t *testing.T) {
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
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 {
case work := <-sink:
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)}
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 {
case work := <-sink:
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++ {
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
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++ {
@ -180,6 +193,9 @@ func TestRemoteMultiNotifyFull(t *testing.T) {
}
sink <- work
}))
// Allowing the server to start listening.
time.Sleep(2 * time.Second)
defer server.Close()
// Create the custom ethash engine.
@ -200,7 +216,11 @@ func TestRemoteMultiNotifyFull(t *testing.T) {
for i := 0; i < cap(sink); i++ {
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
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++ {
@ -266,7 +286,11 @@ func TestStaleSubmission(t *testing.T) {
for id, c := range testcases {
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 {
t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)

View file

@ -59,9 +59,10 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
}
var (
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator)
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
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 parent.GasUsed == parentGasTarget {

View file

@ -20,7 +20,6 @@ import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
@ -47,12 +46,14 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig {
TerminalTotalDifficulty: original.TerminalTotalDifficulty,
Ethash: original.Ethash,
Clique: original.Clique,
Bor: original.Bor,
}
}
func config() *params.ChainConfig {
config := copyConfig(params.TestChainConfig)
config.LondonBlock = big.NewInt(5)
config.Bor.DelhiBlock = big.NewInt(8)
return config
}
@ -108,6 +109,8 @@ func TestBlockGasLimits(t *testing.T) {
// TestCalcBaseFee assumes all blocks are 1559-blocks
func TestCalcBaseFee(t *testing.T) {
t.Parallel()
tests := []struct {
parentBaseFee int64
parentGasLimit uint64
@ -117,10 +120,12 @@ func TestCalcBaseFee(t *testing.T) {
{params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
{params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below 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 {
parent := &types.Header{
Number: common.Big32,
Number: big.NewInt(6),
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
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)
}
}
}

View file

@ -162,7 +162,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
// genUncles generates blocks with two uncle headers.
func genUncles(i int, gen *BlockGen) {
if i >= 6 {
if i >= 7 {
b2 := gen.PrevBlock(i - 6).Header()
b2.Extra = []byte("foo")
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.
// 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()
b.ReportAllocs()
b.ResetTimer()
@ -317,7 +317,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
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 {
b.Fatalf("error creating chain: %v", err)
}

View file

@ -49,7 +49,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header()
}
// 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()
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))
}
// 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()
// Verify the blocks before the merging
@ -279,11 +279,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error
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)
chain.Stop()
} 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)
chain.Stop()
}
@ -346,7 +346,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old)
// 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()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)

View file

@ -29,6 +29,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"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/types"
"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/event"
"github.com/ethereum/go-ethereum/internal/syncx"
@ -90,7 +92,6 @@ const (
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
TriesInMemory = 128
// 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
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
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
}
@ -142,6 +144,7 @@ var DefaultCacheConfig = &CacheConfig{
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
TriesInMemory: 128,
}
// 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
// available in the database. It initialises the default Ethereum Validator
// 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 {
cacheConfig = DefaultCacheConfig
}
if cacheConfig.TriesInMemory <= 0 {
cacheConfig.TriesInMemory = DefaultCacheConfig.TriesInMemory
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
receiptsCache, _ := lru.New(receiptsCacheLimit)
@ -257,7 +266,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
borReceiptsCache: borReceiptsCache,
}
bc.forker = NewForkChoice(bc, shouldPreserve)
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
bc.validator = NewBlockValidator(chainConfig, bc, engine)
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
bc.processor = NewStateProcessor(chainConfig, bc, engine)
@ -825,7 +834,7 @@ func (bc *BlockChain) Stop() {
if !bc.cacheConfig.TrieDirtyDisabled {
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 {
recent := bc.GetBlockByNumber(number - offset)
@ -908,6 +917,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
ancientBlocks, liveBlocks types.Blocks
ancientReceipts, liveReceipts []types.Receipts
)
// Do a sanity check that the provided chain is actually ordered and linked
for i := 0; i < len(blockChain); i++ {
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
// 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() {
return false
}
@ -948,6 +958,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
} else if !reorg {
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())
bc.currentFastBlock.Store(head)
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])
}
// 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{}
var headers []*types.Header
for _, block := range blockChain {
borReceipts = append(borReceipts, []*types.Receipt{bc.GetBorReceiptByHash(block.Hash())})
headers = append(headers, block.Header())
}
// 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.
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
// don't match the canonical chain.
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) {
skipPresenceCheck := false
batch := bc.db.NewBatch()
headers := make([]*types.Header, 0, len(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
if bc.insertStopped() {
return 0, errInsertionInterrupted
@ -1122,7 +1148,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return 0, err
}
}
updateHead(blockChain[len(blockChain)-1])
updateHead(blockChain[len(blockChain)-1], headers)
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
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
var (
nodes, imgs = triedb.Size()
@ -1285,7 +1312,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Cap(limit - ethdb.IdealBatchSize)
}
// 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 bc.gcproc > bc.cacheConfig.TrieTimeLimit {
@ -1297,8 +1324,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
} else {
// If we're exceeding limits but haven't reached a large enough memory gap,
// warn the user that the system is becoming unstable.
if chosen < lastWrite+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)
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.TriesInMemory, "optimum", float64(chosen-lastWrite)/float64((bc.cacheConfig.TriesInMemory)))
}
// Flush an entire trie and restart the counters
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)
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
if bc.skipBlock(err, it) {
// First block (and state) is known
@ -1833,6 +1872,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
externTd *big.Int
lastBlock = block
current = bc.CurrentBlock()
headers []*types.Header
)
// The first sidechain block error is already verified to be ErrPrunedAncestor.
// 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.
err := consensus.ErrPrunedAncestor
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
if number := block.NumberU64(); current.NumberU64() >= number {
canonical := bc.GetBlockByNumber(number)
@ -1895,7 +1936,13 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
if err != nil {
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())
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
return it.index, err

View file

@ -27,7 +27,7 @@ func TestChain2HeadEvent(t *testing.T) {
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()
chain2HeadCh := make(chan Chain2HeadEvent, 64)

View file

@ -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
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
if n == 0 {
return db, blockchain, nil
@ -655,7 +655,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
blockchain.Stop()
// 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 {
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
archiveDb := rawdb.NewMemoryDatabase()
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()
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
fastDb := rawdb.NewMemoryDatabase()
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()
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)
}
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()
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
@ -923,7 +923,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveCaching := *DefaultCacheConfig
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 {
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
fastDb, delfn := makeDb()
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()
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
ancientDb, delfn := makeDb()
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()
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
lightDb, delfn := makeDb()
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 {
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.
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 {
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)
)
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()
rmLogsCh := make(chan RemovedLogsEvent)
@ -1167,7 +1170,7 @@ func TestLogRebirth(t *testing.T) {
genesis = gspec.MustCommit(db)
signer = types.LatestSigner(gspec.Config)
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()
@ -1230,7 +1233,7 @@ func TestSideLogRebirth(t *testing.T) {
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
genesis = gspec.MustCommit(db)
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()
@ -1305,7 +1308,7 @@ func TestReorgSideEvent(t *testing.T) {
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()
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)
)
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()
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)
)
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()
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()
(&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 {
t.Fatalf("failed to create tester chain: %v", err)
}
@ -1648,7 +1652,7 @@ func TestTrieForkGC(t *testing.T) {
db := rawdb.NewMemoryDatabase()
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
forks := make([]*types.Block, len(blocks))
@ -1664,7 +1668,7 @@ func TestTrieForkGC(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase()
(&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 {
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
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(forks[len(blocks)-1-i].Root())
}
@ -1696,14 +1700,14 @@ func TestLargeReorgTrieGC(t *testing.T) {
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}) })
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*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}) })
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*int(DefaultCacheConfig.TriesInMemory)+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
// Import the shared chain and the original canonical one
diskdb := rawdb.NewMemoryDatabase()
(&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 {
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 {
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 {
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)
}
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))
for i, block := range blocks {
@ -1784,7 +1789,7 @@ func TestBlockchainRecovery(t *testing.T) {
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
// 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()
if num := ancient.CurrentBlock().NumberU64(); 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.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()
// 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.
//
// Details at:
// - https://github.com/ethereum/go-ethereum/issues/18977
// - https://github.com/ethereum/go-ethereum/pull/18988
// - https://github.com/ethereum/go-ethereum/issues/18977
// - https://github.com/ethereum/go-ethereum/pull/18988
func TestLowDiffLongChain(t *testing.T) {
// Generate a canonical chain to act as the main dataset
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
// 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.OffsetTime(-9)
})
@ -1897,7 +1902,7 @@ func TestLowDiffLongChain(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase()
(&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 {
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
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})
})
@ -1963,7 +1968,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Generate and import the canonical chain
diskdb := rawdb.NewMemoryDatabase()
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 {
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
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)
if err != nil {
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)
}
lastPrunedIndex := len(blocks) - TriesInMemory - 1
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
// Verify pruning of lastPrunedBlock
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
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
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})
})
// 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.
// So the blocks are
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
// ^ ^ ^ pruned
//
// ^ ^ ^ pruned
func TestPrunedImportSide(t *testing.T) {
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
//glogger.Verbosity(3)
@ -2102,7 +2109,7 @@ func testInsertKnownChainData(t *testing.T, typ string) {
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
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 {
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)
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 {
t.Fatalf("failed to create tester chain: %v", err)
}
@ -2384,7 +2391,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha
diskdb := rawdb.NewMemoryDatabase()
(&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 {
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
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 {
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)
}
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 {
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 */}
tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0}
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 {
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.
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 {
t.Fatalf("failed to create tester chain: %v", err)
}
@ -2768,7 +2775,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
diskdb := rawdb.NewMemoryDatabase()
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 {
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
// ErrPrunedAncestor error.
// This may e.g. happen if
// 1. Downloader rollbacks a batch of inserted blocks and exits
// 2. Downloader starts to sync again
// 3. The blocks fetched are all known and canonical blocks
// 1. Downloader rollbacks a batch of inserted blocks and exits
// 2. Downloader starts to sync again
// 3. The blocks fetched are all known and canonical blocks
func TestSideImportPrunedBlocks(t *testing.T) {
// Generate a canonical chain to act as the main dataset
engine := ethash.NewFaker()
@ -2847,11 +2854,11 @@ func TestSideImportPrunedBlocks(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
// 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()
(&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 {
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)
}
lastPrunedIndex := len(blocks) - TriesInMemory - 1
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex]
// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), 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
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
@ -2945,7 +2953,7 @@ func TestDeleteCreateRevert(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase()
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 {
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{
Debug: true,
Tracer: logger.NewJSONLogger(nil, os.Stdout),
}, nil, nil)
}, nil, nil, nil)
if err != nil {
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{
Debug: true,
Tracer: logger.NewJSONLogger(nil, os.Stdout),
}, nil, nil)
}, nil, nil, nil)
if err != nil {
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{
//Debug: true,
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
}, nil, nil)
}, nil, nil, nil)
if err != nil {
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
// on mainnet over blocks 7338108, 7338110 and 7338115.
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
// with 0.001 ether (thus created but no code)
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
// deployment fails due to OOG during initcode execution
// - Block 7338115: another tx checks the balance of
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
// zero.
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
// with 0.001 ether (thus created but no code)
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
// deployment fails due to OOG during initcode execution
// - Block 7338115: another tx checks the balance of
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
// zero.
//
// The problem being that the snapshotter maintains a destructset, and adds items
// 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
// in the first place.
//
func TestInitThenFailCreateContract(t *testing.T) {
var (
// 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{
//Debug: true,
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
}, nil, nil)
}, nil, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
@ -3533,7 +3540,7 @@ func TestEIP2718Transition(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase()
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 {
t.Fatalf("failed to create tester chain: %v", err)
}
@ -3554,13 +3561,13 @@ func TestEIP2718Transition(t *testing.T) {
// TestEIP1559Transition tests the following:
//
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
// 3. Only the transaction's tip will be received by the coinbase.
// 4. The transaction sender pays for both the tip and baseFee.
// 5. The coinbase receives only the partially realized tip when
// gasFeeCap - gasTipCap < baseFee.
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
// 3. Only the transaction's tip will be received by the coinbase.
// 4. The transaction sender pays for both the tip and baseFee.
// 5. The coinbase receives only the partially realized tip when
// gasFeeCap - gasTipCap < baseFee.
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
func TestEIP1559Transition(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
@ -3628,7 +3635,7 @@ func TestEIP1559Transition(t *testing.T) {
diskdb := rawdb.NewMemoryDatabase()
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 {
t.Fatalf("failed to create tester chain: %v", err)
}

View file

@ -17,6 +17,7 @@
package core
import (
"context"
"fmt"
"math/big"
@ -258,7 +259,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
if b.engine != nil {
// 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
root, err := statedb.Commit(config.IsEIP158(b.header.Number))

View file

@ -79,7 +79,7 @@ func ExampleGenerateChain() {
})
// 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()
if i, err := blockchain.InsertChain(chain); err != nil {

View file

@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock
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()
conDb := rawdb.NewMemoryDatabase()
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock
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()
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
db = rawdb.NewMemoryDatabase()
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()
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
db = rawdb.NewMemoryDatabase()
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()
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
db = rawdb.NewMemoryDatabase()
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()
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
db = rawdb.NewMemoryDatabase()
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()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))

View file

@ -17,15 +17,14 @@
package core
import (
crand "crypto/rand"
"errors"
"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/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
@ -47,25 +46,29 @@ type ChainReader interface {
// for all other proof-of-work networks.
type ForkChoice struct {
chain ChainReader
rand *mrand.Rand
rand Floater
// preserve is a helper function used in td fork choice.
// 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
// client
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, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
log.Crit("Failed to initialize random seed", "err", err)
}
r := crand.NewRand()
return &ForkChoice{
chain: chainReader,
rand: mrand.New(mrand.NewSource(seed.Int64())),
preserve: preserve,
chain: chainReader,
rand: r,
preserve: preserve,
validator: validator,
}
}
@ -106,3 +109,15 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, header *types.Header) (b
}
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
View 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 &params.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
}

View file

@ -117,7 +117,7 @@ func TestSetupGenesis(t *testing.T) {
// Advance to block #4, past the homestead transition block of customg.
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()
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)

View file

@ -283,8 +283,10 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
lastHeader: lastHeader,
}
)
// 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
} else if !reorg {
if inserted != 0 {
@ -292,6 +294,16 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F
}
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
// header chain, skip the reorg operation.
if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {

View file

@ -84,7 +84,7 @@ func TestHeaderInsertion(t *testing.T) {
chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10)
log.Root().SetHandler(log.StdoutHandler)
forker := NewForkChoice(hc, nil)
forker := NewForkChoice(hc, nil, nil)
// Inserting 64 headers on an empty chain, expecting
// 1 callbacks, 1 canon-status, 0 sidestatus,
testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)

View file

@ -18,7 +18,11 @@ var (
getDerivedBorTxHash = types.GetDerivedBorTxHash
// 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 = "matic-bor-receipts"

View file

@ -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
// a contract does the following:
//
// 1. sends funds to sha(account ++ (nonce + 1))
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
// 1. sends funds to sha(account ++ (nonce + 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.
func (s *StateDB) CreateAccount(addr common.Address) {

View file

@ -94,7 +94,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
}
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()
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
@ -235,7 +235,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
}
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()
for i, tt := range []struct {
@ -275,7 +275,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
}
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()
for i, tt := range []struct {

View file

@ -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
4) Value transfer
== 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 ==
5) Run Script section
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
// returning the evm execution result with following fields.
//
// - used gas:
// total gas used (including gas being refunded)
// - returndata:
// the returned data from evm
// - concrete execution error:
// various **EVM** error which aborts the execution,
// e.g. ErrOutOfGas, ErrExecutionReverted
// - used gas:
// total gas used (including gas being refunded)
// - returndata:
// the returned data from evm
// - concrete execution error:
// various **EVM** error which aborts the execution,
// e.g. ErrOutOfGas, ErrExecutionReverted
//
// However if any consensus issue encountered, return the error directly with
// nil evm execution result.

View file

@ -29,7 +29,6 @@ import (
"github.com/golang/mock/gomock"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor"
"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
chainConfig := params.BorUnittestChainConfig
ctrl := gomock.NewController(t)
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()
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,
Address: common.Address{0x1},
Address: miner.TestBankAddress,
VotingPower: 100,
ProposerPriority: 0,
},
@ -1809,44 +1810,15 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
contractMock := bor.NewMockGenesisContract(ctrl)
// Initialize a fresh chain
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)
engine := miner.NewFakeBor(t, db, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
defer engine.Close()
engineBorInternal, ok := engine.(*bor.Bor)
if ok {
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
copy(gspec.ExtraData[32:32+common.AddressLength], testAddress1.Bytes())
chainConfig.LondonBlock = big.NewInt(0)
engineBorInternal.Authorize(testAddress1, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), testKey1)
})
}
_, back, closeFn := miner.NewTestWorker(t, chainConfig, engine, db, 0)
defer closeFn()
genesis := gspec.MustCommit(db)
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)
}
genesis := back.BlockChain().Genesis()
// If sidechain blocks are needed, make a light chain and import it
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) {
b.SetCoinbase(testAddress1)
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
b.SetExtra(gspec.ExtraData)
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.CalculateSprint(b.Number().Uint64())) {
b.SetExtra(back.Genesis.ExtraData)
} else {
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)
}
}
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))
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
b.SetExtra(gspec.ExtraData)
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.CalculateSprint(b.Number().Uint64())) {
b.SetExtra(back.Genesis.ExtraData)
} else {
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)
}
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 {
t.Fatal("on trieDB.Commit", err)
}
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)
}
}
}
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)
}
// Force run a freeze cycle
type freezer interface {
Freeze(threshold uint64) error
Ancients() (uint64, error)
}
db.(freezer).Freeze(tt.freezeThreshold)
// Set the simulated pivot block
if tt.pivotBlock != nil {
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
}
// Pull the plug on the database, simulating a hard crash
db.Close()
@ -1912,12 +1888,14 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
if err != nil {
t.Fatalf("Failed to reopen persistent database: %v", err)
}
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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
defer newChain.Stop()
// 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 {
t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader)
}
if head := newChain.CurrentFastBlock(); 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 {
t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
}
if frozen, err := db.(freezer).Ancients(); err != nil {
t.Errorf("Failed to retrieve ancient count: %v\n", err)
} 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 {
t.Fatalf("Failed to create chain: %v", err)
}
@ -2043,7 +2024,7 @@ func TestIssue23496(t *testing.T) {
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 {
t.Fatalf("Failed to recreate chain: %v", err)
}

View file

@ -1987,7 +1987,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
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 {
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
// the database and errors if found.
//
//nolint:gocognit
func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks) {
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
// the specified limit, but that it is available before.
//
//nolint:gocognit
func verifyCutoff(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks, head int) {
t.Helper()

View file

@ -84,7 +84,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*core.BlockChain, []*type
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 {
t.Fatalf("Failed to create chain: %v", err)
}
@ -246,7 +246,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
// Restart the chain normally
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 {
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
// after the normal stop. It's used to ensure the broken snapshot
// 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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -327,7 +327,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -336,7 +336,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
newchain.Stop()
// 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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -365,7 +365,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
chain.SetHead(snaptest.setHead)
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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -396,7 +396,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
// and state committed.
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 {
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
// 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 {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -449,8 +449,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0,
}
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
newchain, err := core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
@ -467,14 +466,13 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
SnapshotLimit: 256,
SnapshotWait: false, // Don't wait rebuild
}
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
_, err = core.NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, nil)
if err != nil {
t.Fatalf("Failed to recreate chain: %v", err)
}
// 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 {
t.Fatalf("Failed to recreate chain: %v", err)
}

View file

@ -268,6 +268,8 @@ type TxPool struct {
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.
promoteTxCh chan struct{} // should be used only for tests
}
type txpoolResetRequest struct {
@ -276,7 +278,7 @@ type txpoolResetRequest struct {
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
// 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
config = (&config).sanitize()
@ -299,6 +301,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
initDoneCh: make(chan struct{}),
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
}
pool.locals = newAccountSet(pool.signer)
for _, addr := range config.Locals {
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.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.
pool.wg.Add(1)
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!
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
if pool.pending[addr] == nil {
pool.pending[addr] = newTxList(true)
@ -1080,6 +1099,7 @@ func (pool *TxPool) scheduleReorgLoop() {
dirtyAccounts *accountSet
queuedEvents = make(map[common.Address]*txSortedMap)
)
for {
// Launch next background reorg if needed
if curDone == nil && launchNextRun {

View file

@ -17,6 +17,7 @@
package core
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
@ -24,10 +25,17 @@ import (
"math/big"
"math/rand"
"os"
"runtime"
"strings"
"sync"
"sync/atomic"
"testing"
"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/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
@ -47,6 +55,10 @@ var (
eip1559Config *params.ChainConfig
)
const (
txPoolGasLimit = 10_000_000
)
func init() {
testTxPoolConfig = DefaultTxPoolConfig
testTxPoolConfig.Journal = ""
@ -114,15 +126,17 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int,
}
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)
blockchain := &testBlockChain{10000000, statedb, new(event.Feed)}
blockchain := &testBlockChain{gasLimit, statedb, new(event.Feed)}
key, _ := crypto.GenerateKey()
pool := NewTxPool(testTxPoolConfig, config, blockchain)
pool := NewTxPool(txPoolConfig, config, blockchain, options...)
// wait for the pool to initialize
<-pool.initDoneCh
@ -273,6 +287,16 @@ func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) {
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) {
t.Parallel()
@ -384,7 +408,7 @@ func TestTransactionNegativeValue(t *testing.T) {
func TestTransactionTipAboveFeeCap(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(eip1559Config)
pool, key := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
defer pool.Stop()
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) {
t.Parallel()
pool, key := setupTxPoolWithConfig(eip1559Config)
pool, key := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
defer pool.Stop()
veryBigNumber := big.NewInt(1)
@ -1449,7 +1473,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
pool, _ := setupTxPoolWithConfig(eip1559Config)
pool, _ := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
defer pool.Stop()
// 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) {
t.Parallel()
pool, _ := setupTxPoolWithConfig(eip1559Config)
pool, _ := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
defer pool.Stop()
pool.config.GlobalSlots = 2
@ -1927,7 +1951,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
func TestDualHeapEviction(t *testing.T) {
t.Parallel()
pool, _ := setupTxPoolWithConfig(eip1559Config)
pool, _ := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
defer pool.Stop()
pool.config.GlobalSlots = 10
@ -2130,7 +2154,7 @@ func TestTransactionReplacementDynamicFee(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
pool, key := setupTxPoolWithConfig(eip1559Config)
pool, key := setupTxPoolWithConfig(eip1559Config, testTxPoolConfig, txPoolGasLimit)
defer pool.Stop()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
@ -2561,3 +2585,516 @@ func BenchmarkPoolMultiAccountBatchInsert(b *testing.B) {
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
}
}

View file

@ -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
heads := make(TxByPriceAndTime, 0, len(txs))
for from, accTxs := range txs {
if len(accTxs) == 0 {
continue
}
acc, _ := Sender(signer, accTxs[0])
wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee)
// Remove transaction if sender doesn't match from, or if wrapping fails.
@ -550,6 +554,10 @@ func (t *TransactionsByPriceAndNonce) Shift() {
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
// 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.

View file

@ -343,7 +343,8 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
} else {
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()}
}
results = append(results, &BadBlockArgs{

View file

@ -18,6 +18,7 @@
package eth
import (
"context"
"errors"
"fmt"
"math/big"
@ -40,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"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/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
@ -216,9 +218,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
TrieTimeLimit: config.TrieTimeout,
SnapshotLimit: config.SnapshotCache,
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 {
return nil, err
}
@ -259,6 +265,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Checkpoint: checkpoint,
EthAPI: ethAPI,
PeerRequiredBlocks: config.PeerRequiredBlocks,
checker: checker,
}); err != nil {
return nil, err
}
@ -376,6 +383,10 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
s.blockchain.ResetWithGenesisBlock(gb)
}
func (s *Ethereum) PublicBlockChainAPI() *ethapi.PublicBlockChainAPI {
return s.handler.ethAPI
}
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
s.lock.RLock()
etherbase := s.etherbase
@ -621,6 +632,13 @@ func (s *Ethereum) Start() error {
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
// checkpoint whitelist map.
func (s *Ethereum) startCheckpointWhitelistService() {
@ -632,7 +650,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
}
// 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 errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
return
@ -647,7 +669,11 @@ func (s *Ethereum) startCheckpointWhitelistService() {
for {
select {
case <-ticker.C:
err := s.handleWhitelistCheckpoint()
ctx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err := s.handleWhitelistCheckpoint(ctx, false)
cancel()
if err != nil {
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.
func (s *Ethereum) handleWhitelistCheckpoint() error {
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, first bool) error {
ethHandler := (*ethHandler)(s.handler)
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
@ -675,13 +696,20 @@ func (s *Ethereum) handleWhitelistCheckpoint() error {
return ErrBorConsensusWithoutHeimdall
}
endBlockNum, endBlockHash, err := ethHandler.fetchWhitelistCheckpoint(bor)
if err != nil {
// Create a new checkpoint verifier
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
}
// 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
}

View 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}
}

View file

@ -144,7 +144,7 @@ type Downloader struct {
quitCh chan struct{} // Quit channel to signal termination
quitLock sync.Mutex // Lock to prevent double closes
ChainValidator
ethereum.ChainValidator
// Testing hooks
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)
}
// 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.
type LightChain interface {
// 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.
//nolint: staticcheck
func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func(), whitelistService ChainValidator) *Downloader {
// nolint: staticcheck
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 {
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
// common ancestor.
// It returns parameters to be used for peer.RequestHeadersByNumber:
// from - starting block number
// count - number of headers to request
// skip - number of headers to skip
//
// from - starting block number
// 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,
// given the (from,count,skip)
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
// 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) {
// Check the validity of chain to be downloaded
if _, err := d.IsValidChain(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
return 0, err
// Check the validity of peer from which the chain is to be downloaded
if d.ChainValidator != nil {
if _, err := d.IsValidPeer(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
return 0, err
}
}
// Figure out the valid ancestor range to prevent rewrite attacks

View file

@ -71,7 +71,7 @@ func newTester() *downloadTester {
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 {
panic(err)
}
@ -88,7 +88,7 @@ func newTester() *downloadTester {
return tester
}
func (dl *downloadTester) setWhitelist(w ChainValidator) {
func (dl *downloadTester) setWhitelist(w ethereum.ChainValidator) {
dl.downloader.ChainValidator = w
}
@ -1416,9 +1416,9 @@ func newWhitelistFake(validate func(count int) (bool, error)) *whitelistFake {
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.
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() {
w.count++
}()
@ -1426,13 +1426,18 @@ func (w *whitelistFake) IsValidChain(_ *types.Header, _ func(number uint64, amou
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) GetCheckpointWhitelist() map[uint64]common.Hash {
return nil
}
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
// checkpoint mismatch with opposite peer, the sync should fail.

View file

@ -214,7 +214,7 @@ func newTestBlockchain(blocks []*types.Block) *core.BlockChain {
db := rawdb.NewMemoryDatabase()
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 {
panic(err)
}

View file

@ -15,7 +15,8 @@ type Service struct {
m sync.Mutex
checkpointWhitelist map[uint64]common.Hash // Checkpoint whitelist, 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 {
@ -23,6 +24,7 @@ func NewService(maxCapacity uint) *Service {
checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{},
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")
)
// 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.
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're storing in `checkpointWhitelist` with the peer's block.
//
@ -70,6 +72,84 @@ func (w *Service) IsValidChain(remoteHeader *types.Header, fetchHeadersByNumber
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) {
w.m.Lock()
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]])
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
}
}

View file

@ -2,21 +2,26 @@ package whitelist
import (
"errors"
"fmt"
"math/big"
"reflect"
"sort"
"testing"
"time"
"gotest.tools/assert"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// NewMockService creates a new mock whitelist service
func NewMockService(maxCapacity uint) *Service {
func NewMockService(maxCapacity uint, checkpointInterval uint64) *Service {
return &Service{
checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{},
maxCapacity: maxCapacity,
checkpointInterval: checkpointInterval,
}
}
@ -24,34 +29,34 @@ func NewMockService(maxCapacity uint) *Service {
func TestWhitelistCheckpoint(t *testing.T) {
t.Parallel()
s := NewMockService(10)
s := NewMockService(10, 10)
for i := 0; i < 10; i++ {
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.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
func TestIsValidChain(t *testing.T) {
func TestIsValidPeer(t *testing.T) {
t.Parallel()
s := NewMockService(10)
s := NewMockService(10, 10)
// case1: no checkpoint whitelist, should consider the chain as valid
res, err := s.IsValidChain(nil, nil)
assert.NilError(t, err, "expected no error")
assert.Equal(t, res, true, "expected chain to be valid")
res, err := s.IsValidPeer(nil, nil)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// add checkpoint entries and mock fetchHeadersByNumber function
s.ProcessCheckpoint(uint64(0), 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
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
// and throw `ErrNoRemoteCheckoint` error
res, err = s.IsValidChain(nil, falseFetchHeadersByNumber)
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
if err == nil {
t.Fatal("expected error, got nil")
}
@ -69,7 +74,7 @@ func TestIsValidChain(t *testing.T) {
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
// create a mock function, returning a the required header
@ -91,17 +96,316 @@ func TestIsValidChain(t *testing.T) {
}
}
res, err = s.IsValidChain(nil, fetchHeadersByNumber)
assert.NilError(t, err, "expected no error")
assert.Equal(t, res, true, "expected chain to be valid")
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// add one more checkpoint whitelist entry
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
// for block number 2. Should consider the chain as invalid and throw an error
res, err = s.IsValidChain(nil, fetchHeadersByNumber)
assert.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
assert.Equal(t, res, false, "expected chain to be invalid")
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
require.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
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{}{}
}

View file

@ -30,8 +30,9 @@ import (
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/bor"
"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/heimdallgrpc"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
@ -175,6 +176,7 @@ type Config struct {
TrieTimeout time.Duration
SnapshotCache int
Preimages bool
TriesInMemory uint64
// Mining options
Miner miner.Config
@ -216,6 +218,9 @@ type Config struct {
// No heimdall service
WithoutHeimdall bool
// Address to connect to Heimdall gRPC server
HeimdallgRPCAddress string
// Bor logs flag
BorLogs bool
@ -246,7 +251,14 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, et
if ethConfig.WithoutHeimdall {
return bor.New(chainConfig, db, blockchainAPI, spanner, nil, genesisContractsClient)
} 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 {
switch config.PowMode {

257
eth/filters/IBackend.go Normal file
View 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
View 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)
}

View file

@ -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")
}
// get sprint from bor config
sprint := api.chainConfig.Bor.Sprint
borConfig := api.chainConfig.Bor
var filter *Filter
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)
// Block bor filter
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 {
// 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)
// Block bor filter
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")
}
borConfig := api.chainConfig.Bor
var filter *Filter
var borLogsFilter *BorBlockLogsFilter
if f.crit.BlockHash != nil {
// Block filter requested, construct a single-shot filter
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 {
// Convert the RPC block numbers into internal representations
begin := rpc.LatestBlockNumber.Int64()
@ -433,12 +442,27 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty
}
// Construct the range filter
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
logs, err := filter.Logs(ctx)
if err != nil {
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
}

View file

@ -122,13 +122,14 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
b.Log("Running filter benchmarks...")
start = time.Now()
var backend *testBackend
var backend *TestBackend
for i := 0; i < benchFilterCnt; i++ {
if i%20 == 0 {
db.Close()
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
backend = &testBackend{db: db, sections: cnt}
backend = &TestBackend{DB: db, sections: cnt}
}
var addr common.Address
addr[0] = byte(i)
@ -173,7 +174,7 @@ func BenchmarkNoBloomBits(b *testing.B) {
b.Log("Running filter benchmarks...")
start := time.Now()
backend := &testBackend{db: db}
backend := &TestBackend{DB: db}
filter := NewRangeFilter(backend, 0, int64(*headNum), []common.Address{{}}, nil)
filter.Logs(context.Background())
d := time.Since(start)

View file

@ -23,12 +23,12 @@ func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCrit
}
// get sprint from bor config
sprint := api.chainConfig.Bor.Sprint
borConfig := api.chainConfig.Bor
var filter *BorBlockLogsFilter
if crit.BlockHash != nil {
// 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 {
// Convert the RPC block numbers into internal representations
begin := rpc.LatestBlockNumber.Int64()
@ -40,7 +40,7 @@ func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCrit
end = crit.ToBlock.Int64()
}
// 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
@ -59,8 +59,9 @@ func (api *PublicFilterAPI) NewDeposits(ctx context.Context, crit ethereum.State
}
rpcSub := notifier.CreateSubscription()
go func() {
stateSyncData := make(chan *types.StateSyncData)
stateSyncData := make(chan *types.StateSyncData, 10)
stateSyncSub := api.events.SubscribeNewDeposits(stateSyncData)
for {

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