Merge branch 'develop' into shivam/POS-1733

This commit is contained in:
Shivam Sharma 2023-10-05 12:14:31 +05:30
commit 2b1bd0997b
152 changed files with 7781 additions and 1058 deletions

View file

@ -6,7 +6,7 @@ labels: 'type:bug'
assignees: ''
---
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://wiki.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
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://wiki.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/0xPolygonDevs) server
<!--
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).

View file

@ -8,4 +8,4 @@ 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 join our [discord](https://discord.com/invite/zdwkdvMNY2) server.
For general questions please join our [discord](https://discord.com/invite/0xPolygonDevs) server.

View file

@ -15,7 +15,39 @@ concurrency:
cancel-in-progress: true
jobs:
tests:
build:
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
strategy:
matrix:
os: [ ubuntu-20.04 ] # list of os: https://github.com/actions/virtual-environments
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- run: |
git submodule update --init --recursive --force
git fetch --no-tags --prune --depth=1 origin +refs/heads/master:refs/remotes/origin/master
- uses: actions/setup-go@v3
with:
go-version: 1.20.x
- name: Install dependencies on Linux
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install build-essential
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: Build
run: make all
lint:
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
strategy:
matrix:
@ -39,6 +71,30 @@ jobs:
if: runner.os == 'Linux'
run: make lintci-deps
- name: Lint
if: runner.os == 'Linux'
run: make lint
unit-tests:
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
strategy:
matrix:
os: [ ubuntu-20.04 ] # list of os: https://github.com/actions/virtual-environments
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- run: |
git submodule update --init --recursive --force
git fetch --no-tags --prune --depth=1 origin +refs/heads/master:refs/remotes/origin/master
- uses: actions/setup-go@v3
with:
go-version: 1.20.x
- name: Install dependencies on Linux
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install build-essential
- uses: actions/cache@v3
with:
path: |
@ -48,27 +104,12 @@ jobs:
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: Build
run: make all
- name: Lint
if: runner.os == 'Linux'
run: make lint
- name: Test
run: make test
#- name: Data race tests
# run: make test-race
- name: test-integration
run: make test-integration
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./cover.out
# # TODO: make it work
# - name: Reproducible build test
# run: |
@ -81,6 +122,50 @@ jobs:
# fi
integration-tests:
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
strategy:
matrix:
os: [ ubuntu-20.04 ] # list of os: https://github.com/actions/virtual-environments
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- run: |
git submodule update --init --recursive --force
git fetch --no-tags --prune --depth=1 origin +refs/heads/master:refs/remotes/origin/master
- uses: actions/setup-go@v3
with:
go-version: 1.20.x
- name: Install dependencies on Linux
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install build-essential
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: test-integration
run: make test-integration
codecov:
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
strategy:
matrix:
os: [ ubuntu-20.04 ] # list of os: https://github.com/actions/virtual-environments
runs-on: ${{ matrix.os }}
steps:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./cover.out
e2e-tests:
if: (github.event.action != 'closed' || github.event.pull_request.merged == true)
strategy:
matrix:
@ -154,7 +239,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 -
timeout 20m bash bor/integration-tests/smoke_test.sh
timeout 60m bash bor/integration-tests/smoke_test.sh
- name: Upload logs
if: always()

View file

@ -12,9 +12,7 @@ on:
jobs:
build:
runs-on:
group: ubuntu-runners
labels: 18.04RunnerT2Large
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
@ -98,7 +96,7 @@ jobs:
NODE: bootnode
NETWORK: mumbai
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/bor.service
env:
ARCH: amd64
NODE: bootnode
@ -153,7 +151,7 @@ jobs:
NODE: bootnode
NETWORK: mainnet
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/bor.service
env:
ARCH: amd64
NODE: bootnode

View file

@ -29,3 +29,4 @@ jobs:
days-before-pr-stale: 21
days-before-issue-close: 14
days-before-pr-close: 14
exempt-draft-pr: true

View file

@ -58,7 +58,7 @@ ios:
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
test:
$(GOTEST) --timeout 5m -shuffle=on -cover -short -coverprofile=cover.out -covermode=atomic $(TESTALL)
$(GOTEST) --timeout 5m -cover -short -coverprofile=cover.out -covermode=atomic $(TESTALL)
test-txpool-race:
$(GOTEST) -run=TestPoolMiningDataRaces --timeout 600m -race -v ./core/

138
README.md
View file

@ -1,111 +1,71 @@
# 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 compatible.
Bor is the Official Golang implementation of the Polygon PoS blockchain. It is a fork of [geth](https://github.com/ethereum/go-ethereum) and is EVM compatible (upto London fork).
![Forks](https://img.shields.io/github/forks/maticnetwork/bor?style=social)
![Stars](https://img.shields.io/github/stars/maticnetwork/bor?style=social)
![Languages](https://img.shields.io/github/languages/count/maticnetwork/bor)
![Issues](https://img.shields.io/github/issues/maticnetwork/bor)
![PRs](https://img.shields.io/github/issues-pr-raw/maticnetwork/bor)
[![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://pkg.go.dev/github.com/maticnetwork/bor)
[![Go Report Card](https://goreportcard.com/badge/github.com/maticnetwork/bor)](https://goreportcard.com/report/github.com/maticnetwork/bor)
![MIT License](https://img.shields.io/github/license/maticnetwork/bor)
![contributors](https://img.shields.io/github/contributors-anon/maticnetwork/bor)
![size](https://img.shields.io/github/languages/code-size/maticnetwork/bor)
![lines](https://img.shields.io/tokei/lines/github/maticnetwork/bor)
[![Discord](https://img.shields.io/discord/714888181740339261?color=1C1CE1&label=Polygon%20%7C%20Discord%20%F0%9F%91%8B%20&style=flat-square)](https://discord.gg/zdwkdvMNY2)
[![Discord](https://img.shields.io/discord/714888181740339261?color=1C1CE1&label=Polygon%20%7C%20Discord%20%F0%9F%91%8B%20&style=flat-square)](https://discord.com/invite/0xPolygonDevs)
[![Twitter Follow](https://img.shields.io/twitter/follow/0xPolygon.svg?style=social)](https://twitter.com/0xPolygon)
## How to contribute
### Installing bor using packaging
### Contribution Guidelines
We believe one of the things that makes Polygon special is its coherent design and we seek to retain this defining characteristic. From the outset we defined some guidelines to ensure new contributions only ever enhance the project:
The easiest way to get started with bor is to install the packages using the command below. Refer to the [releases](https://github.com/maticnetwork/bor/releases) section to find the latest stable version of bor.
* Quality: Code in the Polygon project should meet the style guidelines, with sufficient test-cases, descriptive commit messages, evidence that the contribution does not break any compatibility commitments or cause adverse feature interactions, and evidence of high-quality peer-review
* Size: The Polygon projects culture is one of small pull-requests, regularly submitted. The larger a pull-request, the more likely it is that you will be asked to resubmit as a series of self-contained and individually reviewable smaller PRs
* Maintainability: If the feature will require ongoing maintenance (eg support for a particular brand of database), we may ask you to accept responsibility for maintaining this feature
### Submit an issue
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/bor.sh | bash -s -- v0.4.0 <network> <node_type>
- Create a [new issue](https://github.com/maticnetwork/bor/issues/new/choose)
- Comment on the issue (if you'd like to be assigned to it) - that way [our team can assign the issue to you](https://github.blog/2019-06-25-assign-issues-to-issue-commenters/).
- If you do not have a specific contribution in mind, you can also browse the issues labelled as `help wanted`
- Issues that additionally have the `good first issue` label are considered ideal for first-timers
The network accepts `mainnet` or `mumbai` and the node type accepts `validator` or `sentry` or `archive`. The installation script does the following things:
- Create a new user named `bor`.
- Install the bor binary at `/usr/bin/bor`.
- Dump the suitable config file (based on the network and node type provided) at `/var/lib/bor` and uses it as the home dir.
- Create a systemd service named `bor` at `/lib/systemd/system/bor.service` which starts bor using the config file as `bor` user.
### Fork the repository (repo)
The releases supports both the networks i.e. Polygon Mainnet and Mumbai (Testnet) unless explicitly specified. Before the stable release for mainnet, pre-releases will be available marked with `beta` tag for deploying on Mumbai (testnet). On sufficient testing, stable release for mainnet will be announced with a forum post.
- If you're not sure, here's how to [fork the repo](https://help.github.com/en/articles/fork-a-repo)
- If this is your first time forking our repo, this is all you need to do for this step:
### Building from source
- Install Go (version 1.19 or later) and a C compiler.
- Clone the repository and build the binary using the following commands:
```shell
make bor
```
$ git clone git@github.com:[your_github_handle]/bor
- Start bor using the ideal config files for validator and sentry provided in the `packaging` folder.
```shell
./build/bin/bor server --config ./packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml
```
- To build full set of utilities, run:
```shell
make all
```
- Run unit and integration tests
```shell
make test && make test-integration
```
- If you've already forked the repo, you'll want to ensure your fork is configured and that it's up to date. This will save you the headache of potential merge conflicts.
#### Using the new cli
- To [configure your fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork):
Post `v0.3.0` release, bor uses a new command line interface (cli). The new-cli (located at `internal/cli`) has been built with keeping the flag usage similar to old-cli (located at `cmd/geth`) with a few notable changes. Please refer to [docs](./docs) section for flag usage guide and example.
```
$ git remote add upstream https://github.com/maticnetwork/bor
```
### Documentation
- To [sync your fork with the latest changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork):
- The official documentation for the Polygon PoS chain can be found [here](https://wiki.polygon.technology/docs/pos/getting-started/). It contains all the conceptual and architectural details of the chain along with operational guide for users running the nodes.
- New release announcements and discussions can be found on our [forum page](https://forum.polygon.technology/).
- Polygon improvement proposals can be found [here](https://github.com/maticnetwork/Polygon-Improvement-Proposals/)
```
$ git checkout master
$ git fetch upstream
$ git merge upstream/master
```
### Contribution guidelines
### Building the source
Thank you for considering helping out with the source code! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! If you'd like to contribute to bor, please fork, fix, commit and send a pull request for the maintainers to review and merge into the main code base.
- Building `bor` requires both a Go (version 1.19 or later) and a C compiler. You can install
them using your favourite package manager. Once the dependencies are installed, run
```shell
$ make bor
```
### Make awesome changes!
1. Create new branch for your changes
```
$ git checkout -b new_branch_name
```
2. Commit and prepare for pull request (PR). In your PR commit message, reference the issue it resolves (see [how to link a commit message to an issue using a keyword](https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword).
Checkout our [Git-Rules](https://wiki.polygon.technology/docs/contribute/orientation/#git-rules)
```
$ git commit -m "brief description of changes [Fixes #1234]"
```
3. Push to your GitHub account
```
$ git push
```
### Submit your PR
- After your changes are committed to your GitHub fork, submit a pull request (PR) to the `master` branch of the `maticnetwork/bor` repo
- In your PR description, reference the issue it resolves (see [linking a pull request to an issue using a keyword](https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword))
- ex. `Updates out of date content [Fixes #1234]`
- Why not say hi and draw attention to your PR in [our discord server](https://discord.gg/0xpolygon)?
### Wait for review
- The team reviews every PR
- Acceptable PRs will be approved & merged into the `master` branch
<hr style="margin-top: 3em; margin-bottom: 3em;">
## Release
- You can [view the history of releases](https://github.com/maticnetwork/bor/releases), which include PR highlights
<hr style="margin-top: 3em; margin-bottom: 3em;">
From the outset we defined some guidelines to ensure new contributions only ever enhance the project:
* Quality: Code in the Polygon project should meet the style guidelines, with sufficient test-cases, descriptive commit messages, evidence that the contribution does not break any compatibility commitments or cause adverse feature interactions, and evidence of high-quality peer-review. Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
* Testing: Please ensure that the updated code passes all the tests locally before submitting a pull request. In order to run unit tests, run `make test` and to run integration tests, run `make test-integration`.
* Size: The Polygon projects culture is one of small pull-requests, regularly submitted. The larger a pull-request, the more likely it is that you will be asked to resubmit as a series of self-contained and individually reviewable smaller PRs.
* Maintainability: If the feature will require ongoing maintenance (e.g. support for a particular brand of database), we may ask you to accept responsibility for maintaining this feature
* Pull requests need to be based on and opened against the `develop` branch.
* PR title should be prefixed with package(s) they modify.
* E.g. "eth, rpc: make trace configs optional"
## License
@ -117,8 +77,6 @@ The go-ethereum binaries (i.e. all code inside of the `cmd` directory) are licen
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
included in our repository in the `COPYING` file.
<hr style="margin-top: 3em; margin-bottom: 3em;">
## Join our Discord server
Join Polygon community share your ideas or just say hi over [on Discord](https://discord.gg/zdwkdvMNY2).
Join Polygon community share your ideas or just say hi over [on Discord](https://discord.com/invite/0xPolygonDevs).

View file

@ -65,22 +65,21 @@ ls
```
./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:
This will create the config files for the different clients in `~/tests/config`
Eventually, these configuration files need to be adapted according to the following document:
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.
```
https://ethereum-tests.readthedocs.io/en/latest/retesteth-tutorial.html
Specifically, if you look inside `~/tests/config`, you'll see a directory for each configured client. Typically this directory contains the following:
* `config`: Contains the test configuration for the client
* The communication protocol to use with the client (typically TCP).
* The address(es) to use with that protocol.
* The forks supported by the client.
* The exceptions the client can throw, and how retesteth should interpret them. This is particularly important when testing the client's behavior when given invalid blocks.
* `start.sh`: Starts the client inside the Docker image
* `stop.sh`: Stops the client instance(s)
* `genesis`: A directory which includes the genesis blocks for the various forks supported by the cient. 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

View file

@ -24,6 +24,10 @@ func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Has
return receipt, nil
}
func (fb *filterBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, nil
}
func (fb *filterBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
receipt, err := fb.GetBorBlockReceipt(ctx, hash)
if err != nil || receipt == nil {

View file

@ -8,6 +8,7 @@ chain = "mainnet"
# vmdebug = false
datadir = "/var/lib/bor/data"
# ancient = ""
# db.engine = "leveldb"
# keystore = "/var/lib/bor/keystore"
# "rpc.batchlimit" = 100
# "rpc.returndatalimit" = 100000
@ -37,9 +38,9 @@ syncmode = "full"
# nodekeyhex = ""
[p2p.discovery]
# v5disc = false
bootnodes = ["enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303", "enode://88116f4295f5a31538ae409e4d44ad40d22e44ee9342869e7d68bdec55b0f83c1530355ce8b41fbec0928a7d75a5745d528450d30aec92066ab6ba1ee351d710@159.203.9.164:30303"]
bootnodes = ["enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303"]
# Uncomment below `bootnodes` field for Mumbai bootnode
# bootnodes = ["enode://095c4465fe509bd7107bbf421aea0d3ad4d4bfc3ff8f9fdc86f4f950892ae3bbc3e5c715343c4cf60c1c06e088e621d6f1b43ab9130ae56c2cacfd356a284ee4@18.213.200.99:30303"]
# bootnodes = ["enode://bdcd4786a616a853b8a041f53496d853c68d99d54ff305615cd91c03cd56895e0a7f6e9f35dbf89131044e2114a9a782b792b5661e3aff07faf125a98606a071@43.200.206.40:30303", "enode://209aaf7ed549cf4a5700fd833da25413f80a1248bd3aa7fe2a87203e3f7b236dd729579e5c8df61c97bf508281bae4969d6de76a7393bcbd04a0af70270333b3@54.216.248.9:30303"]
# bootnodesv4 = []
# bootnodesv5 = []
# static-nodes = []

View file

@ -195,7 +195,7 @@ func initGenesis(ctx *cli.Context) error {
defer stack.Close()
for _, name := range []string{"chaindata", "lightchaindata"} {
chaindb, err := stack.OpenDatabaseWithFreezer(name, 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
chaindb, err := stack.OpenDatabaseWithFreezer(name, 0, 0, ctx.String(utils.AncientFlag.Name), "", false, rawdb.ExtraDBConfig{})
if err != nil {
utils.Fatalf("Failed to open database: %v", err)
}
@ -229,7 +229,7 @@ func dumpGenesis(ctx *cli.Context) error {
// dump whatever already exists in the datadir
stack, _ := makeConfigNode(ctx)
for _, name := range []string{"chaindata", "lightchaindata"} {
db, err := stack.OpenDatabase(name, 0, 0, "", true)
db, err := stack.OpenDatabase(name, 0, 0, "", true, rawdb.ExtraDBConfig{})
if err != nil {
if !os.IsNotExist(err) {

View file

@ -123,7 +123,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
// Retrieve the DAO config flag from the database
path := filepath.Join(datadir, "geth", "chaindata")
db, err := rawdb.NewLevelDBDatabase(path, 0, 0, "", false)
db, err := rawdb.NewLevelDBDatabase(path, 0, 0, "", false, rawdb.ExtraDBConfig{})
if err != nil {
t.Fatalf("test %d: failed to open test database: %v", test, err)
}

View file

@ -461,6 +461,31 @@ var (
Value: 50,
Category: flags.PerfCategory,
}
LevelDbCompactionTableSizeFlag = &cli.Uint64Flag{
Name: "leveldb.compaction.table.size",
Usage: "LevelDB SSTable/file size in mebibytes",
Category: flags.PerfCategory,
}
LevelDbCompactionTableSizeMultiplierFlag = &cli.Float64Flag{
Name: "leveldb.compaction.table.size.multiplier",
Usage: "Multiplier on LevelDB SSTable/file size. Size for a level is determined by: `leveldb.compaction.table.size * (leveldb.compaction.table.size.multiplier ^ Level)`",
Category: flags.PerfCategory,
}
LevelDbCompactionTotalSizeFlag = &cli.Uint64Flag{
Name: "leveldb.compaction.total.size",
Usage: "Total size in mebibytes of SSTables in a given LevelDB level. Size for a level is determined by: `leveldb.compaction.total.size * (leveldb.compaction.total.size.multiplier ^ Level)`",
Category: flags.PerfCategory,
}
LevelDbCompactionTotalSizeMultiplierFlag = &cli.Float64Flag{
Name: "leveldb.compaction.total.size.multiplier",
Usage: "Multiplier on level size on LevelDB levels. Size for a level is determined by: `leveldb.compaction.total.size * (leveldb.compaction.total.size.multiplier ^ Level)`",
Category: flags.PerfCategory,
}
CacheTrieFlag = &cli.IntFlag{
Name: "cache.trie",
Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)",
@ -2170,11 +2195,15 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
LogCacheSize: ethcfg.FilterLogCacheSize,
})
filterAPI := filters.NewFilterAPI(filterSystem, isLightClient, ethcfg.BorLogs)
stack.RegisterAPIs([]rpc.API{{
Namespace: "eth",
Service: filters.NewFilterAPI(filterSystem, isLightClient, ethconfig.Defaults.BorLogs),
Service: filterAPI,
}})
// avoiding constructor changed by introducing new method to set genesis
filterAPI.SetChainConfig(ethcfg.Genesis.Config)
return filterSystem
}
@ -2287,6 +2316,8 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
err error
chainDb ethdb.Database
dbOptions = resolveExtraDBConfig(ctx)
)
switch {
@ -2300,9 +2331,9 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
chainDb = remotedb.New(client)
case ctx.String(SyncModeFlag.Name) == "light":
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly)
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly, dbOptions)
default:
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly)
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly, dbOptions)
}
if err != nil {
@ -2312,6 +2343,15 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
return chainDb
}
func resolveExtraDBConfig(ctx *cli.Context) rawdb.ExtraDBConfig {
return rawdb.ExtraDBConfig{
LevelDBCompactionTableSize: ctx.Uint64(LevelDbCompactionTableSizeFlag.Name),
LevelDBCompactionTableSizeMultiplier: ctx.Float64(LevelDbCompactionTableSizeMultiplierFlag.Name),
LevelDBCompactionTotalSize: ctx.Uint64(LevelDbCompactionTotalSizeFlag.Name),
LevelDBCompactionTotalSizeMultiplier: ctx.Float64(LevelDbCompactionTotalSizeMultiplierFlag.Name),
}
}
func IsNetworkPreset(ctx *cli.Context) bool {
for _, flag := range NetworkFlags {
bFlag, _ := flag.(*cli.BoolFlag)

View file

@ -0,0 +1,3 @@
package flags
const Milestone = true

5
common/gererics/empty.go Normal file
View file

@ -0,0 +1,5 @@
package gererics
func Empty[T any]() (t T) {
return
}

View file

@ -28,8 +28,9 @@ import (
"reflect"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// Lengths of hashes and addresses in bytes.
@ -66,6 +67,12 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
// If b is larger than len(h), b will be cropped from the left.
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
func HexToRefHash(s string) *Hash {
v := BytesToHash(FromHex(s))
return &v
}
// Bytes gets the byte representation of the underlying hash.
func (h Hash) Bytes() []byte { return h[:] }

View file

@ -1229,7 +1229,6 @@ func (c *Bor) CommitStates(
stateSyncDelay := c.config.CalculateStateSyncDelay(number)
to = time.Unix(int64(header.Time-stateSyncDelay), 0)
log.Debug("Post Indore", "lastStateIDBig", lastStateIDBig, "to", to, "stateSyncDelay", stateSyncDelay)
} else {
lastStateIDBig, err = c.GenesisContractsClient.LastStateId(nil, number-1, header.ParentHash)
if err != nil {
@ -1237,7 +1236,6 @@ func (c *Bor) CommitStates(
}
to = time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
log.Debug("Pre Indore", "lastStateIDBig", lastStateIDBig, "to", to)
}
lastStateID := lastStateIDBig.Uint64()

View file

@ -88,10 +88,13 @@ func (gc *GenesisContractsClient) CommitState(
}
msg := statefull.GetSystemMessage(common.HexToAddress(gc.StateReceiverContract), data)
log.Info("→ committing new state", "eventRecord", event.ID)
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))
log.Info("→ committed new state", "eventRecord", event.String(gasUsed))
if err != nil {
return 0, err

View file

@ -5,6 +5,7 @@ import (
"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/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
)
@ -14,5 +15,10 @@ type IHeimdallClient interface {
Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error)
FetchCheckpoint(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
FetchCheckpointCount(ctx context.Context) (int64, error)
FetchMilestone(ctx context.Context) (*milestone.Milestone, error)
FetchMilestoneCount(ctx context.Context) (int64, error)
FetchNoAckMilestone(ctx context.Context, milestoneID string) error //Fetch the bool value whether milestone corresponding to the given id failed in the Heimdall
FetchLastNoAckMilestone(ctx context.Context) (string, error) //Fetch latest failed milestone id
FetchMilestoneID(ctx context.Context, milestoneID string) error //Fetch the bool value whether milestone corresponding to the given id is in process in Heimdall
Close()
}

View file

@ -13,6 +13,7 @@ import (
"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/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@ -23,6 +24,8 @@ var (
ErrShutdownDetected = errors.New("shutdown detected")
ErrNoResponse = errors.New("got a nil response")
ErrNotSuccessfulResponse = errors.New("error while fetching data from Heimdall")
ErrNotInRejectedList = errors.New("milestoneID doesn't exist in rejected list")
ErrNotInMilestoneList = errors.New("milestoneID doesn't exist in Heimdall")
)
const (
@ -66,8 +69,16 @@ func NewHeimdallClient(urlString string) *HeimdallClient {
const (
fetchStateSyncEventsFormat = "from-id=%d&to-time=%d&limit=%d"
fetchStateSyncEventsPath = "clerk/event-record/list"
fetchCheckpoint = "/checkpoints/%s"
fetchCheckpointCount = "/checkpoints/count"
fetchCheckpoint = "/checkpoints/%s"
fetchCheckpointCount = "/checkpoints/count"
fetchMilestone = "/milestone/latest"
fetchMilestoneCount = "/milestone/count"
fetchLastNoAckMilestone = "/milestone/lastNoAck"
fetchNoAckMilestone = "/milestone/noAck/%s"
fetchMilestoneID = "/milestone/ID/%s"
fetchSpanFormat = "bor/span/%d"
)
@ -144,6 +155,23 @@ func (h *HeimdallClient) FetchCheckpoint(ctx context.Context, number int64) (*ch
return &response.Result, nil
}
// FetchMilestone fetches the checkpoint from heimdall
func (h *HeimdallClient) FetchMilestone(ctx context.Context) (*milestone.Milestone, error) {
url, err := milestoneURL(h.urlString)
if err != nil {
return nil, err
}
ctx = withRequestType(ctx, milestoneRequest)
response, err := FetchWithRetry[milestone.MilestoneResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return nil, err
}
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)
@ -161,6 +189,84 @@ func (h *HeimdallClient) FetchCheckpointCount(ctx context.Context) (int64, error
return response.Result.Result, nil
}
// FetchMilestoneCount fetches the milestone count from heimdall
func (h *HeimdallClient) FetchMilestoneCount(ctx context.Context) (int64, error) {
url, err := milestoneCountURL(h.urlString)
if err != nil {
return 0, err
}
ctx = withRequestType(ctx, milestoneCountRequest)
response, err := FetchWithRetry[milestone.MilestoneCountResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return 0, err
}
return response.Result.Count, nil
}
// FetchLastNoAckMilestone fetches the last no-ack-milestone from heimdall
func (h *HeimdallClient) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
url, err := lastNoAckMilestoneURL(h.urlString)
if err != nil {
return "", err
}
ctx = withRequestType(ctx, milestoneLastNoAckRequest)
response, err := FetchWithRetry[milestone.MilestoneLastNoAckResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return "", err
}
return response.Result.Result, nil
}
// FetchNoAckMilestone fetches the last no-ack-milestone from heimdall
func (h *HeimdallClient) FetchNoAckMilestone(ctx context.Context, milestoneID string) error {
url, err := noAckMilestoneURL(h.urlString, milestoneID)
if err != nil {
return err
}
ctx = withRequestType(ctx, milestoneNoAckRequest)
response, err := FetchWithRetry[milestone.MilestoneNoAckResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return err
}
if !response.Result.Result {
return fmt.Errorf("%w: milestoneID %q", ErrNotInRejectedList, milestoneID)
}
return nil
}
// FetchMilestoneID fetches the bool result from Heimdal whether the ID corresponding
// to the given milestone is in process in Heimdall
func (h *HeimdallClient) FetchMilestoneID(ctx context.Context, milestoneID string) error {
url, err := milestoneIDURL(h.urlString, milestoneID)
if err != nil {
return err
}
ctx = withRequestType(ctx, milestoneIDRequest)
response, err := FetchWithRetry[milestone.MilestoneIDResponse](ctx, h.client, url, h.closeCh)
if err != nil {
return err
}
if !response.Result.Result {
return fmt.Errorf("%w: milestoneID %q", ErrNotInMilestoneList, milestoneID)
}
return nil
}
// FetchWithRetry returns data from heimdall with retry
func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
// request data once
@ -266,10 +372,34 @@ func checkpointURL(urlString string, number int64) (*url.URL, error) {
return makeURL(urlString, url, "")
}
func milestoneURL(urlString string) (*url.URL, error) {
url := fetchMilestone
return makeURL(urlString, url, "")
}
func checkpointCountURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchCheckpointCount, "")
}
func milestoneCountURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchMilestoneCount, "")
}
func lastNoAckMilestoneURL(urlString string) (*url.URL, error) {
return makeURL(urlString, fetchLastNoAckMilestone, "")
}
func noAckMilestoneURL(urlString string, id string) (*url.URL, error) {
url := fmt.Sprintf(fetchNoAckMilestone, id)
return makeURL(urlString, url, "")
}
func milestoneIDURL(urlString string, id string) (*url.URL, error) {
url := fmt.Sprintf(fetchMilestoneID, id)
return makeURL(urlString, url, "")
}
func makeURL(urlString, rawPath, rawQuery string) (*url.URL, error) {
u, err := url.Parse(urlString)
if err != nil {

View file

@ -15,6 +15,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/network"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/stretchr/testify/require"
)
@ -23,7 +24,10 @@ import (
// requests to the mock heimdal server for specific functions. Add more handlers
// according to requirements.
type HttpHandlerFake struct {
handleFetchCheckpoint http.HandlerFunc
handleFetchCheckpoint http.HandlerFunc
handleFetchMilestone http.HandlerFunc
handleFetchNoAckMilestone http.HandlerFunc
handleFetchLastNoAckMilestone http.HandlerFunc
}
func (h *HttpHandlerFake) GetCheckpointHandler() http.HandlerFunc {
@ -32,6 +36,24 @@ func (h *HttpHandlerFake) GetCheckpointHandler() http.HandlerFunc {
}
}
func (h *HttpHandlerFake) GetMilestoneHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.handleFetchMilestone.ServeHTTP(w, r)
}
}
func (h *HttpHandlerFake) GetNoAckMilestoneHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.handleFetchNoAckMilestone.ServeHTTP(w, r)
}
}
func (h *HttpHandlerFake) GetLastNoAckMilestoneHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.handleFetchLastNoAckMilestone.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()
@ -41,6 +63,21 @@ func CreateMockHeimdallServer(wg *sync.WaitGroup, port int, listener net.Listene
handler.GetCheckpointHandler()(w, r)
})
// Create a route for fetching milestone
mux.HandleFunc("/milestone/latest", func(w http.ResponseWriter, r *http.Request) {
handler.GetMilestoneHandler()(w, r)
})
// Create a route for fetching milestone
mux.HandleFunc("/milestone/noAck/{id}", func(w http.ResponseWriter, r *http.Request) {
handler.GetNoAckMilestoneHandler()(w, r)
})
// Create a route for fetching milestone
mux.HandleFunc("/milestone/lastNoAck", func(w http.ResponseWriter, r *http.Request) {
handler.GetLastNoAckMilestoneHandler()(w, r)
})
// Add other routes as per requirement
// Create the server with given port and mux
@ -118,9 +155,59 @@ func TestFetchCheckpointFromMockHeimdall(t *testing.T) {
wg.Wait()
}
// TestFetchMilestoneFromMockHeimdall tests the heimdall client side logic
// to fetch milestone from a mock heimdall server.
// It can be used for debugging purpose (like response fields, marshalling/unmarshalling, etc).
func TestFetchMilestoneFromMockHeimdall(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 milestone handler function
handler := &HttpHandlerFake{}
handler.handleFetchMilestone = func(w http.ResponseWriter, _ *http.Request) {
err := json.NewEncoder(w).Encode(milestone.MilestoneResponse{
Height: "0",
Result: milestone.Milestone{
Proposer: common.Address{},
StartBlock: big.NewInt(0),
EndBlock: big.NewInt(512),
Hash: 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.FetchMilestone(context.Background())
require.NoError(t, err, "expect no error in fetching milestone")
// 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.
// interrupt handling while fetching data from a mock heimdall server.
func TestFetchShutdown(t *testing.T) {
t.Parallel()
@ -135,7 +222,7 @@ func TestFetchShutdown(t *testing.T) {
// 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)
time.Sleep(100 * time.Millisecond)
err := json.NewEncoder(w).Encode(checkpoint.CheckpointResponse{
Height: "0",
@ -165,7 +252,7 @@ func TestFetchShutdown(t *testing.T) {
// 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)
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
// Expect this to fail due to timeout
_, err = client.FetchCheckpoint(ctx, -1)
@ -182,7 +269,7 @@ func TestFetchShutdown(t *testing.T) {
w.WriteHeader(500) // Return 500 Internal Server Error.
}
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) // Use some high value for timeout
ctx, cancel = context.WithTimeout(context.Background(), 50*time.Millisecond) // Use some high value for timeout
// Cancel the context after a delay until we make request
go func(cancel context.CancelFunc) {

View file

@ -18,10 +18,15 @@ type (
)
const (
stateSyncRequest requestType = "state-sync"
spanRequest requestType = "span"
checkpointRequest requestType = "checkpoint"
checkpointCountRequest requestType = "checkpoint-count"
stateSyncRequest requestType = "state-sync"
spanRequest requestType = "span"
checkpointRequest requestType = "checkpoint"
checkpointCountRequest requestType = "checkpoint-count"
milestoneRequest requestType = "milestone"
milestoneCountRequest requestType = "milestone-count"
milestoneNoAckRequest requestType = "milestone-no-ack"
milestoneLastNoAckRequest requestType = "milestone-last-no-ack"
milestoneIDRequest requestType = "milestone-id"
)
func withRequestType(ctx context.Context, reqType requestType) context.Context {
@ -63,6 +68,41 @@ var (
},
timer: metrics.NewRegisteredTimer("client/requests/checkpointcount/duration", nil),
},
milestoneRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestone/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestone/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestone/duration", nil),
},
milestoneCountRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestonecount/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestonecount/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestonecount/duration", nil),
},
milestoneNoAckRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestonenoack/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestonenoack/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestonenoack/duration", nil),
},
milestoneLastNoAckRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestonelastnoack/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestonelastnoack/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestonelastnoack/duration", nil),
},
milestoneIDRequest: {
request: map[bool]metrics.Meter{
true: metrics.NewRegisteredMeter("client/requests/milestoneid/valid", nil),
false: metrics.NewRegisteredMeter("client/requests/milestoneid/invalid", nil),
},
timer: metrics.NewRegisteredTimer("client/requests/milestoneid/duration", nil),
},
}
)

View file

@ -0,0 +1,58 @@
package milestone
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// milestone defines a response object type of bor milestone
type Milestone struct {
Proposer common.Address `json:"proposer"`
StartBlock *big.Int `json:"start_block"`
EndBlock *big.Int `json:"end_block"`
Hash common.Hash `json:"hash"`
BorChainID string `json:"bor_chain_id"`
Timestamp uint64 `json:"timestamp"`
}
type MilestoneResponse struct {
Height string `json:"height"`
Result Milestone `json:"result"`
}
type MilestoneCount struct {
Count int64 `json:"count"`
}
type MilestoneCountResponse struct {
Height string `json:"height"`
Result MilestoneCount `json:"result"`
}
type MilestoneLastNoAck struct {
Result string `json:"result"`
}
type MilestoneLastNoAckResponse struct {
Height string `json:"height"`
Result MilestoneLastNoAck `json:"result"`
}
type MilestoneNoAck struct {
Result bool `json:"result"`
}
type MilestoneNoAckResponse struct {
Height string `json:"height"`
Result MilestoneNoAck `json:"result"`
}
type MilestoneID struct {
Result bool `json:"result"`
}
type MilestoneIDResponse struct {
Height string `json:"height"`
Result MilestoneID `json:"result"`
}

View file

@ -4,13 +4,10 @@ import (
"context"
"math/big"
"github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
"github.com/ethereum/go-ethereum/log"
hmTypes "github.com/maticnetwork/heimdall/types"
abci "github.com/tendermint/tendermint/abci/types"
)
func (h *HeimdallAppClient) FetchCheckpointCount(_ context.Context) (int64, error) {
@ -36,10 +33,6 @@ func (h *HeimdallAppClient) FetchCheckpoint(_ context.Context, number int64) (*c
return toBorCheckpoint(res), nil
}
func (h *HeimdallAppClient) NewContext() types.Context {
return h.hApp.NewContext(true, abci.Header{Height: h.hApp.LastBlockHeight()})
}
func toBorCheckpoint(hdCheckpoint hmTypes.Checkpoint) *checkpoint.Checkpoint {
return &checkpoint.Checkpoint{
Proposer: hdCheckpoint.Proposer.EthAddress(),

View file

@ -1,10 +1,14 @@
package heimdallapp
import (
"github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/log"
"github.com/maticnetwork/heimdall/app"
"github.com/maticnetwork/heimdall/cmd/heimdalld/service"
abci "github.com/tendermint/tendermint/abci/types"
)
const (
@ -25,3 +29,7 @@ func (h *HeimdallAppClient) Close() {
// Nothing to close as of now
log.Warn("Shutdown detected, Closing Heimdall App conn")
}
func (h *HeimdallAppClient) NewContext() types.Context {
return h.hApp.NewContext(true, abci.Header{Height: h.hApp.LastBlockHeight()})
}

View file

@ -0,0 +1,82 @@
package heimdallapp
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/log"
chTypes "github.com/maticnetwork/heimdall/checkpoint/types"
hmTypes "github.com/maticnetwork/heimdall/types"
)
func (h *HeimdallAppClient) FetchMilestoneCount(_ context.Context) (int64, error) {
log.Info("Fetching milestone count")
res := h.hApp.CheckpointKeeper.GetMilestoneCount(h.NewContext())
log.Info("Fetched Milestone Count")
return int64(res), nil
}
func (h *HeimdallAppClient) FetchMilestone(_ context.Context) (*milestone.Milestone, error) {
log.Info("Fetching Latest Milestone")
res, err := h.hApp.CheckpointKeeper.GetLastMilestone(h.NewContext())
if err != nil {
return nil, err
}
log.Info("Fetched Latest Milestone")
return toBorMilestone(res), nil
}
func (h *HeimdallAppClient) FetchNoAckMilestone(_ context.Context, milestoneID string) error {
log.Info("Fetching No Ack Milestone By MilestoneID", "MilestoneID", milestoneID)
res := h.hApp.CheckpointKeeper.GetNoAckMilestone(h.NewContext(), milestoneID)
if res {
log.Info("Fetched No Ack By MilestoneID", "MilestoneID", milestoneID)
return nil
}
return fmt.Errorf("Still No Ack Milestone exist corresponding to MilestoneId:%v", milestoneID)
}
func (h *HeimdallAppClient) FetchLastNoAckMilestone(_ context.Context) (string, error) {
log.Info("Fetching Latest No Ack Milestone ID")
res := h.hApp.CheckpointKeeper.GetLastNoAckMilestone(h.NewContext())
log.Info("Fetched Latest No Ack Milestone ID")
return res, nil
}
func (h *HeimdallAppClient) FetchMilestoneID(_ context.Context, milestoneID string) error {
log.Info("Fetching Milestone ID ", "MilestoneID", milestoneID)
res := chTypes.GetMilestoneID()
if res == milestoneID {
return nil
}
return fmt.Errorf("Milestone corresponding to Milestone ID:%v doesn't exist in Heimdall", milestoneID)
}
func toBorMilestone(hdMilestone *hmTypes.Milestone) *milestone.Milestone {
return &milestone.Milestone{
Proposer: hdMilestone.Proposer.EthAddress(),
StartBlock: big.NewInt(int64(hdMilestone.StartBlock)),
EndBlock: big.NewInt(int64(hdMilestone.EndBlock)),
Hash: hdMilestone.Hash.EthHash(),
BorChainID: hdMilestone.BorChainID,
Timestamp: hdMilestone.TimeStamp,
}
}

View file

@ -7,22 +7,18 @@ import (
"github.com/maticnetwork/heimdall/clerk/types"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
abci "github.com/tendermint/tendermint/abci/types"
)
func (h *HeimdallAppClient) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
totalRecords := make([]*clerk.EventRecordWithTime, 0)
hCtx := h.hApp.NewContext(true, abci.Header{Height: h.hApp.LastBlockHeight()})
for {
fromRecord, err := h.hApp.ClerkKeeper.GetEventRecord(hCtx, fromID)
fromRecord, err := h.hApp.ClerkKeeper.GetEventRecord(h.NewContext(), fromID)
if err != nil {
return nil, err
}
events, err := h.hApp.ClerkKeeper.GetEventRecordListWithTime(hCtx, fromRecord.RecordTime, time.Unix(to, 0), 1, stateFetchLimit)
events, err := h.hApp.ClerkKeeper.GetEventRecordListWithTime(h.NewContext(), fromRecord.RecordTime, time.Unix(to, 0), 1, stateFetchLimit)
if err != nil {
return nil, err
}

View file

@ -0,0 +1,103 @@
package heimdallgrpc
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/log"
proto "github.com/maticnetwork/polyproto/heimdall"
protoutils "github.com/maticnetwork/polyproto/utils"
)
func (h *HeimdallGRPCClient) FetchMilestoneCount(ctx context.Context) (int64, error) {
log.Info("Fetching milestone count")
res, err := h.client.FetchMilestoneCount(ctx, nil)
if err != nil {
return 0, err
}
log.Info("Fetched milestone count")
return res.Result.Count, nil
}
func (h *HeimdallGRPCClient) FetchMilestone(ctx context.Context) (*milestone.Milestone, error) {
log.Info("Fetching milestone")
res, err := h.client.FetchMilestone(ctx, nil)
if err != nil {
return nil, err
}
log.Info("Fetched milestone")
milestone := &milestone.Milestone{
StartBlock: new(big.Int).SetUint64(res.Result.StartBlock),
EndBlock: new(big.Int).SetUint64(res.Result.EndBlock),
Hash: protoutils.ConvertH256ToHash(res.Result.RootHash),
Proposer: protoutils.ConvertH160toAddress(res.Result.Proposer),
BorChainID: res.Result.BorChainID,
Timestamp: uint64(res.Result.Timestamp.GetSeconds()),
}
return milestone, nil
}
func (h *HeimdallGRPCClient) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
log.Info("Fetching latest no ack milestone Id")
res, err := h.client.FetchLastNoAckMilestone(ctx, nil)
if err != nil {
return "", err
}
log.Info("Fetched last no-ack milestone")
return res.Result.Result, nil
}
func (h *HeimdallGRPCClient) FetchNoAckMilestone(ctx context.Context, milestoneID string) error {
req := &proto.FetchMilestoneNoAckRequest{
MilestoneID: milestoneID,
}
log.Info("Fetching no ack milestone", "milestoneaID", milestoneID)
res, err := h.client.FetchNoAckMilestone(ctx, req)
if err != nil {
return err
}
if !res.Result.Result {
return fmt.Errorf("Not in rejected list: milestoneID %q", milestoneID)
}
log.Info("Fetched no ack milestone", "milestoneaID", milestoneID)
return nil
}
func (h *HeimdallGRPCClient) FetchMilestoneID(ctx context.Context, milestoneID string) error {
req := &proto.FetchMilestoneIDRequest{
MilestoneID: milestoneID,
}
log.Info("Fetching milestone id", "milestoneID", milestoneID)
res, err := h.client.FetchMilestoneID(ctx, req)
if err != nil {
return err
}
if !res.Result.Result {
return fmt.Errorf("This milestoneID %q does not exist", milestoneID)
}
log.Info("Fetched milestone id", "milestoneID", milestoneID)
return nil
}

View file

@ -12,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
@ -76,8 +77,9 @@ func ApplyMessage(
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, state, chainConfig, vm.Config{})
// nolint : contextcheck
// Apply the transaction to the current state (included in the env)
_, gasLeft, err := vmenv.Call(
ret, gasLeft, err := vmenv.Call(
vm.AccountRef(msg.From()),
*msg.To(),
msg.Data(),
@ -85,6 +87,13 @@ func ApplyMessage(
msg.Value(),
nil,
)
success := big.NewInt(5).SetBytes(ret)
if success.Cmp(big.NewInt(0)) == 0 {
log.Error("message execution failed on contract", "msgData", msg.Data)
}
// Update the state with pending changes
if err != nil {
state.Finalise(true)

View file

@ -193,7 +193,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
} else {
dir := b.TempDir()
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false)
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false, rawdb.ExtraDBConfig{})
if err != nil {
b.Fatalf("cannot create temporary database: %v", err)
}
@ -296,7 +296,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
func benchWriteChain(b *testing.B, full bool, count uint64) {
for i := 0; i < b.N; i++ {
dir := b.TempDir()
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
@ -310,7 +310,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
func benchReadChain(b *testing.B, full bool, count uint64) {
dir := b.TempDir()
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
@ -325,7 +325,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}

View file

@ -163,7 +163,7 @@ var DefaultCacheConfig = &CacheConfig{
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
TriesInMemory: 128,
TriesInMemory: 1024,
}
// BlockChain represents the canonical chain given a database with a genesis
@ -1964,8 +1964,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
if !isValid {
// The chain to be imported is invalid as the blocks doesn't match with
// the whitelisted checkpoints.
return it.index, whitelist.ErrCheckpointMismatch
// the whitelisted block number.
return it.index, whitelist.ErrMismatch
}
// Left-trim all the known blocks that don't need to build snapshot
@ -3112,6 +3112,10 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
return 0, err
}
func (bc *BlockChain) GetChainConfig() *params.ChainConfig {
return bc.chainConfig
}
// SetBlockValidatorAndProcessorForTesting sets the current validator and processor.
// This method can be used to force an invalid blockchain to be verified for tests.
// This method is unsafe and should only be used before block import starts.

View file

@ -63,6 +63,17 @@ func (bc *BlockChain) CurrentSafeBlock() *types.Header {
return bc.currentSafeBlock.Load()
}
// CurrentFinalizedBlock retrieves the current finalized block of the canonical
// chain. The block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentFinalizedBlock(number uint64) *types.Block {
hash := rawdb.ReadCanonicalHash(bc.db, number)
if hash == (common.Hash{}) {
return nil
}
return bc.GetBlock(hash, number)
}
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {

View file

@ -2073,9 +2073,14 @@ func TestLowDiffLongChain(t *testing.T) {
Config: params.TestChainConfig,
BaseFee: big.NewInt(params.InitialBaseFee),
}
//Using TempTriesInMemory variable instead of DefaultTempInTries because changing the
//value of DefaultTempInTries to 1024 is failing the test.
TempTriesInMemory := 128
// We must use a pretty long chain to ensure that the fork doesn't overtake us
// until after at least 128 blocks post tip
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*TempTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
b.OffsetTime(-9)
})
@ -2093,7 +2098,7 @@ func TestLowDiffLongChain(t *testing.T) {
}
// Generate fork chain, starting from an early block
parent := blocks[10]
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*TempTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
})
@ -2165,7 +2170,10 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
}
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) {
//Using TempTriesInMemory variable instead of DefaultTempInTries because changing the
//value of DefaultTempInTries to 1024 is failing the test.
TempTriesInMemory := 128
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*TempTriesInMemory, 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)
@ -2183,14 +2191,14 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
lastPrunedIndex := len(blocks) - TempTriesInMemory - 1
//lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-TempTriesInMemory]
// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
}
// // Verify pruning of lastPrunedBlock
// if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
// t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
// }
// Verify firstNonPrunedBlock is not pruned
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
@ -2214,7 +2222,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Generate fork chain, make it longer than canon
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
parent := blocks[parentIndex]
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*TempTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
if int(b.header.Number.Uint64()) >= mergeBlock {

View file

@ -231,17 +231,37 @@ func (c *chainReaderFake) GetTd(hash common.Hash, number uint64) *big.Int {
}
// 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) {
func (w *chainValidatorFake) IsValidPeer(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, error) {
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) ProcessMilestone(endBlockNum uint64, endBlockHash common.Hash) {}
func (w *chainValidatorFake) ProcessFutureMilestone(num uint64, hash common.Hash) {
}
func (w *chainValidatorFake) PurgeCheckpointWhitelist() {}
func (w *chainValidatorFake) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *chainValidatorFake) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *chainValidatorFake) PurgeWhitelistedCheckpoint() {}
func (w *chainValidatorFake) PurgeWhitelistedMilestone() {}
func (w *chainValidatorFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
return map[uint64]*types.Header{}, nil
}
func (w *chainValidatorFake) LockMutex(endBlockNum uint64) bool {
return false
}
func (w *chainValidatorFake) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
}
func (w *chainValidatorFake) UnlockSprint(endBlockNum uint64) {
}
func (w *chainValidatorFake) RemoveMilestoneID(milestoneId string) {
}
func (w *chainValidatorFake) GetMilestoneIDsList() []string {
return nil
}

33
core/rawdb/checkpoint.go Normal file
View file

@ -0,0 +1,33 @@
// nolint
package rawdb
import (
"errors"
"github.com/ethereum/go-ethereum/common"
)
var (
lastCheckpoint = []byte("LastCheckpoint")
ErrEmptyLastFinality = errors.New("empty response while getting last finality")
ErrIncorrectFinality = errors.New("last checkpoint in the DB is incorrect")
ErrIncorrectFinalityToStore = errors.New("failed to marshal the last finality struct")
ErrDBNotResponding = errors.New("failed to store the last finality struct")
ErrIncorrectLockFieldToStore = errors.New("failed to marshal the lockField struct ")
ErrIncorrectLockField = errors.New("lock field in the DB is incorrect")
ErrIncorrectFutureMilestoneFieldToStore = errors.New("failed to marshal the future milestone field struct ")
ErrIncorrectFutureMilestoneField = errors.New("future milestone field in the DB is incorrect")
)
type Checkpoint struct {
Finality
}
func (c *Checkpoint) clone() *Checkpoint {
return &Checkpoint{}
}
func (c *Checkpoint) block() (uint64, common.Hash) {
return c.Block, c.Hash
}

View file

@ -321,8 +321,8 @@ func NewMemoryDatabaseWithCap(size int) ethdb.Database {
// NewLevelDBDatabase creates a persistent key-value database without a freezer
// moving immutable chain segments into cold storage.
func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
db, err := leveldb.New(file, cache, handles, namespace, readonly)
func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool, extraDBConfig ExtraDBConfig) (ethdb.Database, error) {
db, err := leveldb.New(file, cache, handles, namespace, readonly, resolveLevelDBConfig(extraDBConfig))
if err != nil {
return nil, err
}
@ -332,6 +332,15 @@ func NewLevelDBDatabase(file string, cache int, handles int, namespace string, r
return NewDatabase(db), nil
}
func resolveLevelDBConfig(config ExtraDBConfig) leveldb.LevelDBConfig {
return leveldb.LevelDBConfig{
CompactionTableSize: config.LevelDBCompactionTableSize,
CompactionTableSizeMultiplier: config.LevelDBCompactionTableSizeMultiplier,
CompactionTotalSize: config.LevelDBCompactionTotalSize,
CompactionTotalSizeMultiplier: config.LevelDBCompactionTotalSizeMultiplier,
}
}
const (
dbPebble = "pebble"
dbLeveldb = "leveldb"
@ -366,6 +375,14 @@ type OpenOptions struct {
Cache int // the capacity(in megabytes) of the data caching
Handles int // number of files to be open simultaneously
ReadOnly bool
ExtraDBConfig ExtraDBConfig
}
type ExtraDBConfig struct {
LevelDBCompactionTableSize uint64 // LevelDB SSTable/file size in mebibytes
LevelDBCompactionTableSizeMultiplier float64 // Multiplier on LevelDB SSTable/file size
LevelDBCompactionTotalSize uint64 // Total size in mebibytes of SSTables in a given LevelDB level
LevelDBCompactionTotalSizeMultiplier float64 // Multiplier on level size on LevelDB levels
}
// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
@ -393,9 +410,10 @@ func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
return nil, fmt.Errorf("unknown db.engine %v", o.Type)
}
log.Info("Using leveldb as the backing database")
// Use leveldb, either as default (no explicit choice), or pre-existing, or chosen explicitly
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
log.Info("Using leveldb as the backing database")
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.ExtraDBConfig)
}
// Open opens both a disk-based key-value database such as leveldb or pebble, but also

221
core/rawdb/milestone.go Normal file
View file

@ -0,0 +1,221 @@
// nolint
package rawdb
import (
"fmt"
json "github.com/json-iterator/go"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/gererics"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
var (
lastMilestone = []byte("LastMilestone")
lockFieldKey = []byte("LockField")
futureMilestoneKey = []byte("FutureMilestoneField")
)
type Finality struct {
Block uint64
Hash common.Hash
}
type LockField struct {
Val bool
Block uint64
Hash common.Hash
IdList map[string]struct{}
}
type FutureMilestoneField struct {
Order []uint64
List map[uint64]common.Hash
}
func (f *Finality) set(block uint64, hash common.Hash) {
f.Block = block
f.Hash = hash
}
type Milestone struct {
Finality
}
func (m *Milestone) clone() *Milestone {
return &Milestone{}
}
func (m *Milestone) block() (uint64, common.Hash) {
return m.Block, m.Hash
}
func ReadFinality[T BlockFinality[T]](db ethdb.KeyValueReader) (uint64, common.Hash, error) {
lastTV, key := getKey[T]()
data, err := db.Get(key)
if err != nil {
return 0, common.Hash{}, fmt.Errorf("%w: empty response for %s", err, string(key))
}
if len(data) == 0 {
return 0, common.Hash{}, fmt.Errorf("%w for %s", ErrEmptyLastFinality, string(key))
}
if err = json.Unmarshal(data, lastTV); err != nil {
log.Error(fmt.Sprintf("Unable to unmarshal the last %s block number in database", string(key)), "err", err)
return 0, common.Hash{}, fmt.Errorf("%w(%v) for %s, data %v(%q)",
ErrIncorrectFinality, err, string(key), data, string(data))
}
block, hash := lastTV.block()
return block, hash, nil
}
func WriteLastFinality[T BlockFinality[T]](db ethdb.KeyValueWriter, block uint64, hash common.Hash) error {
lastTV, key := getKey[T]()
lastTV.set(block, hash)
enc, err := json.Marshal(lastTV)
if err != nil {
log.Error(fmt.Sprintf("Failed to marshal the %s struct", string(key)), "err", err)
return fmt.Errorf("%w: %v for %s struct", ErrIncorrectFinalityToStore, err, string(key))
}
if err := db.Put(key, enc); err != nil {
log.Error(fmt.Sprintf("Failed to store the %s struct", string(key)), "err", err)
return fmt.Errorf("%w: %v for %s struct", ErrDBNotResponding, err, string(key))
}
return nil
}
type BlockFinality[T any] interface {
set(block uint64, hash common.Hash)
clone() T
block() (uint64, common.Hash)
}
func getKey[T BlockFinality[T]]() (T, []byte) {
lastT := gererics.Empty[T]().clone()
var key []byte
switch any(lastT).(type) {
case *Milestone:
key = lastMilestone
case *Checkpoint:
key = lastCheckpoint
}
return lastT, key
}
func WriteLockField(db ethdb.KeyValueWriter, val bool, block uint64, hash common.Hash, idListMap map[string]struct{}) error {
lockField := LockField{
Val: val,
Block: block,
Hash: hash,
IdList: idListMap,
}
key := lockFieldKey
enc, err := json.Marshal(lockField)
if err != nil {
log.Error("Failed to marshal the lock field struct", "err", err)
return fmt.Errorf("%w: %v for lock field struct", ErrIncorrectLockFieldToStore, err)
}
if err := db.Put(key, enc); err != nil {
log.Error("Failed to store the lock field struct", "err", err)
return fmt.Errorf("%w: %v for lock field struct", ErrDBNotResponding, err)
}
return nil
}
func ReadLockField(db ethdb.KeyValueReader) (bool, uint64, common.Hash, map[string]struct{}, error) {
key := lockFieldKey
lockField := LockField{}
data, err := db.Get(key)
if err != nil {
return false, 0, common.Hash{}, nil, fmt.Errorf("%w: empty response for lock field", err)
}
if len(data) == 0 {
return false, 0, common.Hash{}, nil, fmt.Errorf("%w for %s", ErrIncorrectLockField, string(key))
}
if err = json.Unmarshal(data, &lockField); err != nil {
log.Error(fmt.Sprintf("Unable to unmarshal the lock field in database"), "err", err)
return false, 0, common.Hash{}, nil, fmt.Errorf("%w(%v) for lock field , data %v(%q)",
ErrIncorrectLockField, err, data, string(data))
}
val, block, hash, idList := lockField.Val, lockField.Block, lockField.Hash, lockField.IdList
return val, block, hash, idList, nil
}
func WriteFutureMilestoneList(db ethdb.KeyValueWriter, order []uint64, list map[uint64]common.Hash) error {
futureMilestoneField := FutureMilestoneField{
Order: order,
List: list,
}
key := futureMilestoneKey
enc, err := json.Marshal(futureMilestoneField)
if err != nil {
log.Error("Failed to marshal the future milestone field struct", "err", err)
return fmt.Errorf("%w: %v for future milestone field struct", ErrIncorrectFutureMilestoneFieldToStore, err)
}
if err = db.Put(key, enc); err != nil {
log.Error("Failed to store the future milestone field struct", "err", err)
return fmt.Errorf("%w: %v for future milestone field struct", ErrDBNotResponding, err)
}
return nil
}
func ReadFutureMilestoneList(db ethdb.KeyValueReader) ([]uint64, map[uint64]common.Hash, error) {
key := futureMilestoneKey
futureMilestoneField := FutureMilestoneField{}
data, err := db.Get(key)
if err != nil {
return nil, nil, fmt.Errorf("%w: empty response for future milestone field", err)
}
if len(data) == 0 {
return nil, nil, fmt.Errorf("%w for %s", ErrIncorrectLockField, string(key))
}
if err = json.Unmarshal(data, &futureMilestoneField); err != nil {
log.Error(fmt.Sprintf("Unable to unmarshal the future milestone field in database"), "err", err)
return nil, nil, fmt.Errorf("%w(%v) for future milestone field, data %v(%q)",
ErrIncorrectFutureMilestoneField, err, data, string(data))
}
order, list := futureMilestoneField.Order, futureMilestoneField.List
return order, list, nil
}

View file

@ -21,8 +21,11 @@ import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
@ -273,3 +276,66 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
}
}
}
func TestValidateKnownAccounts(t *testing.T) {
t.Parallel()
knownAccounts := make(types.KnownAccounts)
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"))
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"), map[common.Hash]common.Hash{
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"),
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"),
})
stateobjaddr1 := common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1")
stateobjaddr2 := common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2")
storageaddr1 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000zzz")
storageaddr21 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa")
storageaddr22 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc")
data1 := common.BytesToHash([]byte{24})
data21 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb")
data22 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd")
s := newStateTest()
// set initial state object value
s.state.SetState(stateobjaddr1, storageaddr1, data1)
s.state.SetState(stateobjaddr2, storageaddr21, data21)
s.state.SetState(stateobjaddr2, storageaddr22, data22)
require.NoError(t, s.state.ValidateKnownAccounts(knownAccounts))
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf"))
stateobjaddr3 := common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2")
storageaddr3 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000yyy")
data3 := common.BytesToHash([]byte{24})
s.state.SetState(stateobjaddr3, storageaddr3, data3)
// expected error
err := s.state.ValidateKnownAccounts(knownAccounts)
require.Error(t, err, "should have been an error")
// correct the previous mistake "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf" -> "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"))
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3"), map[common.Hash]common.Hash{
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"),
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"),
})
stateobjaddr4 := common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3")
storageaddr41 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa")
storageaddr42 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc")
data4 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb")
s.state.SetState(stateobjaddr4, storageaddr41, data4)
s.state.SetState(stateobjaddr4, storageaddr42, data4)
// expected error
err = s.state.ValidateKnownAccounts(knownAccounts)
require.Error(t, err, "should have been an error")
}

View file

@ -1640,6 +1640,39 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre
return s.accessList.Contains(addr, slot)
}
func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error {
if knownAccounts == nil {
return nil
}
for k, v := range knownAccounts {
// check if the value is hex string or an object
switch {
case v.IsSingle():
trie, _ := s.StorageTrie(k)
if trie != nil {
actualRootHash := trie.Hash()
if *v.Single != actualRootHash {
return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash)
}
} else {
return fmt.Errorf("Storage Trie is nil for: %v", k)
}
case v.IsStorage():
for slot, value := range v.Storage {
actualValue := s.GetState(k, slot)
if value != actualValue {
return fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue)
}
}
default:
return fmt.Errorf("impossible to validate known accounts: %v", k)
}
}
return nil
}
// convertAccountSet converts a provided account set from address keyed to hash keyed.
func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.Hash]struct{} {
ret := make(map[common.Hash]struct{})

View file

BIN
core/triecache/data.1.bin Normal file

Binary file not shown.

View file

BIN
core/triecache/data.3.bin Normal file

Binary file not shown.

BIN
core/triecache/data.4.bin Normal file

Binary file not shown.

BIN
core/triecache/data.5.bin Normal file

Binary file not shown.

View file

BIN
core/triecache/data.7.bin Normal file

Binary file not shown.

BIN
core/triecache/metadata.bin Normal file

Binary file not shown.

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
@ -165,7 +166,7 @@ func (m *sortedMap) reheap(withRlock bool) {
if withRlock {
m.m.RLock()
log.Info("[DEBUG] Acquired lock over txpool map while performing reheap")
log.Debug("Acquired lock over txpool map while performing reheap")
}
for nonce := range m.items {
@ -563,6 +564,32 @@ func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactio
return removed, invalids
}
// FilterTxConditional returns the conditional transactions with invalid KnownAccounts
// TODO - We will also have to check block range and time stamp range!
func (l *list) FilterTxConditional(state *state.StateDB) types.Transactions {
removed := l.txs.filter(func(tx *types.Transaction) bool {
if options := tx.GetOptions(); options != nil {
err := state.ValidateKnownAccounts(options.KnownAccounts)
if err != nil {
log.Error("Error while Filtering Tx Conditional", "err", err)
return true
}
return false
}
return false
})
if len(removed) == 0 {
return nil
}
l.txs.reheap(true)
return removed
}
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
func (l *list) Cap(threshold int) types.Transactions {

View file

@ -22,7 +22,11 @@ import (
"testing"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
@ -78,3 +82,63 @@ func BenchmarkListAdd(b *testing.B) {
}
}
}
func TestFilterTxConditional(t *testing.T) {
t.Parallel()
// Create an in memory state db to test against.
memDb := rawdb.NewMemoryDatabase()
db := state.NewDatabase(memDb)
state, _ := state.New(common.Hash{}, db, nil)
// Create a private key to sign transactions.
key, _ := crypto.GenerateKey()
// Create a list.
list := newList(true)
// Create a transaction with no defined tx options
// and add to the list.
tx := transaction(0, 1000, key)
list.Add(tx, DefaultConfig.PriceBump)
// There should be no drops at this point.
// No state has been modified.
drops := list.FilterTxConditional(state)
count := len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)
// Create another transaction with a known account storage root tx option
// and add to the list.
tx2 := transaction(1, 1000, key)
var options types.OptionsAA4337
options.KnownAccounts = types.KnownAccounts{
common.Address{19: 1}: &types.Value{
Single: common.HexToRefHash("0xe734938daf39aae1fa4ee64dc3155d7c049f28b57a8ada8ad9e86832e0253bef"),
},
}
state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{30: 1})
tx2.PutOptions(&options)
list.Add(tx2, DefaultConfig.PriceBump)
// There should still be no drops as no state has been modified.
drops = list.FilterTxConditional(state)
count = len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)
// Set state that conflicts with tx2's policy
state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{31: 1})
// tx2 should be the single transaction filtered out
drops = list.FilterTxConditional(state)
count = len(drops)
require.Equal(t, 1, count, "got %d filtered by TxOptions when there should be a single one", count)
require.Equal(t, tx2, drops[0], "Got %x, expected %x", drops[0].Hash(), tx2.Hash())
}

View file

@ -825,6 +825,9 @@ func (pool *TxPool) validateTxBasics(tx *types.Transaction, local bool) error {
// return core.ErrInsufficientFunds
// }
// Verify that replacing transactions will not result in overdraft
pool.pendingMu.RLock()
defer pool.pendingMu.RUnlock()
list := pool.pending[from]
if list != nil { // Sender already has pending txs
sum := new(big.Int).Add(tx.Cost(), list.totalcost)
@ -2303,10 +2306,19 @@ func (pool *TxPool) demoteUnexecutables() {
pool.enqueueTx(hash, tx, false, false)
}
pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen))
// Drop all transactions that no longer have valid TxOptions
txConditionalsRemoved := list.FilterTxConditional(pool.currentState)
for _, tx := range txConditionalsRemoved {
hash := tx.Hash()
pool.all.Remove(hash)
log.Trace("Removed invalid conditional transaction", "hash", hash)
}
pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved)))
if pool.locals.contains(addr) {
localGauge.Dec(int64(oldsLen + dropsLen + invalidsLen))
localGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved)))
}
// If there's a gap in front, alert (should never happen) and postpone all transactions
if list.Len() > 0 && list.txs.Get(nonce) == nil {

View file

@ -1993,6 +1993,7 @@ func TestUnderpricing(t *testing.T) {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
@ -2023,6 +2024,7 @@ func TestUnderpricing(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding an underpriced transaction on block limit fails
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); !errors.Is(err, ErrUnderpriced) {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
@ -2064,6 +2066,7 @@ func TestUnderpricing(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
if err := pool.AddLocal(ltx); err != nil {
@ -2263,6 +2266,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2])
if err := pool.AddLocal(ltx); err != nil {

View file

@ -188,6 +188,44 @@ func (h *Header) EmptyReceipts() bool {
return h.ReceiptHash == EmptyReceiptsHash
}
// ValidateBlockNumberOptions4337 validates the block range passed as in the options parameter in the conditional transaction (EIP-4337)
func (h *Header) ValidateBlockNumberOptions4337(minBlockNumber *big.Int, maxBlockNumber *big.Int) error {
currentBlockNumber := h.Number
if minBlockNumber != nil {
if currentBlockNumber.Cmp(minBlockNumber) == -1 {
return fmt.Errorf("current block number %v is less than minimum block number: %v", currentBlockNumber, minBlockNumber)
}
}
if maxBlockNumber != nil {
if currentBlockNumber.Cmp(maxBlockNumber) == 1 {
return fmt.Errorf("current block number %v is greater than maximum block number: %v", currentBlockNumber, maxBlockNumber)
}
}
return nil
}
// ValidateBlockNumberOptions4337 validates the timestamp range passed as in the options parameter in the conditional transaction (EIP-4337)
func (h *Header) ValidateTimestampOptions4337(minTimestamp *uint64, maxTimestamp *uint64) error {
currentBlockTime := h.Time
if minTimestamp != nil {
if currentBlockTime < *minTimestamp {
return fmt.Errorf("current block time %v is less than minimum timestamp: %v", currentBlockTime, minTimestamp)
}
}
if maxTimestamp != nil {
if currentBlockTime > *maxTimestamp {
return fmt.Errorf("current block time %v is greater than maximum timestamp: %v", currentBlockTime, maxTimestamp)
}
}
return nil
}
// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {

View file

@ -463,3 +463,167 @@ func TestRlpDecodeParentHash(t *testing.T) {
}
}
}
func TestValidateBlockNumberOptions4337(t *testing.T) {
t.Parallel()
testsPass := []struct {
number string
header Header
minBlockNumber *big.Int
maxBlockNumber *big.Int
}{
{
"1",
Header{Number: big.NewInt(10)},
big.NewInt(0),
big.NewInt(20),
},
{
"2",
Header{Number: big.NewInt(10)},
big.NewInt(10),
big.NewInt(10),
},
{
"3",
Header{Number: big.NewInt(10)},
big.NewInt(10),
big.NewInt(11),
},
{
"4",
Header{Number: big.NewInt(10)},
big.NewInt(0),
big.NewInt(10),
},
}
testsFail := []struct {
number string
header Header
minBlockNumber *big.Int
maxBlockNumber *big.Int
}{
{
"5",
Header{Number: big.NewInt(10)},
big.NewInt(0),
big.NewInt(0),
},
{
"6",
Header{Number: big.NewInt(10)},
big.NewInt(0),
big.NewInt(9),
},
{
"7",
Header{Number: big.NewInt(10)},
big.NewInt(11),
big.NewInt(9),
},
{
"8",
Header{Number: big.NewInt(10)},
big.NewInt(11),
big.NewInt(20),
},
}
for _, test := range testsPass {
if err := test.header.ValidateBlockNumberOptions4337(test.minBlockNumber, test.maxBlockNumber); err != nil {
t.Fatalf("test number %v should not have failed. err: %v", test.number, err)
}
}
for _, test := range testsFail {
if err := test.header.ValidateBlockNumberOptions4337(test.minBlockNumber, test.maxBlockNumber); err == nil {
t.Fatalf("test number %v should have failed. err is nil", test.number)
}
}
}
func TestValidateTimestampOptions4337(t *testing.T) {
t.Parallel()
u64Ptr := func(n uint64) *uint64 {
return &n
}
testsPass := []struct {
number string
header Header
minTimestamp *uint64
maxTimestamp *uint64
}{
{
"1",
Header{Time: 1600000000},
u64Ptr(1500000000),
u64Ptr(1700000000),
},
{
"2",
Header{Time: 1600000000},
u64Ptr(1600000000),
u64Ptr(1600000000),
},
{
"3",
Header{Time: 1600000000},
u64Ptr(1600000000),
u64Ptr(1700000000),
},
{
"4",
Header{Time: 1600000000},
u64Ptr(1500000000),
u64Ptr(1600000000),
},
}
testsFail := []struct {
number string
header Header
minTimestamp *uint64
maxTimestamp *uint64
}{
{
"5",
Header{Time: 1600000000},
u64Ptr(1500000000),
u64Ptr(1500000000),
},
{
"6",
Header{Time: 1600000000},
u64Ptr(1400000000),
u64Ptr(1500000000),
},
{
"7",
Header{Time: 1600000000},
u64Ptr(1700000000),
u64Ptr(1500000000),
},
{
"8",
Header{Time: 1600000000},
u64Ptr(1700000000),
u64Ptr(1800000000),
},
}
for _, test := range testsPass {
if err := test.header.ValidateTimestampOptions4337(test.minTimestamp, test.maxTimestamp); err != nil {
t.Fatalf("test number %v should not have failed. err: %v", test.number, err)
}
}
for _, test := range testsFail {
if err := test.header.ValidateTimestampOptions4337(test.minTimestamp, test.maxTimestamp); err == nil {
t.Fatalf("test number %v should have failed. err is nil", test.number)
}
}
}

View file

@ -25,7 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"`
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
BlockHash common.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex hexutil.Uint `json:"transactionIndex"`
@ -40,7 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
enc.TxHash = r.TxHash
enc.ContractAddress = r.ContractAddress
enc.GasUsed = hexutil.Uint64(r.GasUsed)
enc.EffectiveGasPrice = r.EffectiveGasPrice
enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice)
enc.BlockHash = r.BlockHash
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
@ -59,7 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress *common.Address `json:"contractAddress"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"`
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
BlockHash *common.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
@ -101,7 +101,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
}
r.GasUsed = uint64(*dec.GasUsed)
if dec.EffectiveGasPrice != nil {
r.EffectiveGasPrice = dec.EffectiveGasPrice
r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice)
}
if dec.BlockHash != nil {
r.BlockHash = *dec.BlockHash

View file

@ -76,6 +76,7 @@ type receiptMarshaling struct {
PostState hexutil.Bytes
Status hexutil.Uint64
CumulativeGasUsed hexutil.Uint64
EffectiveGasPrice *hexutil.Big
GasUsed hexutil.Uint64
BlockNumber *hexutil.Big
TransactionIndex hexutil.Uint

View file

@ -54,6 +54,9 @@ type Transaction struct {
inner TxData // Consensus contents of a transaction
time time.Time // Time first seen locally (spam avoidance)
// knownAccounts (EIP-4337)
optionsAA4337 *OptionsAA4337
// caches
hash atomic.Pointer[common.Hash]
size atomic.Pointer[uint64]
@ -101,6 +104,16 @@ type TxData interface {
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
}
// PutOptions stores the optionsAA4337 field of the conditional transaction (EIP-4337)
func (tx *Transaction) PutOptions(options *OptionsAA4337) {
tx.optionsAA4337 = options
}
// GetOptions returns the optionsAA4337 field of the conditional transaction (EIP-4337)
func (tx *Transaction) GetOptions() *OptionsAA4337 {
return tx.optionsAA4337
}
// EncodeRLP implements rlp.Encoder
func (tx *Transaction) EncodeRLP(w io.Writer) error {
if tx.Type() == LegacyTxType {

View file

@ -0,0 +1,146 @@
package types
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
type KnownAccounts map[common.Address]*Value
type Value struct {
Single *common.Hash
Storage map[common.Hash]common.Hash
}
func SingleFromHex(hex string) *Value {
return &Value{Single: common.HexToRefHash(hex)}
}
func FromMap(m map[string]string) *Value {
res := map[common.Hash]common.Hash{}
for k, v := range m {
res[common.HexToHash(k)] = common.HexToHash(v)
}
return &Value{Storage: res}
}
func (v *Value) IsSingle() bool {
return v != nil && v.Single != nil && !v.IsStorage()
}
func (v *Value) IsStorage() bool {
return v != nil && v.Storage != nil
}
const EmptyValue = "{}"
func (v *Value) MarshalJSON() ([]byte, error) {
if v.IsSingle() {
return json.Marshal(v.Single)
}
if v.IsStorage() {
return json.Marshal(v.Storage)
}
return []byte(EmptyValue), nil
}
const hashTypeName = "Hash"
func (v *Value) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
var m map[string]json.RawMessage
err := json.Unmarshal(data, &m)
if err != nil {
// single Hash value case
v.Single = new(common.Hash)
innerErr := json.Unmarshal(data, v.Single)
if innerErr != nil {
return fmt.Errorf("can't unmarshal to single value with error: %v value %q", innerErr, string(data))
}
return nil
}
res := make(map[common.Hash]common.Hash, len(m))
for k, v := range m {
// check k if it is a Hex value
var kHash common.Hash
err = hexutil.UnmarshalFixedText(hashTypeName, []byte(k), kHash[:])
if err != nil {
return fmt.Errorf("%w by key: %s with key %q and value %q", ErrKnownAccounts, err, k, string(v))
}
// check v if it is a Hex value
var vHash common.Hash
err = hexutil.UnmarshalFixedText("hashTypeName", bytes.Trim(v, "\""), vHash[:])
if err != nil {
return fmt.Errorf("%w by value: %s with key %q and value %q", ErrKnownAccounts, err, k, string(v))
}
res[kHash] = vHash
}
v.Storage = res
return nil
}
func InsertKnownAccounts[T common.Hash | map[common.Hash]common.Hash](accounts KnownAccounts, k common.Address, v T) {
switch typedV := any(v).(type) {
case common.Hash:
accounts[k] = &Value{Single: &typedV}
case map[common.Hash]common.Hash:
accounts[k] = &Value{Storage: typedV}
}
}
type OptionsAA4337 struct {
KnownAccounts KnownAccounts `json:"knownAccounts"`
BlockNumberMin *big.Int `json:"blockNumberMin"`
BlockNumberMax *big.Int `json:"blockNumberMax"`
TimestampMin *uint64 `json:"timestampMin"`
TimestampMax *uint64 `json:"timestampMax"`
}
var ErrKnownAccounts = errors.New("an incorrect list of knownAccounts")
func (ka KnownAccounts) ValidateLength() error {
if ka == nil {
return nil
}
length := 0
for _, v := range ka {
// check if the value is hex string or an object
if v.IsSingle() {
length += 1
} else {
length += len(v.Storage)
}
}
if length >= 1000 {
return fmt.Errorf("number of slots/accounts in KnownAccounts %v exceeds the limit of 1000", length)
}
return nil
}

View file

@ -0,0 +1,31 @@
package types
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
)
func TestKnownAccounts(t *testing.T) {
t.Parallel()
requestRaw := []byte(`{"0xadd1add1add1add1add1add1add1add1add1add1": "0x000000000000000000000000313aadca1750caadc7bcb26ff08175c95dcf8e38", "0xadd2add2add2add2add2add2add2add2add2add2": {"0x0000000000000000000000000000000000000000000000000000000000000aaa": "0x0000000000000000000000000000000000000000000000000000000000000bbb", "0x0000000000000000000000000000000000000000000000000000000000000ccc": "0x0000000000000000000000000000000000000000000000000000000000000ddd"}}`)
accs := &KnownAccounts{}
err := json.Unmarshal(requestRaw, accs)
require.NoError(t, err)
expected := &KnownAccounts{
common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"): SingleFromHex("0x000000000000000000000000313aadca1750caadc7bcb26ff08175c95dcf8e38"),
common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"): FromMap(map[string]string{
"0x0000000000000000000000000000000000000000000000000000000000000aaa": "0x0000000000000000000000000000000000000000000000000000000000000bbb",
"0x0000000000000000000000000000000000000000000000000000000000000ccc": "0x0000000000000000000000000000000000000000000000000000000000000ddd",
}),
}
require.Equal(t, expected, accs)
}

View file

@ -8,6 +8,7 @@ verbosity = 3 # Logging verbosity for the server (5=trace|4=de
vmdebug = false # Record information useful for VM and contract debugging
datadir = "var/lib/bor" # Path of the data directory to store information
ancient = "" # Data directory for ancient chain segments (default = inside chaindata)
db.engine = "leveldb" # Used to select leveldb or pebble as database (default = leveldb)
keystore = "" # Path of the directory where keystores are located
"rpc.batchlimit" = 100 # Maximum number of messages in a batch (default=100, use 0 for no limits)
"rpc.returndatalimit" = 100000 # Maximum size (in bytes) a result of an rpc request could have (default=100000, use 0 for no limits)

View file

@ -18,6 +18,8 @@ The ```bor server``` command runs the Bor client.
- ```datadir.ancient```: Data directory for ancient chain segments (default = inside chaindata)
- ```db.engine```: Backing database implementation to use ('leveldb' or 'pebble') (default: leveldb)
- ```keystore```: Path of the directory where keystores are located
- ```rpc.batchlimit```: Maximum number of messages in a batch (default=100, use 0 for no limits) (default: 100)
@ -124,6 +126,16 @@ The ```bor server``` command runs the Bor client.
- ```fdlimit```: Raise the open file descriptor resource limit (default = system fd limit) (default: 0)
### ExtraDB Options
- ```leveldb.compaction.table.size```: LevelDB SSTable/file size in mebibytes (default: 2)
- ```leveldb.compaction.table.size.multiplier```: Multiplier on LevelDB SSTable/file size. Size for a level is determined by: `leveldb.compaction.table.size * (leveldb.compaction.table.size.multiplier ^ Level)` (default: 1)
- ```leveldb.compaction.total.size```: Total size in mebibytes of SSTables in a given LevelDB level. Size for a level is determined by: `leveldb.compaction.total.size * (leveldb.compaction.total.size.multiplier ^ Level)` (default: 10)
- ```leveldb.compaction.total.size.multiplier```: Multiplier on level size on LevelDB levels. Size for a level is determined by: `leveldb.compaction.total.size * (leveldb.compaction.total.size.multiplier ^ Level)` (default: 10)
### JsonRPC Options
- ```rpc.gascap```: Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite) (default: 50000000)

View file

@ -280,11 +280,22 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
return stateDb.RawDump(opts), nil
}
// TODO - Check this
var header *types.Header
if blockNr == rpc.LatestBlockNumber {
header = api.eth.blockchain.CurrentBlock()
} else if blockNr == rpc.FinalizedBlockNumber {
header = api.eth.blockchain.CurrentFinalBlock()
finalBlockNumber, err := getFinalizedBlockNumber(api.eth)
if err != nil {
return state.Dump{}, fmt.Errorf("finalized block not found")
}
block := api.eth.blockchain.CurrentFinalizedBlock(finalBlockNumber)
if block == nil {
return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
}
header = block.Header()
} else if blockNr == rpc.SafeBlockNumber {
header = api.eth.blockchain.CurrentSafeBlock()
} else {
@ -379,7 +390,17 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if number == rpc.LatestBlockNumber {
header = api.eth.blockchain.CurrentBlock()
} else if number == rpc.FinalizedBlockNumber {
header = api.eth.blockchain.CurrentFinalBlock()
finalBlockNumber, err := getFinalizedBlockNumber(api.eth)
if err != nil {
return state.IteratorDump{}, fmt.Errorf("finalized block not found")
}
block := api.eth.blockchain.CurrentFinalizedBlock(finalBlockNumber)
if block == nil {
return state.IteratorDump{}, fmt.Errorf("finalized block not found")
}
header = block.Header()
} else if number == rpc.SafeBlockNumber {
header = api.eth.blockchain.CurrentSafeBlock()
} else {
@ -678,3 +699,27 @@ func (api *DebugAPI) SetTrieFlushInterval(interval string) error {
return nil
}
func getFinalizedBlockNumber(eth *Ethereum) (uint64, error) {
currentBlockNum := eth.BlockChain().CurrentBlock()
doExist, number, hash := eth.Downloader().GetWhitelistedMilestone()
if doExist && number <= currentBlockNum.Number.Uint64() {
block := eth.BlockChain().GetBlockByNumber(number)
if block.Hash() == hash {
return number, nil
}
}
doExist, number, hash = eth.Downloader().GetWhitelistedCheckpoint()
if doExist && number <= currentBlockNum.Number.Uint64() {
block := eth.BlockChain().GetBlockByNumber(number)
if block.Hash() == hash {
return number, nil
}
}
return 0, fmt.Errorf("No finalized block")
}

View file

@ -76,14 +76,15 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
}
if number == rpc.FinalizedBlockNumber {
if !b.eth.Merger().TDDReached() {
return nil, errors.New("'finalized' tag not supported on pre-merge network")
finalBlockNumber, err := getFinalizedBlockNumber(b.eth)
if err != nil {
return nil, errors.New("finalized block not found")
}
block := b.eth.blockchain.CurrentFinalBlock()
block := b.eth.blockchain.CurrentFinalizedBlock(finalBlockNumber)
if block != nil {
return block, nil
return block.Header(), nil
}
return nil, errors.New("finalized block not found")
@ -144,13 +145,12 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
}
if number == rpc.FinalizedBlockNumber {
if !b.eth.Merger().TDDReached() {
return nil, errors.New("'finalized' tag not supported on pre-merge network")
finalBlocknumber, err := getFinalizedBlockNumber(b.eth)
if err != nil {
return nil, errors.New("finalized block not found")
}
header := b.eth.blockchain.CurrentFinalBlock()
return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil
return b.eth.blockchain.CurrentFinalizedBlock(finalBlocknumber), nil
}
if number == rpc.SafeBlockNumber {
@ -313,6 +313,10 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
}
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
if signedTx.GetOptions() != nil && !b.eth.Miner().GetWorker().IsRunning() {
return errors.New("bundled transactions are not broadcasted therefore they will not submitted to the transaction pool")
}
err := b.eth.txPool.AddLocal(signedTx)
if err != nil {
if unwrapped := errors.Unwrap(err); unwrapped != nil {
@ -451,10 +455,18 @@ func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Blo
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}
func (b *EthAPIBackend) GetCheckpointWhitelist() map[uint64]common.Hash {
return b.eth.Downloader().ChainValidator.GetCheckpointWhitelist()
func (b *EthAPIBackend) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return b.eth.Downloader().ChainValidator.GetWhitelistedCheckpoint()
}
func (b *EthAPIBackend) PurgeCheckpointWhitelist() {
b.eth.Downloader().ChainValidator.PurgeCheckpointWhitelist()
func (b *EthAPIBackend) PurgeWhitelistedCheckpoint() {
b.eth.Downloader().ChainValidator.PurgeWhitelistedCheckpoint()
}
func (b *EthAPIBackend) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return b.eth.Downloader().ChainValidator.GetWhitelistedMilestone()
}
func (b *EthAPIBackend) PurgeWhitelistedMilestone() {
b.eth.Downloader().ChainValidator.PurgeWhitelistedMilestone()
}

View file

@ -141,7 +141,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
// Assemble the Ethereum object
chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "ethereum/db/chaindata/", false)
extraDBConfig := resolveExtraDBConfig(config)
chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "ethereum/db/chaindata/", false, extraDBConfig)
if err != nil {
return nil, err
}
@ -246,7 +247,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
)
checker := whitelist.NewService(10)
checker := whitelist.NewService(chainDb)
// check if Parallel EVM is enabled
// if enabled, use parallel state processor
@ -332,6 +333,15 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
return ethereum, nil
}
func resolveExtraDBConfig(config *ethconfig.Config) rawdb.ExtraDBConfig {
return rawdb.ExtraDBConfig{
LevelDBCompactionTableSize: config.LevelDbCompactionTableSize,
LevelDBCompactionTableSizeMultiplier: config.LevelDbCompactionTableSizeMultiplier,
LevelDBCompactionTotalSize: config.LevelDbCompactionTotalSize,
LevelDBCompactionTotalSizeMultiplier: config.LevelDbCompactionTotalSizeMultiplier,
}
}
func makeExtraData(extra []byte) []byte {
if len(extra) == 0 {
// create default extradata
@ -351,6 +361,11 @@ func makeExtraData(extra []byte) []byte {
return extra
}
// PeerCount returns the number of connected peers.
func (s *Ethereum) PeerCount() int {
return s.p2pServer.PeerCount()
}
// APIs return the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API {
@ -565,7 +580,8 @@ func (s *Ethereum) StopMining() {
th.SetThreads(-1)
}
// Stop the block creating itself
s.miner.Stop()
ch := make(chan struct{})
s.miner.Stop(ch)
}
func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
@ -634,6 +650,9 @@ func (s *Ethereum) Start() error {
s.handler.Start(maxPeers)
go s.startCheckpointWhitelistService()
go s.startMilestoneWhitelistService()
go s.startNoAckMilestoneService()
go s.startNoAckMilestoneByIDService()
return nil
}
@ -641,83 +660,183 @@ func (s *Ethereum) Start() error {
var (
ErrNotBorConsensus = errors.New("not bor consensus was given")
ErrBorConsensusWithoutHeimdall = errors.New("bor consensus without heimdall")
)
whitelistTimeout = 30 * time.Second
const (
whitelistTimeout = 30 * time.Second
noAckMilestoneTimeout = 4 * time.Second
)
// StartCheckpointWhitelistService starts the goroutine to fetch checkpoints and update the
// checkpoint whitelist map.
func (s *Ethereum) startCheckpointWhitelistService() {
const (
tickerDuration = 100 * time.Second
fnName = "whitelist checkpoint"
)
s.retryHeimdallHandler(s.handleWhitelistCheckpoint, tickerDuration, whitelistTimeout, fnName)
}
// startMilestoneWhitelistService starts the goroutine to fetch milestiones and update the
// milestone whitelist map.
func (s *Ethereum) startMilestoneWhitelistService() {
const (
tickerDuration = 12 * time.Second
fnName = "whitelist milestone"
)
s.retryHeimdallHandler(s.handleMilestone, tickerDuration, whitelistTimeout, fnName)
}
func (s *Ethereum) startNoAckMilestoneService() {
const (
tickerDuration = 6 * time.Second
fnName = "no-ack-milestone service"
)
s.retryHeimdallHandler(s.handleNoAckMilestone, tickerDuration, noAckMilestoneTimeout, fnName)
}
func (s *Ethereum) startNoAckMilestoneByIDService() {
const (
tickerDuration = 1 * time.Minute
fnName = "no-ack-milestone-by-id service"
)
s.retryHeimdallHandler(s.handleNoAckMilestoneByID, tickerDuration, noAckMilestoneTimeout, fnName)
}
func (s *Ethereum) retryHeimdallHandler(fn heimdallHandler, tickerDuration time.Duration, timeout time.Duration, fnName string) {
retryHeimdallHandler(fn, tickerDuration, timeout, fnName, s.closeCh, s.getHandler)
}
func retryHeimdallHandler(fn heimdallHandler, tickerDuration time.Duration, timeout time.Duration, fnName string, closeCh chan struct{}, getHandler func() (*ethHandler, *bor.Bor, error)) {
// a shortcut helps with tests and early exit
select {
case <-s.closeCh:
case <-closeCh:
return
default:
}
// first run the checkpoint whitelist
firstCtx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err := s.handleWhitelistCheckpoint(firstCtx, true)
ethHandler, bor, err := getHandler()
if err != nil {
log.Error("error while getting the ethHandler", "err", err)
return
}
// first run for fetching milestones
firstCtx, cancel := context.WithTimeout(context.Background(), timeout)
err = fn(firstCtx, ethHandler, bor)
cancel()
if err != nil {
if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) {
return
}
log.Warn("unable to whitelist checkpoint - first run", "err", err)
log.Warn(fmt.Sprintf("unable to start the %s service - first run", fnName), "err", err)
}
ticker := time.NewTicker(100 * time.Second)
ticker := time.NewTicker(tickerDuration)
defer ticker.Stop()
for {
select {
case <-ticker.C:
ctx, cancel := context.WithTimeout(context.Background(), whitelistTimeout)
err := s.handleWhitelistCheckpoint(ctx, false)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
err := fn(ctx, ethHandler, bor)
cancel()
if err != nil {
log.Warn("unable to whitelist checkpoint", "err", err)
log.Warn(fmt.Sprintf("unable to handle %s", fnName), "err", err)
}
case <-s.closeCh:
case <-closeCh:
return
}
}
}
// handleWhitelistCheckpoint handles the checkpoint whitelist mechanism.
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, first bool) error {
func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
// Create a new bor verifier, which will be used to verify checkpoints and milestones
verifier := newBorVerifier()
blockNum, blockHash, err := ethHandler.fetchWhitelistCheckpoint(ctx, bor, s, verifier)
// 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 err != nil {
return err
}
ethHandler.downloader.ProcessCheckpoint(blockNum, blockHash)
return nil
}
type heimdallHandler func(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error
// handleMilestone handles the milestone mechanism.
func (s *Ethereum) handleMilestone(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
// Create a new bor verifier, which will be used to verify checkpoints and milestones
verifier := newBorVerifier()
num, hash, err := ethHandler.fetchWhitelistMilestone(ctx, bor, s, verifier)
// If the current chain head is behind the received milestone, add it to the future milestone
// list. Also, the hash mismatch (end block hash) error will lead to rewind so also
// add that milestone to the future milestone list.
if errors.Is(err, errMissingBlocks) || errors.Is(err, errHashMismatch) {
ethHandler.downloader.ProcessFutureMilestone(num, hash)
}
if err != nil {
return err
}
ethHandler.downloader.ProcessMilestone(num, hash)
return nil
}
func (s *Ethereum) handleNoAckMilestone(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
milestoneID, err := ethHandler.fetchNoAckMilestone(ctx, bor)
//If failed to fetch the no-ack milestone then it give the error.
if err != nil {
return err
}
ethHandler.downloader.RemoveMilestoneID(milestoneID)
return nil
}
func (s *Ethereum) handleNoAckMilestoneByID(ctx context.Context, ethHandler *ethHandler, bor *bor.Bor) error {
milestoneIDs := ethHandler.downloader.GetMilestoneIDsList()
for _, milestoneID := range milestoneIDs {
// todo: check if we can ignore the error
err := ethHandler.fetchNoAckMilestoneByID(ctx, bor, milestoneID)
if err == nil {
ethHandler.downloader.RemoveMilestoneID(milestoneID)
}
}
return nil
}
func (s *Ethereum) getHandler() (*ethHandler, *bor.Bor, error) {
ethHandler := (*ethHandler)(s.handler)
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
if !ok {
return ErrNotBorConsensus
return nil, nil, ErrNotBorConsensus
}
if bor.HeimdallClient == nil {
return ErrBorConsensusWithoutHeimdall
return nil, nil, ErrBorConsensusWithoutHeimdall
}
// 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.
for i := 0; i < len(blockNums); i++ {
ethHandler.downloader.ProcessCheckpoint(blockNums[i], blockHashes[i])
}
return nil
return ethHandler, bor, nil
}
// Stop implements node.Lifecycle, terminating all internal goroutines used by the

View file

@ -3,6 +3,7 @@ package eth
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
@ -11,8 +12,11 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc"
)
var errBorEngineNotAvailable error = errors.New("Only available in Bor engine")
// GetRootHash returns root hash for given start and end block
func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
var api *bor.API
@ -24,7 +28,7 @@ func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, end
}
if api == nil {
return "", errors.New("Only available in Bor engine")
return "", errBorEngineNotAvailable
}
root, err := api.GetRootHash(starBlockNr, endBlockNr)
@ -35,6 +39,70 @@ func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, end
return root, nil
}
// GetRootHash returns root hash for given start and end block
func (b *EthAPIBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
var api *bor.API
for _, _api := range b.eth.Engine().APIs(b.eth.BlockChain()) {
if _api.Namespace == "bor" {
api = _api.Service.(*bor.API)
}
}
if api == nil {
return false, errBorEngineNotAvailable
}
//Confirmation of 16 blocks on the endblock
tipConfirmationBlockNr := endBlockNr + uint64(16)
//Check if tipConfirmation block exit
_, err := b.BlockByNumber(ctx, rpc.BlockNumber(tipConfirmationBlockNr))
if err != nil {
return false, errTipConfirmationBlock
}
//Check if end block exist
localEndBlock, err := b.BlockByNumber(ctx, rpc.BlockNumber(endBlockNr))
if err != nil {
return false, errEndBlock
}
localEndBlockHash := localEndBlock.Hash().String()
downloader := b.eth.handler.downloader
isLocked := downloader.LockMutex(endBlockNr)
if !isLocked {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
return false, errors.New("Whitelisted number or locked sprint number is more than the received end block number")
}
if localEndBlockHash != hash {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
return false, fmt.Errorf("Hash mismatch: localChainHash %s, milestoneHash %s", localEndBlockHash, hash)
}
ethHandler := (*ethHandler)(b.eth.handler)
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
if !ok {
return false, fmt.Errorf("Bor not available")
}
err = bor.HeimdallClient.FetchMilestoneID(ctx, milestoneId)
if err != nil {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
return false, fmt.Errorf("Milestone ID doesn't exist in Heimdall")
}
downloader.UnlockMutex(true, milestoneId, endBlockNr, localEndBlock.Hash())
return true, nil
}
// GetBorBlockReceipt returns bor block receipt
func (b *EthAPIBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
receipt := b.eth.blockchain.GetBorReceiptByHash(hash)

View file

@ -1,61 +1,171 @@
// nolint
package eth
import (
"context"
"errors"
"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/metrics"
"github.com/ethereum/go-ethereum/rpc"
)
type checkpointVerifier struct {
verify func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error)
var (
// errMissingBlocks is returned when we don't have the blocks locally, yet.
errMissingBlocks = errors.New("missing blocks")
// errRootHash is returned when we aren't able to calculate the root hash
// locally for a range of blocks.
errRootHash = errors.New("failed to get local root hash")
// errHashMismatch is returned when the local hash doesn't match
// with the hash of checkpoint/milestone. It is the root hash of blocks
// in case of checkpoint and is end block hash in case of milestones.
errHashMismatch = errors.New("hash mismatch")
// errEndBlock is returned when we're unable to fetch a block locally.
errEndBlock = errors.New("failed to get end block")
// errEndBlock is returned when we're unable to fetch a block locally.
errTipConfirmationBlock = errors.New("failed to get tip confirmation block")
// errBlockNumberConversion is returned when we get err in parsing hexautil block number
errBlockNumberConversion = errors.New("failed to parse the block number")
//Metrics for collecting the rewindLength
rewindLengthMeter = metrics.NewRegisteredMeter("chain/autorewind/length", nil)
)
type borVerifier struct {
verify func(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error)
}
func newCheckpointVerifier(verifyFn func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error)) *checkpointVerifier {
if verifyFn != nil {
return &checkpointVerifier{verifyFn}
func newBorVerifier() *borVerifier {
return &borVerifier{borVerify}
}
func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error) {
str := "milestone"
if isCheckpoint {
str = "checkpoint"
}
verifyFn = func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error) {
// check if we have the given blocks
currentBlock := eth.BlockChain().CurrentBlock()
if currentBlock == nil {
log.Debug(fmt.Sprintf("Failed to fetch current block from blockchain while verifying incoming %s", str))
return hash, errMissingBlocks
}
head := currentBlock.Number.Uint64()
if head < end {
log.Debug(fmt.Sprintf("Current head block behind incoming %s block", str), "head", head, "end block", end)
return hash, errMissingBlocks
}
var localHash string
// verify the hash
if isCheckpoint {
var err error
// in case of checkpoint get the rootHash
localHash, err = handler.ethAPI.GetRootHash(ctx, start, end)
if err != nil {
log.Debug("Failed to get root hash of given block range while whitelisting checkpoint", "start", start, "end", end, "err", err)
return hash, errRootHash
}
} else {
// in case of milestone(isCheckpoint==false) get the hash of endBlock
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(end), false)
if err != nil {
log.Debug("Failed to get end block hash while whitelisting milestone", "number", end, "err", err)
return hash, errEndBlock
}
localHash = fmt.Sprintf("%v", block["hash"])[2:]
}
//nolint
if localHash != hash {
if isCheckpoint {
log.Warn("Root hash mismatch while whitelisting checkpoint", "expected", localHash, "got", hash)
} else {
log.Warn("End block hash mismatch while whitelisting milestone", "expected", localHash, "got", hash)
}
ethHandler := (*ethHandler)(eth.handler)
var (
startBlock = checkpoint.StartBlock.Uint64()
endBlock = checkpoint.EndBlock.Uint64()
rewindTo uint64
doExist bool
)
// check if we have the checkpoint blocks
//nolint:contextcheck
head := handler.ethAPI.BlockNumber()
if head < hexutil.Uint64(endBlock) {
log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", endBlock)
return "", errMissingCheckpoint
if doExist, rewindTo, _ = ethHandler.downloader.GetWhitelistedMilestone(); doExist {
} else if doExist, rewindTo, _ = ethHandler.downloader.GetWhitelistedCheckpoint(); doExist {
} else {
if start <= 0 {
rewindTo = 0
} else {
rewindTo = start - 1
}
}
// 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 head-rewindTo > 255 {
rewindTo = head - 255
}
if roothash != checkpoint.RootHash.String()[2:] {
log.Warn("Checkpoint root hash mismatch while whitelisting", "expected", checkpoint.RootHash.String()[2:], "got", roothash)
return "", errCheckpointRootHashMismatch
if isCheckpoint {
log.Warn("Rewinding chain due to checkpoint root hash mismatch", "number", rewindTo)
} else {
log.Warn("Rewinding chain due to milestone endblock hash mismatch", "number", rewindTo)
}
// 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
}
rewindBack(eth, head, rewindTo)
hash := fmt.Sprintf("%v", block["hash"])
return hash, nil
return hash, errHashMismatch
}
// fetch the end block hash
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(end), false)
if err != nil {
log.Debug("Failed to get end block hash while whitelisting", "err", err)
return hash, errEndBlock
}
hash = fmt.Sprintf("%v", block["hash"])
return hash, nil
}
// Stop the miner if the mining process is running and rewind back the chain
func rewindBack(eth *Ethereum, head uint64, rewindTo uint64) {
if eth.Miner().Mining() {
ch := make(chan struct{})
eth.Miner().Stop(ch)
<-ch
rewind(eth, head, rewindTo)
eth.Miner().Start()
} else {
rewind(eth, head, rewindTo)
}
}
func rewind(eth *Ethereum, head uint64, rewindTo uint64) {
err := eth.blockchain.SetHead(rewindTo)
if err != nil {
log.Error("Error while rewinding the chain", "to", rewindTo, "err", err)
} else {
rewindLengthMeter.Mark(int64(head - rewindTo))
}
return &checkpointVerifier{verifyFn}
}

View file

@ -216,6 +216,8 @@ type BlockChain interface {
// Snapshots returns the blockchain snapshot tree to paused it during sync.
Snapshots() *snapshot.Tree
GetChainConfig() *params.ChainConfig
// TrieDB retrieves the low level trie database used for interacting
// with trie nodes.
TrieDB() *trie.Database
@ -367,8 +369,7 @@ func (d *Downloader) LegacySync(id string, head common.Hash, td, ttd *big.Int, m
if errors.Is(err, errInvalidChain) || errors.Is(err, errBadPeer) || errors.Is(err, errTimeout) ||
errors.Is(err, errStallingPeer) || errors.Is(err, errUnsyncedPeer) || errors.Is(err, errEmptyHeaderSet) ||
errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) ||
errors.Is(err, whitelist.ErrCheckpointMismatch) {
errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) {
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
if d.dropPeer == nil {
@ -386,7 +387,13 @@ func (d *Downloader) LegacySync(id string, head common.Hash, td, ttd *big.Int, m
return err // This is an expected fault, don't keep printing it in a spin-loop
}
log.Warn("Synchronisation failed, retrying", "err", err)
// Warn in case of any error thrown by whitelisting module
if errors.Is(err, whitelist.ErrNoRemote) || errors.Is(err, whitelist.ErrMismatch) {
log.Warn("Synchronisation failed due to whitelist validation", "peer", id, "err", err)
return err
}
log.Warn("Synchronisation failed, retrying", "peer", id, "err", err)
return err
}
@ -895,9 +902,10 @@ 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 peer from which the chain is to be downloaded
if d.ChainValidator != nil {
if _, err := d.IsValidPeer(remoteHeader, d.getFetchHeadersByNumber(p)); err != nil {
if _, err := d.IsValidPeer(d.getFetchHeadersByNumber(p)); err != nil {
return 0, err
}
}
@ -1989,6 +1997,11 @@ func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) erro
}
}
// GetWhitelistService returns the pointer to the whitelist service
func (d *Downloader) GetWhitelistService() ethereum.ChainValidator {
return d.ChainValidator
}
// readHeaderRange returns a list of headers, using the given last header as the base,
// and going backwards towards genesis. This method assumes that the caller already has
// placed a reasonable cap on count.

View file

@ -95,7 +95,7 @@ func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
}
//nolint: staticcheck
tester.downloader = New(0, db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, success, whitelist.NewService(10))
tester.downloader = New(0, db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, success, whitelist.NewService(db))
return tester
}
@ -1965,7 +1965,7 @@ func newWhitelistFake(validate func(count int) (bool, error)) *whitelistFake {
// IsValidPeer is the mock function which the downloader will use to validate the chain
// to be received from a peer.
func (w *whitelistFake) IsValidPeer(_ *types.Header, _ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
func (w *whitelistFake) IsValidPeer(_ func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
defer func() {
w.count++
}()
@ -1978,13 +1978,33 @@ func (w *whitelistFake) IsValidChain(current *types.Header, headers []*types.Hea
}
func (w *whitelistFake) ProcessCheckpoint(_ uint64, _ common.Hash) {}
func (w *whitelistFake) GetCheckpointWhitelist() map[uint64]common.Hash {
return nil
func (w *whitelistFake) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *whitelistFake) PurgeCheckpointWhitelist() {}
func (w *whitelistFake) PurgeWhitelistedCheckpoint() {}
func (w *whitelistFake) ProcessMilestone(_ uint64, _ common.Hash) {}
func (w *whitelistFake) ProcessFutureMilestone(_ uint64, _ common.Hash) {}
func (w *whitelistFake) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return false, 0, common.Hash{}
}
func (w *whitelistFake) PurgeWhitelistedMilestone() {}
func (w *whitelistFake) GetCheckpoints(current, sidechainHeader *types.Header, sidechainCheckpoints []*types.Header) (map[uint64]*types.Header, error) {
return map[uint64]*types.Header{}, nil
}
func (w *whitelistFake) LockMutex(endBlockNum uint64) bool {
return false
}
func (w *whitelistFake) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
}
func (w *whitelistFake) UnlockSprint(endBlockNum uint64) {
}
func (w *whitelistFake) RemoveMilestoneID(milestoneId string) {
}
func (w *whitelistFake) GetMilestoneIDsList() []string {
return nil
}
// TestFakedSyncProgress66WhitelistMismatch tests if in case of whitelisted
// checkpoint mismatch with opposite peer, the sync should fail.
@ -1996,7 +2016,7 @@ func TestFakedSyncProgress66WhitelistMismatch(t *testing.T) {
tester := newTester(t)
validate := func(count int) (bool, error) {
return false, whitelist.ErrCheckpointMismatch
return false, whitelist.ErrMismatch
}
tester.downloader.ChainValidator = newWhitelistFake(validate)
@ -2049,7 +2069,7 @@ func TestFakedSyncProgress66NoRemoteCheckpoint(t *testing.T) {
validate := func(count int) (bool, error) {
// only return the `ErrNoRemoteCheckpoint` error for the first call
if count == 0 {
return false, whitelist.ErrNoRemoteCheckpoint
return false, whitelist.ErrNoRemote
}
return true, nil
@ -2065,7 +2085,7 @@ func TestFakedSyncProgress66NoRemoteCheckpoint(t *testing.T) {
// Synchronise with the peer and make sure all blocks were retrieved
// Should fail in first attempt
if err := tester.sync("light", nil, mode); err != nil {
assert.Equal(t, whitelist.ErrNoRemoteCheckpoint, err, "failed synchronisation")
assert.Equal(t, whitelist.ErrNoRemote, err, "failed synchronisation")
}
// Try syncing again, should succeed

View file

@ -0,0 +1,67 @@
package whitelist
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/metrics"
)
type checkpoint struct {
finality[*rawdb.Checkpoint]
}
type checkpointService interface {
finalityService
}
var (
//Metrics for collecting the whitelisted milestone number
whitelistedCheckpointNumberMeter = metrics.NewRegisteredGauge("chain/checkpoint/latest", nil)
//Metrics for collecting the number of invalid chains received
CheckpointChainMeter = metrics.NewRegisteredMeter("chain/checkpoint/isvalidchain", nil)
//Metrics for collecting the number of valid peers received
CheckpointPeerMeter = metrics.NewRegisteredMeter("chain/checkpoint/isvalidpeer", nil)
)
// IsValidChain checks the validity of chain by comparing it
// against the local checkpoint entry
func (w *checkpoint) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
w.finality.RLock()
defer w.finality.RUnlock()
res, err := w.finality.IsValidChain(currentHeader, chain)
if res {
CheckpointChainMeter.Mark(int64(1))
} else {
CheckpointPeerMeter.Mark(int64(-1))
}
return res, err
}
// 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 finality submitted to mainchain.
func (w *checkpoint) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
res, err := w.finality.IsValidPeer(fetchHeadersByNumber)
if res {
CheckpointPeerMeter.Mark(int64(1))
} else {
CheckpointPeerMeter.Mark(int64(-1))
}
return res, err
}
func (w *checkpoint) Process(block uint64, hash common.Hash) {
w.finality.Lock()
defer w.finality.Unlock()
w.finality.Process(block, hash)
whitelistedCheckpointNumberMeter.Update(int64(block))
}

View file

@ -0,0 +1,96 @@
package whitelist
import (
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
type finality[T rawdb.BlockFinality[T]] struct {
sync.RWMutex
db ethdb.Database
Hash common.Hash // Whitelisted Hash, populated by reaching out to heimdall
Number uint64 // Number , populated by reaching out to heimdall
interval uint64 // Interval, until which we can allow importing
doExist bool
}
type finalityService interface {
IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error)
Get() (bool, uint64, common.Hash)
Process(block uint64, hash common.Hash)
Purge()
}
// 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 finality submitted to mainchain.
func (f *finality[T]) IsValidPeer(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 finalized block
f.RLock()
doExist := f.doExist
number := f.Number
hash := f.Hash
f.RUnlock()
return isValidPeer(fetchHeadersByNumber, doExist, number, hash)
}
// IsValidChain checks the validity of chain by comparing it
// against the local checkpoint entry
// todo: need changes
func (f *finality[T]) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
// Return if we've received empty chain
if len(chain) == 0 {
return false, nil
}
res, err := isValidChain(currentHeader, chain, f.doExist, f.Number, f.Hash)
return res, err
}
func (f *finality[T]) Process(block uint64, hash common.Hash) {
f.doExist = true
f.Hash = hash
f.Number = block
err := rawdb.WriteLastFinality[T](f.db, block, hash)
if err != nil {
log.Error("Error in writing whitelist state to db", "err", err)
}
}
// Get returns the existing whitelisted
// entries of checkpoint of the form (doExist,block number,block hash.)
func (f *finality[T]) Get() (bool, uint64, common.Hash) {
f.RLock()
defer f.RUnlock()
if f.doExist {
return f.doExist, f.Number, f.Hash
}
block, hash, err := rawdb.ReadFinality[T](f.db)
if err != nil {
fmt.Println("Error while reading whitelisted state from Db", "err", err)
return false, f.Number, f.Hash
}
return true, block, hash
}
// Purge purges the whitlisted checkpoint
func (f *finality[T]) Purge() {
f.Lock()
defer f.Unlock()
f.doExist = false
}

View file

@ -0,0 +1,315 @@
package whitelist
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/flags"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)
type milestone struct {
finality[*rawdb.Milestone]
LockedMilestoneNumber uint64 // Locked sprint number
LockedMilestoneHash common.Hash //Hash for the locked endBlock
Locked bool //
LockedMilestoneIDs map[string]struct{} //list of milestone ids
FutureMilestoneList map[uint64]common.Hash // Future Milestone list
FutureMilestoneOrder []uint64 // Future Milestone Order
MaxCapacity int //Capacity of future Milestone list
}
type milestoneService interface {
finalityService
GetMilestoneIDsList() []string
RemoveMilestoneID(milestoneId string)
LockMutex(endBlockNum uint64) bool
UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash)
UnlockSprint(endBlockNum uint64)
ProcessFutureMilestone(num uint64, hash common.Hash)
}
var (
//Metrics for collecting the whitelisted milestone number
whitelistedMilestoneMeter = metrics.NewRegisteredGauge("chain/milestone/latest", nil)
//Metrics for collecting the future milestone number
FutureMilestoneMeter = metrics.NewRegisteredGauge("chain/milestone/future", nil)
//Metrics for collecting the length of the MilestoneIds map
MilestoneIdsLengthMeter = metrics.NewRegisteredGauge("chain/milestone/idslength", nil)
//Metrics for collecting the number of valid chains received
MilestoneChainMeter = metrics.NewRegisteredMeter("chain/milestone/isvalidchain", nil)
//Metrics for collecting the number of valid peers received
MilestonePeerMeter = metrics.NewRegisteredMeter("chain/milestone/isvalidpeer", nil)
)
// IsValidChain checks the validity of chain by comparing it
// against the local milestone entries
func (m *milestone) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
//Checking for the milestone flag
if !flags.Milestone {
return true, nil
}
m.finality.RLock()
defer m.finality.RUnlock()
var isValid bool = false
defer func() {
if isValid {
MilestoneChainMeter.Mark(int64(1))
} else {
MilestoneChainMeter.Mark(int64(-1))
}
}()
res, err := m.finality.IsValidChain(currentHeader, chain)
if !res {
isValid = false
return isValid, err
}
if m.Locked && !m.IsReorgAllowed(chain, m.LockedMilestoneNumber, m.LockedMilestoneHash) {
isValid = false
return isValid, nil
}
if !m.IsFutureMilestoneCompatible(chain) {
isValid = false
return isValid, nil
}
isValid = true
return isValid, nil
}
// 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 finality submitted to mainchain.
func (m *milestone) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
if !flags.Milestone {
return true, nil
}
res, err := m.finality.IsValidPeer(fetchHeadersByNumber)
if res {
MilestonePeerMeter.Mark(int64(1))
} else {
MilestonePeerMeter.Mark(int64(-1))
}
return res, err
}
func (m *milestone) Process(block uint64, hash common.Hash) {
m.finality.Lock()
defer m.finality.Unlock()
m.finality.Process(block, hash)
for i := 0; i < len(m.FutureMilestoneOrder); i++ {
if m.FutureMilestoneOrder[i] <= block {
m.dequeueFutureMilestone()
} else {
break
}
}
whitelistedMilestoneMeter.Update(int64(block))
m.UnlockSprint(block)
}
// This function will Lock the mutex at the time of voting
// fixme: get rid of it
func (m *milestone) LockMutex(endBlockNum uint64) bool {
m.finality.Lock()
if m.doExist && endBlockNum <= m.Number { //if endNum is less than whitelisted milestone, then we won't lock the sprint
log.Debug("endBlockNumber is less than or equal to latesMilestoneNumber", "endBlock Number", endBlockNum, "LatestMilestone Number", m.Number)
return false
}
if m.Locked && endBlockNum < m.LockedMilestoneNumber {
log.Debug("endBlockNum is less than locked milestone number", "endBlock Number", endBlockNum, "Locked Milestone Number", m.LockedMilestoneNumber)
return false
}
return true
}
// This function will unlock the mutex locked in LockMutex
// fixme: get rid of it
func (m *milestone) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
m.Locked = m.Locked || doLock
if doLock {
m.UnlockSprint(m.LockedMilestoneNumber)
m.Locked = true
m.LockedMilestoneHash = endBlockHash
m.LockedMilestoneNumber = endBlockNum
m.LockedMilestoneIDs[milestoneId] = struct{}{}
}
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
milestoneIDLength := int64(len(m.LockedMilestoneIDs))
MilestoneIdsLengthMeter.Update(milestoneIDLength)
m.finality.Unlock()
}
// This function will unlock the locked sprint
func (m *milestone) UnlockSprint(endBlockNum uint64) {
if endBlockNum < m.LockedMilestoneNumber {
return
}
m.Locked = false
m.purgeMilestoneIDsList()
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// This function will remove the stored milestoneID
func (m *milestone) RemoveMilestoneID(milestoneId string) {
m.finality.Lock()
delete(m.LockedMilestoneIDs, milestoneId)
if len(m.LockedMilestoneIDs) == 0 {
m.Locked = false
}
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
m.finality.Unlock()
}
// This will check whether the incoming chain matches the locked sprint hash
func (m *milestone) IsReorgAllowed(chain []*types.Header, lockedMilestoneNumber uint64, lockedMilestoneHash common.Hash) bool {
if chain[len(chain)-1].Number.Uint64() <= lockedMilestoneNumber { //Can't reorg if the end block of incoming
return false //chain is less than locked sprint number
}
for i := 0; i < len(chain); i++ {
if chain[i].Number.Uint64() == lockedMilestoneNumber {
return chain[i].Hash() == lockedMilestoneHash
}
}
return true
}
// This will return the list of milestoneIDs stored.
func (m *milestone) GetMilestoneIDsList() []string {
m.finality.RLock()
defer m.finality.RUnlock()
// fixme: use generics :)
keys := make([]string, 0, len(m.LockedMilestoneIDs))
for key := range m.LockedMilestoneIDs {
keys = append(keys, key)
}
return keys
}
// This is remove the milestoneIDs stored in the list.
func (m *milestone) purgeMilestoneIDsList() {
m.LockedMilestoneIDs = make(map[string]struct{})
}
func (m *milestone) IsFutureMilestoneCompatible(chain []*types.Header) bool {
//Tip of the received chain
chainTipNumber := chain[len(chain)-1].Number.Uint64()
for i := len(m.FutureMilestoneOrder) - 1; i >= 0; i-- {
//Finding out the highest future milestone number
//which is less or equal to received chain tip
if chainTipNumber >= m.FutureMilestoneOrder[i] {
//Looking for the received chain 's particular block number(matching future milestone number)
for j := len(chain) - 1; j >= 0; j-- {
if chain[j].Number.Uint64() == m.FutureMilestoneOrder[i] {
endBlockNum := m.FutureMilestoneOrder[i]
endBlockHash := m.FutureMilestoneList[endBlockNum]
//Checking the received chain matches with future milestone
return chain[j].Hash() == endBlockHash
}
}
}
}
return true
}
func (m *milestone) ProcessFutureMilestone(num uint64, hash common.Hash) {
if len(m.FutureMilestoneOrder) < m.MaxCapacity {
m.enqueueFutureMilestone(num, hash)
}
if num < m.LockedMilestoneNumber {
return
}
m.Locked = false
m.purgeMilestoneIDsList()
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// EnqueueFutureMilestone add the future milestone to the list
func (m *milestone) enqueueFutureMilestone(key uint64, hash common.Hash) {
if _, ok := m.FutureMilestoneList[key]; ok {
log.Debug("Future milestone already exist", "endBlockNumber", key, "futureMilestoneHash", hash)
return
}
log.Debug("Enqueing new future milestone", "endBlockNumber", key, "futureMilestoneHash", hash)
m.FutureMilestoneList[key] = hash
m.FutureMilestoneOrder = append(m.FutureMilestoneOrder, key)
err := rawdb.WriteFutureMilestoneList(m.db, m.FutureMilestoneOrder, m.FutureMilestoneList)
if err != nil {
log.Error("Error in writing future milestone data to db", "err", err)
}
FutureMilestoneMeter.Update(int64(key))
}
// DequeueFutureMilestone remove the future milestone entry from the list.
func (m *milestone) dequeueFutureMilestone() {
delete(m.FutureMilestoneList, m.FutureMilestoneOrder[0])
m.FutureMilestoneOrder = m.FutureMilestoneOrder[1:]
err := rawdb.WriteFutureMilestoneList(m.db, m.FutureMilestoneOrder, m.FutureMilestoneList)
if err != nil {
log.Error("Error in writing future milestone data to db", "err", err)
}
}

View file

@ -3,135 +3,150 @@ package whitelist
import (
"errors"
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/ethdb"
)
// Checkpoint whitelist
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 // Max capacity of the whitelist
checkpointInterval uint64 // Checkpoint interval, until which we can allow importing
}
func NewService(maxCapacity uint) *Service {
return &Service{
checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{},
maxCapacity: maxCapacity,
checkpointInterval: 256, // TODO: make it configurable through params?
}
}
var (
ErrMismatch = errors.New("mismatch error")
ErrNoRemote = errors.New("remote peer doesn't have a target block number")
ErrCheckpointMismatch = errors.New("checkpoint mismatch")
ErrLongFutureChain = errors.New("received future chain of unacceptable length")
ErrNoRemoteCheckpoint = errors.New("remote peer doesn't have a checkpoint")
)
// 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) 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.
//
// Check for availaibility of the last checkpointed block.
// This can be also be empty if our heimdall is not responding
// or we're running without it.
if len(w.checkpointWhitelist) == 0 {
// worst case, we don't have the checkpoints in memory
return true, nil
}
// Fetch the last checkpoint entry
lastCheckpointBlockNum := w.checkpointOrder[len(w.checkpointOrder)-1]
lastCheckpointBlockHash := w.checkpointWhitelist[lastCheckpointBlockNum]
// todo: we can extract this as an interface and mock as well or just test IsValidChain in isolation from downloader passing fake fetchHeadersByNumber functions
headers, hashes, err := fetchHeadersByNumber(lastCheckpointBlockNum, 1, 0, false)
if err != nil {
return false, fmt.Errorf("%w: last checkpoint %d, err %v", ErrNoRemoteCheckpoint, lastCheckpointBlockNum, err)
}
if len(headers) == 0 {
return false, fmt.Errorf("%w: last checkpoint %d", ErrNoRemoteCheckpoint, lastCheckpointBlockNum)
}
reqBlockNum := headers[0].Number.Uint64()
reqBlockHash := hashes[0]
// Check against the checkpointed blocks
if reqBlockNum == lastCheckpointBlockNum && reqBlockHash == lastCheckpointBlockHash {
return true, nil
}
return false, ErrCheckpointMismatch
type Service struct {
checkpointService
milestoneService
}
// 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, error) {
// 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, nil
func NewService(db ethdb.Database) *Service {
var checkpointDoExist = true
checkpointNumber, checkpointHash, err := rawdb.ReadFinality[*rawdb.Checkpoint](db)
if err != nil {
checkpointDoExist = false
}
// Return if we've received empty chain
if len(chain) == 0 {
return false, nil
var milestoneDoExist = true
milestoneNumber, milestoneHash, err := rawdb.ReadFinality[*rawdb.Milestone](db)
if err != nil {
milestoneDoExist = 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, nil
locked, lockedMilestoneNumber, lockedMilestoneHash, lockedMilestoneIDs, err := rawdb.ReadLockField(db)
if err != nil || !locked {
locked = false
lockedMilestoneIDs = make(map[string]struct{})
}
// Split the chain into past and future chain
pastChain, _ := splitChain(current, chain)
order, list, err := rawdb.ReadFutureMilestoneList(db)
if err != nil {
order = make([]uint64, 0)
list = make(map[uint64]common.Hash)
}
// Note: Do not act on future chain and allow importing all kinds of future chains.
return &Service{
&checkpoint{
finality[*rawdb.Checkpoint]{
doExist: checkpointDoExist,
Number: checkpointNumber,
Hash: checkpointHash,
interval: 256,
db: db,
},
},
// 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
// }
&milestone{
finality: finality[*rawdb.Milestone]{
doExist: milestoneDoExist,
Number: milestoneNumber,
Hash: milestoneHash,
interval: 256,
db: db,
},
// Don't accept future chain of unacceptable length (from current block)
// if len(futureChain)+offset > int(w.checkpointInterval) {
// return false, ErrLongFutureChain
// }
Locked: locked,
LockedMilestoneNumber: lockedMilestoneNumber,
LockedMilestoneHash: lockedMilestoneHash,
LockedMilestoneIDs: lockedMilestoneIDs,
FutureMilestoneList: list,
FutureMilestoneOrder: order,
MaxCapacity: 10,
},
}
}
// 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()], nil
}
// 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 and last milestone voted in the heimdall
func (s *Service) IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error) {
checkpointBool, err := s.checkpointService.IsValidPeer(fetchHeadersByNumber)
if !checkpointBool {
return checkpointBool, err
}
milestoneBool, err := s.milestoneService.IsValidPeer(fetchHeadersByNumber)
if !milestoneBool {
return milestoneBool, err
}
return true, nil
}
func (s *Service) PurgeWhitelistedCheckpoint() {
s.checkpointService.Purge()
}
func (s *Service) PurgeWhitelistedMilestone() {
s.milestoneService.Purge()
}
func (s *Service) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
return s.checkpointService.Get()
}
func (s *Service) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
return s.milestoneService.Get()
}
func (s *Service) ProcessMilestone(endBlockNum uint64, endBlockHash common.Hash) {
s.milestoneService.Process(endBlockNum, endBlockHash)
}
func (s *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
s.checkpointService.Process(endBlockNum, endBlockHash)
}
func (s *Service) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
checkpointBool, err := s.checkpointService.IsValidChain(currentHeader, chain)
if !checkpointBool {
return checkpointBool, err
}
milestoneBool, err := s.milestoneService.IsValidChain(currentHeader, chain)
if !milestoneBool {
return milestoneBool, err
}
return true, nil
}
func (s *Service) GetMilestoneIDsList() []string {
return s.milestoneService.GetMilestoneIDsList()
}
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()
first = chain[0].Number.Uint64()
last = chain[len(chain)-1].Number.Uint64()
)
if current >= first {
@ -153,57 +168,68 @@ func splitChain(current uint64, chain []*types.Header) ([]*types.Header, []*type
return pastChain, futureChain
}
func (w *Service) ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash) {
w.m.Lock()
defer w.m.Unlock()
w.enqueueCheckpointWhitelist(endBlockNum, endBlockHash)
// If size of checkpoint whitelist map is greater than 10, remove the oldest entry.
if w.length() > int(w.maxCapacity) {
w.dequeueCheckpointWhitelist()
//nolint:unparam
func isValidChain(currentHeader *types.Header, chain []*types.Header, doExist bool, number uint64, hash common.Hash) (bool, error) {
// Check if we have milestone to validate incoming chain in memory
if !doExist {
// We don't have any entry, no additional validation will be possible
return true, nil
}
}
// GetCheckpointWhitelist returns the existing whitelisted
// entries of checkpoint of the form block number -> block hash.
func (w *Service) GetCheckpointWhitelist() map[uint64]common.Hash {
w.m.Lock()
defer w.m.Unlock()
current := currentHeader.Number.Uint64()
return w.checkpointWhitelist
}
// PurgeCheckpointWhitelist purges data from checkpoint whitelist map
func (w *Service) PurgeCheckpointWhitelist() {
w.m.Lock()
defer w.m.Unlock()
w.checkpointWhitelist = make(map[uint64]common.Hash)
w.checkpointOrder = make([]uint64, 0)
}
// EnqueueWhitelistBlock enqueues blockNumber, blockHash to the checkpoint whitelist map
func (w *Service) enqueueCheckpointWhitelist(key uint64, val common.Hash) {
if _, ok := w.checkpointWhitelist[key]; !ok {
log.Debug("Enqueing new checkpoint whitelist", "block number", key, "block hash", val)
w.checkpointWhitelist[key] = val
w.checkpointOrder = append(w.checkpointOrder, key)
// Check if imported chain is less than whitelisted number
if chain[len(chain)-1].Number.Uint64() < number {
if current >= number { //If current tip of the chain is greater than whitelist number then return false
return false, nil
} else {
return true, nil
}
}
}
// DequeueWhitelistBlock dequeues block, blockhash from the checkpoint whitelist map
func (w *Service) dequeueCheckpointWhitelist() {
if len(w.checkpointOrder) > 0 {
log.Debug("Dequeing checkpoint whitelist", "block number", w.checkpointOrder[0], "block hash", w.checkpointWhitelist[w.checkpointOrder[0]])
// Split the chain into past and future chain
pastChain, _ := splitChain(current, chain)
delete(w.checkpointWhitelist, w.checkpointOrder[0])
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
// Iterate over the chain and validate against the last milestone
// It will handle all cases when the incoming chain has atleast one milestone
for i := len(pastChain) - 1; i >= 0; i-- {
if pastChain[i].Number.Uint64() == number {
res := pastChain[i].Hash() == hash
return res, nil
}
}
return true, nil
}
// length returns the len of the whitelist.
func (w *Service) length() int {
return len(w.checkpointWhitelist)
// FIXME: remoteHeader is not used
func isValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error), doExist bool, number uint64, hash common.Hash) (bool, error) {
// Check for availaibility of the last milestone block.
// This can be also be empty if our heimdall is not responding
// or we're running without it.
if !doExist {
// worst case, we don't have the milestone in memory
return true, nil
}
// todo: we can extract this as an interface and mock as well or just test IsValidChain in isolation from downloader passing fake fetchHeadersByNumber functions
headers, hashes, err := fetchHeadersByNumber(number, 1, 0, false)
if err != nil {
return false, fmt.Errorf("%w: last whitelisted block number %d, err %v", ErrNoRemote, number, err)
}
if len(headers) == 0 {
return false, fmt.Errorf("%w: last whitlisted block number %d", ErrNoRemote, number)
}
reqBlockNum := headers[0].Number.Uint64()
reqBlockHash := hashes[0]
// Check against the whitelisted blocks
if reqBlockNum == number && reqBlockHash == hash {
return true, nil
}
return false, ErrMismatch
}

View file

@ -1,3 +1,4 @@
// nolint
package whitelist
import (
@ -9,35 +10,243 @@ import (
"testing"
"time"
"pgregory.net/rapid"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
)
// NewMockService creates a new mock whitelist service
func NewMockService(maxCapacity uint, checkpointInterval uint64) *Service {
func NewMockService(db ethdb.Database) *Service {
return &Service{
checkpointWhitelist: make(map[uint64]common.Hash),
checkpointOrder: []uint64{},
maxCapacity: maxCapacity,
checkpointInterval: checkpointInterval,
&checkpoint{
finality[*rawdb.Checkpoint]{
doExist: false,
interval: 256,
db: db,
},
},
&milestone{
finality: finality[*rawdb.Milestone]{
doExist: false,
interval: 256,
db: db,
},
LockedMilestoneIDs: make(map[string]struct{}),
FutureMilestoneList: make(map[uint64]common.Hash),
FutureMilestoneOrder: make([]uint64, 0),
MaxCapacity: 10,
},
}
}
// TestWhitelistCheckpoint checks the checkpoint whitelist map queue mechanism
func TestWhitelistCheckpoint(t *testing.T) {
// TestWhitelistCheckpoint checks the checkpoint whitelist setter and getter functions.
func TestWhitelistedCheckpoint(t *testing.T) {
t.Parallel()
s := NewMockService(10, 10)
for i := 0; i < 10; i++ {
s.enqueueCheckpointWhitelist(uint64(i), common.Hash{})
}
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
db := rawdb.NewMemoryDatabase()
s.enqueueCheckpointWhitelist(11, common.Hash{})
s.dequeueCheckpointWhitelist()
require.Equal(t, s.length(), 10, "expected 10 items in whitelist")
//Creating the service for the whitelisting the checkpoints
s := NewMockService(db)
cp := s.checkpointService.(*checkpoint)
require.Equal(t, cp.doExist, false, "expected false as no cp exist at this point")
_, _, err := rawdb.ReadFinality[*rawdb.Checkpoint](db)
require.NotNil(t, err, "Error should be nil while reading from the db")
//Adding the checkpoint
s.ProcessCheckpoint(11, common.Hash{})
require.Equal(t, cp.doExist, true, "expected true as cp exist")
//Removing the checkpoint
s.PurgeWhitelistedCheckpoint()
require.Equal(t, cp.doExist, false, "expected false as no cp exist at this point")
//Adding the checkpoint
s.ProcessCheckpoint(12, common.Hash{1})
//Receiving the stored checkpoint
doExist, number, hash := s.GetWhitelistedCheckpoint()
//Validating the values received
require.Equal(t, doExist, true, "expected true ascheckpoint exist at this point")
require.Equal(t, number, uint64(12), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
require.NotEqual(t, hash, common.Hash{}, "expected the hash to be different from zero hash")
c1 := s.checkpointService.(*checkpoint)
fmt.Println("!!!-0", c1.doExist)
s.PurgeWhitelistedCheckpoint()
fmt.Println("!!!-1", c1.doExist)
doExist, number, hash = s.GetWhitelistedCheckpoint()
fmt.Println("!!!-2", c1.doExist)
//Validating the values received from the db, not memory
require.Equal(t, doExist, true, "expected true ascheckpoint exist at this point")
require.Equal(t, number, uint64(12), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
require.NotEqual(t, hash, common.Hash{}, "expected the hash to be different from zero hash")
checkpointNumber, checkpointHash, err := rawdb.ReadFinality[*rawdb.Checkpoint](db)
require.Nil(t, err, "Error should be nil while reading from the db")
require.Equal(t, checkpointHash, common.Hash{1}, "expected the 1 hash but got", hash)
require.Equal(t, checkpointNumber, uint64(12), "expected number to be 11 but got", number)
}
// TestMilestone checks the milestone whitelist setter and getter functions
func TestMilestone(t *testing.T) {
t.Parallel()
db := rawdb.NewMemoryDatabase()
s := NewMockService(db)
milestone := s.milestoneService.(*milestone)
//Checking for the variables when no milestone is Processed
require.Equal(t, milestone.doExist, false, "expected false as no milestone exist at this point")
require.Equal(t, milestone.Locked, false, "expected false as it was not locked")
require.Equal(t, milestone.LockedMilestoneNumber, uint64(0), "expected 0 as it was not initialized")
_, _, err := rawdb.ReadFinality[*rawdb.Milestone](db)
require.NotNil(t, err, "Error should be nil while reading from the db")
//Acquiring the mutex lock
milestone.LockMutex(11)
require.Equal(t, milestone.Locked, false, "expected false as sprint is not locked till this point")
//Releasing the mutex lock
milestone.UnlockMutex(true, "milestoneID1", uint64(11), common.Hash{})
require.Equal(t, milestone.LockedMilestoneNumber, uint64(11), "expected 11 as it was not initialized")
require.Equal(t, milestone.Locked, true, "expected true as sprint is locked now")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as only 1 milestoneID has been entered")
_, ok := milestone.LockedMilestoneIDs["milestoneID1"]
require.True(t, ok, "milestoneID1 should exist in the LockedMilestoneIDs map")
_, ok = milestone.LockedMilestoneIDs["milestoneID2"]
require.False(t, ok, "milestoneID2 shouldn't exist in the LockedMilestoneIDs map")
milestone.LockMutex(11)
milestone.UnlockMutex(true, "milestoneID2", uint64(11), common.Hash{})
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as only 1 milestoneID has been entered")
_, ok = milestone.LockedMilestoneIDs["milestoneID2"]
require.True(t, ok, "milestoneID2 should exist in the LockedMilestoneIDs map")
milestone.RemoveMilestoneID("milestoneID1")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as one out of two has been removed in previous step")
require.Equal(t, milestone.Locked, true, "expected true as sprint is locked now")
milestone.RemoveMilestoneID("milestoneID2")
require.Equal(t, len(milestone.LockedMilestoneIDs), 0, "expected 1 as both the milestonesIDs has been removed in previous step")
require.Equal(t, milestone.Locked, false, "expected false")
milestone.LockMutex(11)
milestone.UnlockMutex(true, "milestoneID3", uint64(11), common.Hash{})
require.True(t, milestone.Locked, "expected true")
require.Equal(t, milestone.LockedMilestoneNumber, uint64(11), "Expected 11")
milestone.LockMutex(15)
require.True(t, milestone.Locked, "expected true")
require.Equal(t, milestone.LockedMilestoneNumber, uint64(11), "Expected 11")
milestone.UnlockMutex(true, "milestoneID4", uint64(15), common.Hash{})
require.True(t, milestone.Locked, "expected true as final confirmation regarding the lock has been made")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as previous milestonesIDs has been removed in previous step")
//Adding the milestone
s.ProcessMilestone(11, common.Hash{})
require.True(t, milestone.Locked, "expected true as locked sprint is of number 15")
require.Equal(t, milestone.doExist, true, "expected true as milestone exist")
require.Equal(t, len(milestone.LockedMilestoneIDs), 1, "expected 1 as still last milestone of sprint number 15 exist")
//Reading from the Db
locked, lockedMilestoneNumber, lockedMilestoneHash, lockedMilestoneIDs, err := rawdb.ReadLockField(db)
require.Nil(t, err)
require.True(t, locked, "expected true as locked sprint is of number 15")
require.Equal(t, lockedMilestoneNumber, uint64(15), "Expected 15")
require.Equal(t, lockedMilestoneHash, common.Hash{}, "Expected", common.Hash{})
require.Equal(t, len(lockedMilestoneIDs), 1, "expected 1 as still last milestone of sprint number 15 exist")
_, ok = lockedMilestoneIDs["milestoneID4"]
require.True(t, ok, "expected true as milestoneIDList should contain 'milestoneID4'")
//Asking the lock for sprintNumber less than last whitelisted milestone
require.False(t, milestone.LockMutex(11), "Cant lock the sprintNumber less than equal to latest whitelisted milestone")
milestone.UnlockMutex(false, "", uint64(11), common.Hash{}) //Unlock is required after every lock to release the mutex
//Adding the milestone
s.ProcessMilestone(51, common.Hash{})
require.False(t, milestone.Locked, "expected false as lock from sprint number 15 is removed")
require.Equal(t, milestone.doExist, true, "expected true as milestone exist")
require.Equal(t, len(milestone.LockedMilestoneIDs), 0, "expected 0 as all the milestones have been removed")
//Reading from the Db
locked, _, _, lockedMilestoneIDs, err = rawdb.ReadLockField(db)
require.Nil(t, err)
require.False(t, locked, "expected true as locked sprint is of number 15")
require.Equal(t, len(lockedMilestoneIDs), 0, "expected 0 as milestoneID exist in the map")
//Removing the milestone
s.PurgeWhitelistedMilestone()
require.Equal(t, milestone.doExist, false, "expected false as no milestone exist at this point")
//Removing the milestone
s.ProcessMilestone(11, common.Hash{1})
doExist, number, hash := s.GetWhitelistedMilestone()
//validating the values received
require.Equal(t, doExist, true, "expected true as milestone exist at this point")
require.Equal(t, number, uint64(11), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
s.PurgeWhitelistedMilestone()
doExist, number, hash = s.GetWhitelistedMilestone()
//Validating the values received from the db, not memory
require.Equal(t, doExist, true, "expected true as milestone exist at this point")
require.Equal(t, number, uint64(11), "expected number to be 11 but got", number)
require.Equal(t, hash, common.Hash{1}, "expected the 1 hash but got", hash)
milestoneNumber, milestoneHash, err := rawdb.ReadFinality[*rawdb.Milestone](db)
require.Nil(t, err, "Error should be nil while reading from the db")
require.Equal(t, milestoneHash, common.Hash{1}, "expected the 1 hash but got", hash)
require.Equal(t, milestoneNumber, uint64(11), "expected number to be 11 but got", number)
_, _, err = rawdb.ReadFutureMilestoneList(db)
require.NotNil(t, err, "Error should be not nil")
s.ProcessFutureMilestone(16, common.Hash{16})
require.Equal(t, len(milestone.FutureMilestoneOrder), 1, "expected length is 1 as we added only 1 future milestone")
require.Equal(t, milestone.FutureMilestoneOrder[0], uint64(16), "expected value is 16 but got", milestone.FutureMilestoneOrder[0])
require.Equal(t, milestone.FutureMilestoneList[16], common.Hash{16}, "expected value is", common.Hash{16}.String()[2:], "but got", milestone.FutureMilestoneList[16])
order, list, err := rawdb.ReadFutureMilestoneList(db)
require.Nil(t, err, "Error should be nil while reading from the db")
require.Equal(t, len(order), 1, "expected the 1 hash but got", len(order))
require.Equal(t, order[0], uint64(16), "expected number to be 16 but got", order[0])
require.Equal(t, list[order[0]], common.Hash{16}, "expected value is", common.Hash{16}.String()[2:], "but got", list[order[0]])
capicity := milestone.MaxCapacity
for i := 16; i <= 16*(capicity+1); i = i + 16 {
s.ProcessFutureMilestone(uint64(i), common.Hash{16})
}
require.Equal(t, len(milestone.FutureMilestoneOrder), capicity, "expected length is", capicity)
require.Equal(t, milestone.FutureMilestoneOrder[capicity-1], uint64(16*capicity), "expected value is", uint64(16*capicity), "but got", milestone.FutureMilestoneOrder[capicity-1])
}
// TestIsValidPeer checks the IsValidPeer function in isolation
@ -45,18 +254,26 @@ func TestWhitelistCheckpoint(t *testing.T) {
func TestIsValidPeer(t *testing.T) {
t.Parallel()
s := NewMockService(10, 10)
db := rawdb.NewMemoryDatabase()
s := NewMockService(db)
// case1: no checkpoint whitelist, should consider the chain as valid
res, err := s.IsValidPeer(nil, nil)
res, err := s.IsValidPeer(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{})
// add checkpoint entry and mock fetchHeadersByNumber function
s.ProcessCheckpoint(uint64(1), common.Hash{})
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
// add milestone entry and mock fetchHeadersByNumber function
s.ProcessMilestone(uint64(1), common.Hash{})
checkpoint := s.checkpointService.(*checkpoint)
milestone := s.milestoneService.(*milestone)
//Check whether the milestone and checkpoint exist
require.Equal(t, checkpoint.doExist, true, "expected true as checkpoint exists")
require.Equal(t, milestone.doExist, true, "expected true as milestone exists")
// create a false function, returning absolutely nothing
falseFetchHeadersByNumber := func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error) {
@ -64,20 +281,19 @@ func TestIsValidPeer(t *testing.T) {
}
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
// and throw `ErrNoRemoteCheckpoint` error
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
// and throw `ErrNoRemoteCheckoint` error
res, err = s.IsValidPeer(falseFetchHeadersByNumber)
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, ErrNoRemoteCheckpoint) {
t.Fatalf("expected error ErrNoRemoteCheckpoint, got %v", err)
if !errors.Is(err, ErrNoRemote) {
t.Fatalf("expected error ErrNoRemote, got %v", err)
}
require.Equal(t, res, false, "expected chain to be invalid")
require.Equal(t, res, false, "expected peer chain to be invalid")
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
// create a mock function, returning a the required header
// create a mock function, returning the required header
fetchHeadersByNumber := func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
header := types.Header{Number: big.NewInt(0)}
@ -96,18 +312,124 @@ func TestIsValidPeer(t *testing.T) {
}
}
res, err = s.IsValidPeer(nil, fetchHeadersByNumber)
// case3: correct fetchHeadersByNumber function provided, should consider the chain as valid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// add one more checkpoint whitelist entry
// add checkpoint whitelist entry
s.ProcessCheckpoint(uint64(2), common.Hash{})
require.Equal(t, s.length(), 3, "expected 3 items in whitelist")
require.Equal(t, checkpoint.doExist, true, "expected true as checkpoint exists")
// 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.IsValidPeer(nil, fetchHeadersByNumber)
require.Equal(t, err, ErrCheckpointMismatch, "expected checkpoint mismatch error")
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
// create a mock function, returning the required header
fetchHeadersByNumber = func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
header := types.Header{Number: big.NewInt(0)}
switch number {
case 0:
return []*types.Header{&header}, []common.Hash{hash}, nil
case 1:
header.Number = big.NewInt(1)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 2:
header.Number = big.NewInt(2)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 3:
header.Number = big.NewInt(3)
hash3 := common.Hash{3}
return []*types.Header{&header}, []common.Hash{hash3}, nil
default:
return nil, nil, errors.New("invalid number")
}
}
s.ProcessMilestone(uint64(3), common.Hash{})
//Case5: correct fetchHeadersByNumber function provided with hash mismatch, should consider the chain as invalid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected milestone mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
s.ProcessMilestone(uint64(2), common.Hash{})
// create a mock function, returning the required header
fetchHeadersByNumber = func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
header := types.Header{Number: big.NewInt(0)}
switch number {
case 0:
return []*types.Header{&header}, []common.Hash{hash}, nil
case 1:
header.Number = big.NewInt(1)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 2:
header.Number = big.NewInt(2)
return []*types.Header{&header}, []common.Hash{hash}, nil
default:
return nil, nil, errors.New("invalid number")
}
}
// case6: correct fetchHeadersByNumber function provided, should consider the chain as valid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.NoError(t, err, "expected no error")
require.Equal(t, res, true, "expected chain to be valid")
// create a mock function, returning the required header
fetchHeadersByNumber = func(number uint64, _ int, _ int, _ bool) ([]*types.Header, []common.Hash, error) {
hash := common.Hash{}
hash3 := common.Hash{3}
header := types.Header{Number: big.NewInt(0)}
switch number {
case 0:
return []*types.Header{&header}, []common.Hash{hash}, nil
case 1:
header.Number = big.NewInt(1)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 2:
header.Number = big.NewInt(2)
return []*types.Header{&header}, []common.Hash{hash}, nil
case 3:
header.Number = big.NewInt(2) // sending wrong header for misamatch
return []*types.Header{&header}, []common.Hash{hash}, nil
case 4:
header.Number = big.NewInt(4) // sending wrong header for misamatch
return []*types.Header{&header}, []common.Hash{hash3}, nil
default:
return nil, nil, errors.New("invalid number")
}
}
//Add one more milestone in the list
s.ProcessMilestone(uint64(3), common.Hash{})
// case7: correct fetchHeadersByNumber function provided with wrong header for block 3, should consider the chain as invalid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected milestone mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
//require.Equal(t, milestone.length(), 3, "expected 3 items in milestoneList")
//Add one more milestone in the list
s.ProcessMilestone(uint64(4), common.Hash{})
// case8: correct fetchHeadersByNumber function provided with wrong hash for block 3, should consider the chain as valid
res, err = s.IsValidPeer(fetchHeadersByNumber)
require.Equal(t, err, ErrMismatch, "expected milestone mismatch error")
require.Equal(t, res, false, "expected chain to be invalid")
}
@ -116,71 +438,479 @@ func TestIsValidPeer(t *testing.T) {
func TestIsValidChain(t *testing.T) {
t.Parallel()
s := NewMockService(10, 10)
db := rawdb.NewMemoryDatabase()
s := NewMockService(db)
chainA := createMockChain(1, 20) // A1->A2...A19->A20
// case1: no checkpoint whitelist, should consider the chain as valid
//Case1: no checkpoint whitelist and no milestone and no locking, should consider the chain as valid
res, err := s.IsValidChain(nil, chainA)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
require.Nil(t, err)
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())
// add mock checkpoint entry
s.ProcessCheckpoint(tempChain[1].Number.Uint64(), tempChain[1].Hash())
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
//Make the mock chain with zero blocks
zeroChain := make([]*types.Header, 0)
// case2: We're behind the oldest whitelisted block entry, should consider
// the chain as valid as we're still far behind the latest blocks
//Case2: As input chain is of zero length,should consider the chain as invalid
res, err = s.IsValidChain(nil, zeroChain)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid", len(zeroChain))
//Case3A: As the received chain and current tip of local chain is behind the oldest whitelisted block entry, should consider
// the chain as valid
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
// Clear checkpoint whitelist and add blocks A5 and A15 in whitelist
s.PurgeCheckpointWhitelist()
s.ProcessCheckpoint(chainA[5].Number.Uint64(), chainA[5].Hash())
//Case3B: As the received chain is behind the oldest whitelisted block entry,but current tip is at par with whitelisted checkpoint, should consider
// the chain as invalid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid ")
// add mock milestone entry
s.ProcessMilestone(tempChain[1].Number.Uint64(), tempChain[1].Hash())
//Case4A: As the received chain and current tip of local chain is behind the oldest whitelisted block entry, should consider
// the chain as valid
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
//Case4B: As the received chain is behind the oldest whitelisted block entry and but current tip is at par with whitelisted milestine, should consider
// the chain as invalid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
//Remove the whitelisted checkpoint
s.PurgeWhitelistedCheckpoint()
//Case5: As the received chain is still invalid after removing the checkpoint as it is
//still behind the whitelisted milestone
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
//Remove the whitelisted milestone
s.PurgeWhitelistedMilestone()
//At this stage there is no whitelisted milestone and checkpoint
checkpoint := s.checkpointService.(*checkpoint)
milestone := s.milestoneService.(*milestone)
//Locking for sprintNumber 15
milestone.LockMutex(chainA[len(chainA)-5].Number.Uint64())
milestone.UnlockMutex(true, "MilestoneID1", chainA[len(chainA)-5].Number.Uint64(), chainA[len(chainA)-5].Hash())
//Case6: As the received chain is valid as the locked sprintHash matches with the incoming chain.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid as incoming chain matches with the locked value ")
hash3 := common.Hash{3}
//Locking for sprintNumber 16 with different hash
milestone.LockMutex(chainA[len(chainA)-4].Number.Uint64())
milestone.UnlockMutex(true, "MilestoneID2", chainA[len(chainA)-4].Number.Uint64(), hash3)
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as incoming chain does match with the locked value hash ")
//Locking for sprintNumber 19
milestone.LockMutex(chainA[len(chainA)-1].Number.Uint64())
milestone.UnlockMutex(true, "MilestoneID1", chainA[len(chainA)-1].Number.Uint64(), chainA[len(chainA)-1].Hash())
//Case7: As the received chain is valid as the locked sprintHash matches with the incoming chain.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as incoming chain is less than the locked value ")
//Locking for sprintNumber 19
milestone.LockMutex(uint64(21))
milestone.UnlockMutex(true, "MilestoneID1", uint64(21), hash3)
//Case8: As the received chain is invalid as the locked sprintHash matches is ahead of incoming chain.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as incoming chain is less than the locked value ")
//Unlocking the sprint
milestone.UnlockSprint(uint64(21))
// Clear checkpoint whitelist and add block A15 in whitelist
s.PurgeWhitelistedCheckpoint()
s.ProcessCheckpoint(chainA[15].Number.Uint64(), chainA[15].Hash())
require.Equal(t, s.length(), 2, "expected 2 items in whitelist")
require.Equal(t, checkpoint.doExist, true, "expected true as checkpoint exists.")
// case3: Try importing a past chain having valid checkpoint, should
// consider the chain as valid
// case9: As the received chain is having valid checkpoint,should consider the chain as valid.
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
// add mock milestone entries
s.ProcessMilestone(tempChain[1].Number.Uint64(), tempChain[1].Hash())
// case10: Try importing a past chain having valid checkpoint, should
// consider the chain as invalid as still lastest milestone is ahead of the chain.
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
// add mock milestone entries
s.ProcessMilestone(chainA[19].Number.Uint64(), chainA[19].Hash())
// case12: Try importing a chain having valid checkpoint and milestone, should
// consider the chain as valid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be invalid")
// add mock milestone entries
s.ProcessMilestone(chainA[19].Number.Uint64(), chainA[19].Hash())
// case13: Try importing a past chain having valid checkpoint and milestone, should
// consider the chain as valid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
// add mock milestone entries with wrong hash
s.ProcessMilestone(chainA[19].Number.Uint64(), chainA[18].Hash())
// case14: Try importing a past chain having valid checkpoint and milestone with wrong hash, should
// consider the chain as invalid
res, err = s.IsValidChain(chainA[len(chainA)-1], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid as hash mismatches")
// Clear milestone and add blocks A15 in whitelist
s.ProcessMilestone(chainA[15].Number.Uint64(), chainA[15].Hash())
// case16: Try importing a past chain having valid checkpoint, should
// consider the chain as valid
res, err = s.IsValidChain(tempChain[1], chainA)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
// Clear checkpoint whitelist and mock blocks in whitelist
tempChain = createMockChain(20, 20) // A20
s.PurgeCheckpointWhitelist()
s.PurgeWhitelistedCheckpoint()
s.ProcessCheckpoint(tempChain[0].Number.Uint64(), tempChain[0].Hash())
require.Equal(t, s.length(), 1, "expected 1 items in whitelist")
require.Equal(t, checkpoint.doExist, true, "expected true")
// case4: Try importing a past chain having invalid checkpoint
res, _ = s.IsValidChain(chainA[len(chainA)-1], chainA)
// case17: Try importing a past chain having invalid checkpoint,should consider the chain as invalid
res, err = s.IsValidChain(tempChain[0], chainA)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
// Not checking error here because we return nil in case of checkpoint mismatch
// case18: Try importing a future chain but within interval, should consider the chain as valid
res, err = s.IsValidChain(tempChain[len(tempChain)-1], tempChain)
require.Nil(t, err)
require.Equal(t, res, true, "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 (1)
res, err = s.IsValidChain(chainA[len(chainA)-1], chainB)
// case19: Try importing a future chain of acceptable length,should consider the chain as valid
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
// create a future chain to be imported of length > `checkpointInterval`
chainB = createMockChain(21, 40) // C21->C22...C39->C40
s.PurgeWhitelistedCheckpoint()
s.PurgeWhitelistedMilestone()
// Note: Earlier, it used to reject future chains longer than some threshold.
// That check is removed for now.
chainB = createMockChain(21, 29) // C21->C22....C29
// case6: Try importing a future chain (2)
res, err = s.IsValidChain(chainA[len(chainA)-1], chainB)
s.milestoneService.ProcessFutureMilestone(29, chainB[8].Hash())
// case20: Try importing a future chain which match the future milestone should the chain as valid
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
require.Equal(t, err, nil, "expected error to be nil")
chainB = createMockChain(21, 27) // C21->C22...C39->C40...C->256
// case21: Try importing a chain whose end point is less than future milestone
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be valid")
chainB = createMockChain(30, 39) // C21->C22...C39->C40...C->256
//Processing wrong hash
s.milestoneService.ProcessFutureMilestone(38, chainB[9].Hash())
// case22: Try importing a future chain with mismatch future milestone
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, false, "expected chain to be invalid")
chainB = createMockChain(40, 49) // C40->C41...C48->C49
// case23: Try importing a future chain whose starting point is ahead of latest future milestone
res, err = s.IsValidChain(tempChain[0], chainB)
require.Nil(t, err)
require.Equal(t, res, true, "expected chain to be invalid")
}
func TestPropertyBasedTestingMilestone(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
db := rawdb.NewMemoryDatabase()
milestone := milestone{
finality: finality[*rawdb.Milestone]{
doExist: false,
Number: 0,
Hash: common.Hash{},
interval: 256,
db: db,
},
Locked: false,
LockedMilestoneNumber: 0,
LockedMilestoneHash: common.Hash{},
LockedMilestoneIDs: make(map[string]struct{}),
FutureMilestoneList: make(map[uint64]common.Hash),
FutureMilestoneOrder: make([]uint64, 0),
MaxCapacity: 10,
}
var (
milestoneEndNum = rapid.Uint64().Draw(t, "endBlock")
milestoneID = rapid.String().Draw(t, "MilestoneID")
doLock = rapid.Bool().Draw(t, "Voted")
)
val := milestone.LockMutex(milestoneEndNum.(uint64))
if !val {
t.Error("LockMutex need to return true when there is no whitelisted milestone and locked milestone")
}
milestone.UnlockMutex(doLock.(bool), milestoneID.(string), milestoneEndNum.(uint64), common.Hash{})
if doLock.(bool) {
//Milestone should not be whitelisted
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
//Local chain should be locked
if !milestone.Locked {
t.Error("Milestone is expected to be locked at", milestoneEndNum.(uint64))
}
if milestone.LockedMilestoneNumber != milestoneEndNum.(uint64) {
t.Error("Locked milestone number is expected to be", milestoneEndNum.(uint64))
}
if len(milestone.LockedMilestoneIDs) != 1 {
t.Error("List should contain 1 milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID.(string)]
if !ok {
t.Error("List doesn't contain correct milestoneID")
}
}
if !doLock.(bool) {
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
if milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
if milestone.LockedMilestoneNumber != 0 {
t.Error("Locked milestone number is expected to be", 0)
}
if len(milestone.LockedMilestoneIDs) != 0 {
t.Error("List should not contain milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID.(string)]
if ok {
t.Error("List shouldn't contain any milestoneID")
}
}
fitlerFn := func(i uint64) bool {
if i <= uint64(1000) {
return true
}
return false
}
var (
start = rapid.Uint64Max(milestoneEndNum.(uint64)).Draw(t, "start for mock chain")
end = rapid.Uint64Min(start.(uint64)).Filter(fitlerFn).Draw(t, "end for mock chain")
)
chainTemp := createMockChain(start.(uint64), end.(uint64))
val, err := milestone.IsValidChain(chainTemp[0], chainTemp)
if err != nil {
t.Error("Error", err)
}
if doLock.(bool) && val {
t.Error("When the chain is locked at milestone, it should not pass IsValidChain for incompatible incoming chain")
}
if !doLock.(bool) && !val {
t.Error("When the chain is not locked at milestone, it should pass IsValidChain for incoming chain")
}
var (
milestoneEndNum2 = rapid.Uint64().Draw(t, "endBlockNum 2")
milestoneID2 = rapid.String().Draw(t, "MilestoneID 2")
doLock2 = rapid.Bool().Draw(t, "Voted 2")
)
val = milestone.LockMutex(milestoneEndNum2.(uint64))
if doLock.(bool) && milestoneEndNum.(uint64) > milestoneEndNum2.(uint64) && val {
t.Error("LockMutex need to return false as previous locked milestone is greater")
}
if doLock.(bool) && milestoneEndNum.(uint64) <= milestoneEndNum2.(uint64) && !val {
t.Error("LockMutex need to return true as previous locked milestone is less")
}
milestone.UnlockMutex(doLock2.(bool), milestoneID2.(string), milestoneEndNum2.(uint64), common.Hash{})
if doLock2.(bool) {
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
if !milestone.Locked {
t.Error("Milestone is expected to be locked at", milestoneEndNum2.(uint64))
}
if milestone.LockedMilestoneNumber != milestoneEndNum2.(uint64) {
t.Error("Locked milestone number is expected to be", milestoneEndNum.(uint64))
}
if len(milestone.LockedMilestoneIDs) != 1 {
t.Error("List should contain 1 milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID2.(string)]
if !ok {
t.Error("List doesn't contain correct milestoneID")
}
}
if !doLock2.(bool) {
if milestone.doExist {
t.Error("Milestone is not expected to be whitelisted")
}
if !doLock.(bool) && milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
if doLock.(bool) && !milestone.Locked {
t.Error("Milestone is expected to be locked at", milestoneEndNum.(uint64))
}
if !doLock.(bool) && milestone.LockedMilestoneNumber != 0 {
t.Error("Locked milestone number is expected to be", 0)
}
if doLock.(bool) && milestone.LockedMilestoneNumber != milestoneEndNum.(uint64) {
t.Error("Locked milestone number is expected to be", milestoneEndNum.(uint64))
}
if !doLock.(bool) && len(milestone.LockedMilestoneIDs) != 0 {
t.Error("List should not contain milestone")
}
if doLock.(bool) && len(milestone.LockedMilestoneIDs) != 1 {
t.Error("List should not contain milestone")
}
_, ok := milestone.LockedMilestoneIDs[milestoneID.(string)]
if !doLock.(bool) && ok {
t.Error("List shouldn't contain any milestoneID")
}
if doLock.(bool) && !ok {
t.Error("List should contain milestoneID")
}
}
var (
milestoneNum = rapid.Uint64().Draw(t, "milestone Number")
)
lockedValue := milestone.LockedMilestoneNumber
milestone.Process(milestoneNum.(uint64), common.Hash{})
isChainLocked := doLock.(bool) || doLock2.(bool)
if !milestone.doExist {
t.Error("Should have the whitelisted milestone")
}
if milestone.finality.Number != milestoneNum.(uint64) {
t.Error("Should have the whitelisted milestone", milestoneNum.(uint64))
}
if isChainLocked {
if milestoneNum.(uint64) < lockedValue {
if !milestone.Locked {
t.Error("Milestone is expected to be locked")
}
} else {
if milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
}
}
var (
futureMilestoneNum = rapid.Uint64Min(milestoneNum.(uint64)).Draw(t, "future milestone Number")
)
isChainLocked = milestone.Locked
milestone.ProcessFutureMilestone(futureMilestoneNum.(uint64), common.Hash{})
if isChainLocked {
if futureMilestoneNum.(uint64) < lockedValue {
if !milestone.Locked {
t.Error("Milestone is expected to be locked")
}
} else {
if milestone.Locked {
t.Error("Milestone is expected not to be locked")
}
}
}
})
}
func TestSplitChain(t *testing.T) {
@ -384,11 +1114,12 @@ func TestSplitChainProperties(t *testing.T) {
// 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)
i uint64
idx uint64
)
chain := make([]*types.Header, end-start+1)
for i = start; i <= end; i++ {
header := &types.Header{
Number: big.NewInt(int64(i)),

View file

@ -170,6 +170,12 @@ type Config struct {
DatabaseCache int
DatabaseFreezer string
// Database - LevelDB options
LevelDbCompactionTableSize uint64
LevelDbCompactionTableSizeMultiplier float64
LevelDbCompactionTotalSize uint64
LevelDbCompactionTotalSizeMultiplier float64
TrieCleanCache int
TrieCleanCacheJournal string `toml:",omitempty"` // Disk journal directory for trie cache to survive node restarts
TrieCleanCacheRejournal time.Duration `toml:",omitempty"` // Time interval to regenerate the journal for clean cache

View file

@ -174,6 +174,21 @@ func (mr *MockBackendMockRecorder) GetReceipts(arg0, arg1 interface{}) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReceipts", reflect.TypeOf((*MockBackend)(nil).GetReceipts), arg0, arg1)
}
// GetVoteOnHash mocks base method.
func (m *MockBackend) GetVoteOnHash(arg0 context.Context, arg1, arg2 uint64, arg3, arg4 string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetVoteOnHash", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetVoteOnHash indicates an expected call of GetVoteOnHash.
func (mr *MockBackendMockRecorder) GetVoteOnHash(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVoteOnHash", reflect.TypeOf((*MockBackend)(nil).GetVoteOnHash), arg0, arg1, arg2, arg3, arg4)
}
// HeaderByHash mocks base method.
func (m *MockBackend) HeaderByHash(arg0 context.Context, arg1 common.Hash) (*types.Header, error) {
m.ctrl.T.Helper()

View file

@ -68,7 +68,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
b.Log("Running bloombits benchmark section size:", sectionSize)
db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
if err != nil {
b.Fatalf("error opening database at %v: %v", benchDataDir, err)
}
@ -145,7 +145,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
for i := 0; i < benchFilterCnt; i++ {
if i%20 == 0 {
db.Close()
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
backend = &testBackend{db: db, sections: cnt}
sys = NewFilterSystem(backend, Config{})
}
@ -187,7 +187,7 @@ func BenchmarkNoBloomBits(b *testing.B) {
b.Log("Running benchmark without bloombits")
db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
if err != nil {
b.Fatalf("error opening database at %v: %v", benchDataDir, err)
}

View file

@ -491,6 +491,8 @@ func TestInvalidGetLogsRequest(t *testing.T) {
blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
)
api.SetChainConfig(params.BorUnittestChainConfig)
// Reason: Cannot specify both BlockHash and FromBlock/ToBlock)
testCases := []FilterCriteria{
0: {BlockHash: &blockHash, FromBlock: big.NewInt(100)},
@ -808,6 +810,7 @@ func TestPendingLogsSubscription(t *testing.T) {
<-testCases[i].sub.Err()
}
}
// nolint:gocognit
func TestLightFilterLogs(t *testing.T) {
t.Parallel()

View file

@ -43,7 +43,7 @@ func makeReceipt(addr common.Address) *types.Receipt {
func BenchmarkFilters(b *testing.B) {
var (
db, _ = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false)
db, _ = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false, rawdb.ExtraDBConfig{})
_, sys = newTestFilterSystem(b, db, Config{})
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
@ -106,7 +106,7 @@ func BenchmarkFilters(b *testing.B) {
func TestFilters(t *testing.T) {
var (
db, _ = rawdb.NewLevelDBDatabase(t.TempDir(), 0, 0, "", false)
db, _ = rawdb.NewLevelDBDatabase(t.TempDir(), 0, 0, "", false, rawdb.ExtraDBConfig{})
_, sys = newTestFilterSystem(t, db, Config{})
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key1.PublicKey)

View file

@ -103,6 +103,10 @@ func (b *TestBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.
return nil, nil
}
func (b *TestBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
return false, nil
}
func (b *TestBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
receipts := rawdb.ReadReceipts(b.DB, hash, number, params.TestChainConfig)

View file

@ -10,94 +10,112 @@ import (
)
var (
// errCheckpointCount is returned when we are unable to fetch
// the checkpoint count from local heimdall.
errCheckpointCount = errors.New("failed to fetch checkpoint count")
// errNoCheckpoint is returned when there is not checkpoint proposed
// by heimdall yet or heimdall is not in sync
errNoCheckpoint = errors.New("no checkpoint proposed")
// errCheckpoint is returned when we are unable to fetch the
// latest checkpoint from the local heimdall.
errCheckpoint = errors.New("failed to fetch latest checkpoint")
// errMissingCheckpoint is returned when we don't have the
// checkpoint blocks locally, yet.
errMissingCheckpoint = errors.New("missing checkpoint blocks")
// errMilestone is returned when we are unable to fetch the
// latest milestone from the local heimdall.
errMilestone = errors.New("failed to fetch latest milestone")
// errRootHash is returned when we aren't able to calculate the root hash
// locally for a range of blocks.
errRootHash = errors.New("failed to get local root hash")
// errCheckpointRootHashMismatch is returned when the local root hash
// doesn't match with the root hash in checkpoint.
errCheckpointRootHashMismatch = errors.New("checkpoint roothash mismatch")
// errEndBlock is returned when we're unable to fetch a block locally.
errEndBlock = errors.New("failed to get end block")
ErrNotInRejectedList = errors.New("MilestoneID not in rejected list")
)
// fetchWhitelistCheckpoints fetches the latest checkpoint/s from it's local heimdall
// fetchWhitelistCheckpoint fetches the latest checkpoint from it's local heimdall
// and verifies the data against bor data.
func (h *ethHandler) fetchWhitelistCheckpoints(ctx context.Context, bor *bor.Bor, checkpointVerifier *checkpointVerifier, first bool) ([]uint64, []common.Hash, error) {
// Create an array for block number and block hashes
//nolint:prealloc
func (h *ethHandler) fetchWhitelistCheckpoint(ctx context.Context, bor *bor.Bor, eth *Ethereum, verifier *borVerifier) (uint64, common.Hash, error) {
var (
blockNums []uint64 = make([]uint64, 0)
blockHashes []common.Hash = make([]common.Hash, 0)
blockNum uint64
blockHash common.Hash
)
// Fetch the checkpoint count from heimdall
count, err := bor.HeimdallClient.FetchCheckpointCount(ctx)
// fetch the latest checkpoint from Heimdall
checkpoint, err := bor.HeimdallClient.FetchCheckpoint(ctx, -1)
if err != nil {
log.Debug("Failed to fetch checkpoint count for whitelisting", "err", err)
return blockNums, blockHashes, errCheckpointCount
log.Debug("Failed to fetch latest checkpoint for whitelisting", "err", err)
return blockNum, blockHash, errCheckpoint
}
if count == 0 {
return blockNums, blockHashes, errNoCheckpoint
log.Info("Got new checkpoint from heimdall", "start", checkpoint.StartBlock.Uint64(), "end", checkpoint.EndBlock.Uint64(), "rootHash", checkpoint.RootHash.String())
// Verify if the checkpoint fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the checkpoint. If not,
// it will return appropriate error.
hash, err := verifier.verify(ctx, eth, h, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64(), checkpoint.RootHash.String()[2:], true)
if err != nil {
log.Warn("Failed to whitelist checkpoint", "err", err)
return blockNum, blockHash, err
}
blockNum = checkpoint.EndBlock.Uint64()
blockHash = common.HexToHash(hash)
return blockNum, blockHash, nil
}
// fetchWhitelistMilestone fetches the latest milestone from it's local heimdall
// and verifies the data against bor data.
func (h *ethHandler) fetchWhitelistMilestone(ctx context.Context, bor *bor.Bor, eth *Ethereum, verifier *borVerifier) (uint64, common.Hash, error) {
var (
start int64
end int64
num uint64
hash common.Hash
)
// Prepare the checkpoint range to fetch
if count <= 10 {
start = 1
} else {
start = count - 10 + 1 // 10 is the max number of checkpoints to fetch
// fetch latest milestone
milestone, err := bor.HeimdallClient.FetchMilestone(ctx)
if err != nil {
log.Error("Failed to fetch latest milestone for whitelisting", "err", err)
return num, hash, errMilestone
}
end = count
num = milestone.EndBlock.Uint64()
hash = milestone.Hash
// If we're in not in the first iteration, only fetch the latest checkpoint
if !first {
start = count
log.Info("Got new milestone from heimdall", "start", milestone.StartBlock.Uint64(), "end", milestone.EndBlock.Uint64(), "hash", milestone.Hash.String())
// Verify if the milestone fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the milestone. If not,
// it will return appropriate error.
_, err = verifier.verify(ctx, eth, h, milestone.StartBlock.Uint64(), milestone.EndBlock.Uint64(), milestone.Hash.String()[2:], false)
if err != nil {
h.downloader.UnlockSprint(milestone.EndBlock.Uint64())
return num, hash, err
}
for i := start; i <= end; i++ {
// fetch `i` indexed checkpoint from heimdall
checkpoint, err := bor.HeimdallClient.FetchCheckpoint(ctx, i)
if err != nil {
log.Debug("Failed to fetch latest checkpoint for whitelisting", "err", err)
return blockNums, blockHashes, errCheckpoint
}
// Verify if the checkpoint fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the checkpoint. If not,
// it will return appropriate error.
hash, err := checkpointVerifier.verify(ctx, h, checkpoint)
if err != nil {
return blockNums, blockHashes, err
}
blockNums = append(blockNums, checkpoint.EndBlock.Uint64())
blockHashes = append(blockHashes, common.HexToHash(hash))
}
return blockNums, blockHashes, nil
return num, hash, nil
}
func (h *ethHandler) fetchNoAckMilestone(ctx context.Context, bor *bor.Bor) (string, error) {
var (
milestoneID string
)
// fetch latest milestone
milestoneID, err := bor.HeimdallClient.FetchLastNoAckMilestone(ctx)
if err != nil {
log.Error("Failed to fetch latest no-ack milestone", "err", err)
return milestoneID, errMilestone
}
return milestoneID, nil
}
func (h *ethHandler) fetchNoAckMilestoneByID(ctx context.Context, bor *bor.Bor, milestoneID string) error {
// fetch latest milestone
err := bor.HeimdallClient.FetchNoAckMilestone(ctx, milestoneID)
// fixme: handle different types of errors
if errors.Is(err, ErrNotInRejectedList) {
log.Warn("MilestoneID not in rejected list", "milestoneID", milestoneID, "err", err)
return err
}
if err != nil {
log.Error("Failed to fetch no-ack milestone by ID ", "milestoneID", milestoneID, "err", err)
return errMilestone
}
return nil
}

View file

@ -12,12 +12,17 @@ import (
"github.com/ethereum/go-ethereum/consensus/bor"
"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/milestone"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
)
type mockHeimdall struct {
fetchCheckpoint func(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
fetchCheckpointCount func(ctx context.Context) (int64, error)
fetchCheckpoint func(ctx context.Context, number int64) (*checkpoint.Checkpoint, error)
fetchCheckpointCount func(ctx context.Context) (int64, error)
fetchMilestone func(ctx context.Context) (*milestone.Milestone, error)
fetchMilestoneCount func(ctx context.Context) (int64, error)
fetchNoAckMilestone func(ctx context.Context, milestoneID string) error
fetchLastNoAckMilestone func(ctx context.Context) (string, error)
}
func (m *mockHeimdall) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) {
@ -33,78 +38,112 @@ func (m *mockHeimdall) FetchCheckpoint(ctx context.Context, number int64) (*chec
func (m *mockHeimdall) FetchCheckpointCount(ctx context.Context) (int64, error) {
return m.fetchCheckpointCount(ctx)
}
func (m *mockHeimdall) FetchMilestone(ctx context.Context) (*milestone.Milestone, error) {
return m.fetchMilestone(ctx)
}
func (m *mockHeimdall) FetchMilestoneCount(ctx context.Context) (int64, error) {
return m.fetchMilestoneCount(ctx)
}
func (m *mockHeimdall) FetchNoAckMilestone(ctx context.Context, milestoneID string) error {
return m.fetchNoAckMilestone(ctx, milestoneID)
}
func (m *mockHeimdall) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
return m.fetchLastNoAckMilestone(ctx)
}
func (m *mockHeimdall) FetchMilestoneID(ctx context.Context, milestoneID string) error {
return m.fetchNoAckMilestone(ctx, milestoneID)
}
func (m *mockHeimdall) Close() {}
func TestFetchWhitelistCheckpoints(t *testing.T) {
func TestFetchWhitelistCheckpointAndMilestone(t *testing.T) {
t.Parallel()
// create an empty ethHandler
handler := &ethHandler{}
// create a mock checkpoint verification function and use it to create a verifier
verify := func(ctx context.Context, handler *ethHandler, checkpoint *checkpoint.Checkpoint) (string, error) {
verify := func(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error) {
return "", nil
}
verifier := newCheckpointVerifier(verify)
verifier := newBorVerifier()
verifier.setVerify(verify)
// Create a mock heimdall instance and use it for creating a bor instance
var heimdall mockHeimdall
bor := &bor.Bor{HeimdallClient: &heimdall}
// create 20 mock checkpoints
checkpoints := createMockCheckpoints(20)
fetchCheckpointTest(t, &heimdall, bor, handler, verifier)
fetchMilestoneTest(t, &heimdall, bor, handler, verifier)
}
func (b *borVerifier) setVerify(verifyFn func(ctx context.Context, eth *Ethereum, handler *ethHandler, start uint64, end uint64, hash string, isCheckpoint bool) (string, error)) {
b.verify = verifyFn
}
func fetchCheckpointTest(t *testing.T, heimdall *mockHeimdall, bor *bor.Bor, handler *ethHandler, verifier *borVerifier) {
t.Helper()
var checkpoints []*checkpoint.Checkpoint
// create a mock fetch checkpoint function
heimdall.fetchCheckpoint = func(_ context.Context, number int64) (*checkpoint.Checkpoint, error) {
return checkpoints[number-1], nil // we're sure that number won't exceed 20
if len(checkpoints) == 0 {
return nil, errCheckpoint
} else if number == -1 {
return checkpoints[len(checkpoints)-1], nil
} else {
return checkpoints[number-1], nil
}
}
// create a background context
ctx := context.Background()
testCases := []struct {
name string
first bool
count int64
length int
start uint64
end uint64
fetchErr error
expectedErr error
}{
{"fail to fetch checkpoint count", false, 0, 0, 0, 0, errCheckpointCount, errCheckpointCount},
{"no checkpoints available", false, 0, 0, 0, 0, nil, errNoCheckpoint},
{"fetch multiple checkpoints (count < 10)", true, 6, 6, 0, 6, nil, nil},
{"fetch multiple checkpoints (count = 10)", true, 10, 10, 0, 10, nil, nil},
{"fetch multiple checkpoints (count > 10)", true, 16, 10, 6, 16, nil, nil},
{"fetch single checkpoint", false, 18, 1, 17, 18, nil, nil},
}
_, _, err := handler.fetchWhitelistCheckpoint(ctx, bor, nil, verifier)
require.Equal(t, err, errCheckpoint)
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// create 4 mock checkpoints
checkpoints = createMockCheckpoints(4)
heimdall.fetchCheckpointCount = getMockFetchCheckpointFn(tc.count, tc.fetchErr)
blockNums, blockHashes, err := handler.fetchWhitelistCheckpoints(ctx, bor, verifier, tc.first)
blockNum, blockHash, err := handler.fetchWhitelistCheckpoint(ctx, bor, nil, verifier)
// Check if we have expected result
require.Equal(t, tc.expectedErr, err)
require.Equal(t, tc.length, len(blockNums))
require.Equal(t, tc.length, len(blockHashes))
validateBlockNumber(t, blockNums, checkpoints[tc.start:tc.end])
})
}
// Check if we have expected result
require.Equal(t, err, nil)
require.Equal(t, checkpoints[len(checkpoints)-1].EndBlock.Uint64(), blockNum)
require.Equal(t, checkpoints[len(checkpoints)-1].RootHash, blockHash)
}
func validateBlockNumber(t *testing.T, blockNums []uint64, checkpoints []*checkpoint.Checkpoint) {
func fetchMilestoneTest(t *testing.T, heimdall *mockHeimdall, bor *bor.Bor, handler *ethHandler, verifier *borVerifier) {
t.Helper()
for i, blockNum := range blockNums {
require.Equal(t, blockNum, checkpoints[i].EndBlock.Uint64(), "expect block number in array to match with checkpoint")
var milestones []*milestone.Milestone
// create a mock fetch checkpoint function
heimdall.fetchMilestone = func(_ context.Context) (*milestone.Milestone, error) {
if len(milestones) == 0 {
return nil, errMilestone
} else {
return milestones[len(milestones)-1], nil
}
}
// create a background context
ctx := context.Background()
_, _, err := handler.fetchWhitelistMilestone(ctx, bor, nil, verifier)
require.Equal(t, err, errMilestone)
// create 4 mock checkpoints
milestones = createMockMilestones(4)
num, hash, err := handler.fetchWhitelistMilestone(ctx, bor, nil, verifier)
// Check if we have expected result
require.Equal(t, err, nil)
require.Equal(t, milestones[len(milestones)-1].EndBlock.Uint64(), num)
require.Equal(t, milestones[len(milestones)-1].Hash, hash)
}
func getMockFetchCheckpointFn(number int64, err error) func(ctx context.Context) (int64, error) {
@ -133,3 +172,24 @@ func createMockCheckpoints(count int) []*checkpoint.Checkpoint {
return checkpoints
}
func createMockMilestones(count int) []*milestone.Milestone {
var (
milestones []*milestone.Milestone = make([]*milestone.Milestone, count)
startBlock int64 = 257 // any number can be used
)
for i := 0; i < count; i++ {
milestones[i] = &milestone.Milestone{
Proposer: common.Address{},
StartBlock: big.NewInt(startBlock),
EndBlock: big.NewInt(startBlock + 255),
Hash: common.Hash{},
BorChainID: "137",
Timestamp: uint64(time.Now().Unix()),
}
startBlock += 256
}
return milestones
}

View file

@ -84,7 +84,10 @@ func (p *Peer) broadcastTransactions() {
)
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if tx := p.txpool.Get(queue[i]); tx != nil {
tx := p.txpool.Get(queue[i])
// Skip EIP-4337 bundled transactions
if tx != nil && tx.GetOptions() == nil {
txs = append(txs, tx)
size += common.StorageSize(tx.Size())
}
@ -158,7 +161,10 @@ func (p *Peer) announceTransactions() {
)
for count = 0; count < len(queue) && size < maxTxPacketSize; count++ {
if tx := p.txpool.Get(queue[count]); tx != nil {
tx := p.txpool.Get(queue[count])
// Skip EIP-4337 bundled transactions
if tx != nil && tx.GetOptions() == nil {
pending = append(pending, queue[count])
pendingTypes = append(pendingTypes, tx.Type())
pendingSizes = append(pendingSizes, uint32(tx.Size()))

View file

@ -11,13 +11,23 @@ import (
// GetRootHash returns the merkle root of the block headers
func (ec *Client) GetRootHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64) (string, error) {
var rootHash string
if err := ec.c.CallContext(ctx, &rootHash, "eth_getRootHash", startBlockNumber, endBlockNumber); err != nil {
if err := ec.c.CallContext(ctx, &rootHash, "bor_getRootHash", startBlockNumber, endBlockNumber); err != nil {
return "", err
}
return rootHash, nil
}
// GetRootHash returns the merkle root of the block headers
func (ec *Client) GetVoteOnHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64, hash string, milestoneID string) (bool, error) {
var value bool
if err := ec.c.CallContext(ctx, &value, "bor_getVoteOnHash", startBlockNumber, endBlockNumber, hash, milestoneID); err != nil {
return false, err
}
return value, nil
}
// GetBorBlockReceipt returns bor block receipt
func (ec *Client) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
var r *types.Receipt

View file

@ -83,9 +83,16 @@ type Database struct {
log log.Logger // Contextual logger tracking the database path
}
type LevelDBConfig struct {
CompactionTableSize uint64 // LevelDB SSTable/file size in mebibytes
CompactionTableSizeMultiplier float64 // Multiplier on LevelDB SSTable/file size
CompactionTotalSize uint64 // Total size in mebibytes of SSTables in a given LevelDB level
CompactionTotalSizeMultiplier float64 // Multiplier on level size on LevelDB levels
}
// New returns a wrapped LevelDB object. The namespace is the prefix that the
// metrics reporting should use for surfacing internal stats.
func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) {
func New(file string, cache int, handles int, namespace string, readonly bool, config LevelDBConfig) (*Database, error) {
return NewCustom(file, namespace, func(options *opt.Options) {
// Ensure we have some minimal caching and file guarantees
if cache < minCache {
@ -100,6 +107,22 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
options.BlockCacheCapacity = cache / 2 * opt.MiB
options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally
if config.CompactionTableSize != 0 {
options.CompactionTableSize = int(config.CompactionTableSize * opt.MiB)
}
if config.CompactionTableSizeMultiplier != 0 {
options.CompactionTableSizeMultiplier = config.CompactionTableSizeMultiplier
}
if config.CompactionTotalSize != 0 {
options.CompactionTotalSize = int(config.CompactionTotalSize * opt.MiB)
}
if config.CompactionTotalSizeMultiplier != 0 {
options.CompactionTotalSizeMultiplier = config.CompactionTotalSizeMultiplier
}
if readonly {
options.ReadOnly = true
}
@ -114,7 +137,14 @@ func NewCustom(file string, namespace string, customize func(options *opt.Option
logger := log.New("database", file)
usedCache := options.GetBlockCacheCapacity() + options.GetWriteBuffer()*2
logCtx := []interface{}{"cache", common.StorageSize(usedCache), "handles", options.GetOpenFilesCacheCapacity()}
logCtx := []interface{}{
"cache", common.StorageSize(usedCache),
"handles", options.GetOpenFilesCacheCapacity(),
"compactionTableSize", options.CompactionTableSize,
"compactionTableSizeMultiplier", options.CompactionTableSizeMultiplier,
"compactionTotalSize", options.CompactionTotalSize,
"compactionTotalSizeMultiplier", options.CompactionTotalSizeMultiplier}
if options.ReadOnly {
logCtx = append(logCtx, "readonly", "true")
}

62
go.mod
View file

@ -17,14 +17,14 @@ require (
github.com/cloudflare/cloudflare-go v0.14.0
github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811
github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c
github.com/cosmos/cosmos-sdk v0.37.4
github.com/cosmos/cosmos-sdk v0.46.2
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.1.0
github.com/docker/docker v1.6.2
github.com/docker/docker v24.0.5+incompatible
github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7
github.com/edsrzf/mmap-go v1.0.0
github.com/emirpasic/gods v1.18.1
github.com/fatih/color v1.9.0
github.com/fatih/color v1.13.0
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/fsnotify/fsnotify v1.6.0
@ -53,12 +53,13 @@ require (
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/json-iterator/go v1.1.12
github.com/julienschmidt/httprouter v1.3.0
github.com/karalabe/usb v0.0.2
github.com/kylelemons/godebug v1.1.0
github.com/maticnetwork/crand v1.0.2
github.com/maticnetwork/heimdall v0.3.1-0.20230105132832-d0063f71e3f0
github.com/maticnetwork/polyproto v0.0.2
github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.16
github.com/mitchellh/cli v1.1.2
@ -88,10 +89,28 @@ require (
)
require (
cloud.google.com/go/compute v1.7.0 // indirect
cloud.google.com/go/iam v0.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
require (
cloud.google.com/go v0.104.0 // indirect
cloud.google.com/go/pubsub v1.3.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect
github.com/DataDog/zstd v1.5.2 // indirect
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aws/aws-sdk-go v1.34.28 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1 // indirect
@ -119,7 +138,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
@ -141,7 +160,6 @@ require (
go.uber.org/goleak v1.1.12
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
gonum.org/v1/gonum v0.11.0
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
@ -168,10 +186,10 @@ require (
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-redis/redis v6.15.7+incompatible // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/googleapis/gax-go/v2 v2.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
@ -181,25 +199,21 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/posener/complete v1.1.1 // indirect
github.com/rakyll/statik v0.1.7 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.4.0 // indirect
github.com/spf13/viper v1.13.0 // indirect
github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 // indirect
github.com/stumble/gorocksdb v0.0.3 // indirect
github.com/tendermint/btcd v0.1.1 // indirect
@ -211,33 +225,27 @@ require (
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/oauth2 v0.3.0 // indirect
gotest.tools/v3 v3.5.0 // indirect
)
require (
cloud.google.com/go v0.65.0 // indirect
cloud.google.com/go/pubsub v1.3.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
github.com/RichardKnop/machinery v1.7.4 // indirect
github.com/RichardKnop/redsync v1.2.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aws/aws-sdk-go v1.34.28 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
github.com/zondax/hid v0.9.0 // indirect
go.mongodb.org/mongo-driver v1.3.0 // indirect
go.opencensus.io v0.22.6 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 // indirect
go.opentelemetry.io/otel/trace v1.2.0
go.opentelemetry.io/proto/otlp v0.10.0 // indirect
google.golang.org/api v0.34.0 // indirect
google.golang.org/api v0.97.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b // indirect
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce // indirect
)
replace github.com/cosmos/cosmos-sdk => github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7

269
go.sum
View file

@ -4,6 +4,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@ -14,8 +15,25 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -23,8 +41,19 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.5.0 h1:fz9X5zyTWBmamZsqvqZqD7khbifcZF/q+Z1J8pfhIUg=
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@ -35,6 +64,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
@ -204,6 +235,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@ -283,8 +315,8 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/docker v1.6.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.6.2 h1:HlFGsy+9/xrgMmhmN+NGhCc5SHGJ7I+kHosRR1xc/aI=
github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7 h1:kgvzE5wLsLa7XKfV85VZl40QXaMCaeFtHpPwJ8fhotY=
@ -306,6 +338,7 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
@ -322,8 +355,9 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQD
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c h1:CndMRAH4JIwxbW8KYq6Q+cGWcGHz0FjGR3QqcInWcW0=
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY=
@ -335,6 +369,7 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@ -454,8 +489,9 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@ -464,6 +500,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -483,6 +520,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -521,6 +559,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@ -528,6 +568,14 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -536,9 +584,20 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/gax-go/v2 v2.5.1 h1:kBRZU0PSuI7PspsSb/ChWoVResUcwNVIdpB049pKTiw=
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@ -609,6 +668,7 @@ github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixH
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
@ -661,7 +721,6 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@ -703,6 +762,7 @@ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH6
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -728,8 +788,9 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
@ -739,10 +800,12 @@ github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7 h1:8NoE
github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7/go.mod h1:uW55Ru86N5o3L8SVkVL1TPE+mV/WRM2la8sC3TR/Ajc=
github.com/maticnetwork/crand v1.0.2 h1:Af0tAivC8zrxXDpGWNWVT/0s1fOz8w0eRbahZgURS8I=
github.com/maticnetwork/crand v1.0.2/go.mod h1:/NRNL3bj2eYdqpWmoIP5puxndTpi0XRxpj5ZKxfHjyg=
github.com/maticnetwork/heimdall v0.3.1-0.20230105132832-d0063f71e3f0 h1:MYTAcBs4y88GEzesT8eAU472ZfQSLluW0qgkZTzNgwM=
github.com/maticnetwork/heimdall v0.3.1-0.20230105132832-d0063f71e3f0/go.mod h1:A3bUSe9jjMQHEPPOUW1JytM3x2g3t+jvsbPY66KBPIs=
github.com/maticnetwork/polyproto v0.0.2 h1:cPxuxbIDItdwGnucc3lZB58U8Zfe1mH73PWTGd15554=
github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc h1:7wEWQbYs6ESGsVBhjiVp7fZZyfaN4lg4XYLOLfRIpmY=
github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc/go.mod h1:P2DoKhovYP9G9Kj2EH/zHaiRJF1jNU7ZJOyelG4UCa8=
github.com/maticnetwork/polyproto v0.0.2/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o=
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53 h1:PjYV+lghs106JKkrYgOnrsfDLoTc11BxZd4rUa4Rus4=
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o=
github.com/maticnetwork/tendermint v0.26.0-dev0.0.20220923185258-3e7c7f86ce9f h1:iV69PJUEdwJJFXQvbADYVEMxDrkKAsPdHTg4U3F510I=
github.com/maticnetwork/tendermint v0.26.0-dev0.0.20220923185258-3e7c7f86ce9f/go.mod h1:90S74348uYSGfWwNIgvzQiRRakSH/c7VVt1TR5mzIuY=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
@ -751,6 +814,7 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@ -793,8 +857,9 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
@ -859,10 +924,14 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg=
@ -881,6 +950,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -980,11 +1050,13 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
@ -998,8 +1070,9 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
github.com/status-im/keycard-go v0.0.0-20211109104530-b0e0482ba91d/go.mod h1:97vT0Rym0wCnK4B++hNA3nCetr0Mh1KXaVxzSt1arjg=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
@ -1026,6 +1099,8 @@ github.com/stumble/gorocksdb v0.0.3 h1:9UU+QA1pqFYJuf9+5p7z1IqdE5k0mma4UAeu2wmX8
github.com/stumble/gorocksdb v0.0.3/go.mod h1:v6IHdFBXk5DJ1K4FZ0xi+eY737quiiBxYtSWXadLybY=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 h1:m+8fKfQwCAy1QjzINvKe/pYtLjo2dl59x2w9YSEJxuY=
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
@ -1127,8 +1202,10 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.6 h1:BdkrbWrzDlV9dnbzoP7sfN+dHheJ4J9JOaYxcUDL+ok=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ=
go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 h1:xzbcGykysUh776gzD1LUPsNNHKWN0kQWDnJhn1ddUuk=
@ -1174,7 +1251,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1218,6 +1297,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
@ -1228,6 +1308,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
@ -1278,18 +1360,31 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
@ -1300,7 +1395,21 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1315,6 +1424,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1378,10 +1488,17 @@ golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1389,19 +1506,36 @@ golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1498,10 +1632,17 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
@ -1515,8 +1656,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
@ -1546,8 +1688,32 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A=
google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.97.0 h1:x/vEL1XDF/2V4xzdNgFPaKHluRESo2aTsL7QzHnBtGQ=
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1596,10 +1762,60 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b h1:SfSkJugek6xm7lWywqth4r2iTrYLpD8lOj1nMIIhMNM=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce h1:+2ye9vAK4F9F/LCex8dT2cDk0VnTAwUL8uRgX/6nAMU=
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -1622,15 +1838,28 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -1661,6 +1890,8 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
@ -1689,6 +1920,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -1350,7 +1350,8 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
return []*Block{}, nil
}
ret := make([]*Block, 0, to-from+1)
// nolint:prealloc
var ret []*Block
for i := from; i <= to; i++ {
numberOrHash := rpc.BlockNumberOrHashWithNumber(i)
@ -1370,6 +1371,10 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
}
ret = append(ret, block)
if err := ctx.Err(); err != nil {
return nil, err
}
}
return ret, nil

View file

@ -250,9 +250,19 @@ type StateSyncFilter struct {
// interface for whitelist service
type ChainValidator interface {
IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
IsValidPeer(fetchHeadersByNumber func(number uint64, amount int, skip int, reverse bool) ([]*types.Header, []common.Hash, error)) (bool, error)
IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error)
GetWhitelistedCheckpoint() (bool, uint64, common.Hash)
GetWhitelistedMilestone() (bool, uint64, common.Hash)
ProcessCheckpoint(endBlockNum uint64, endBlockHash common.Hash)
GetCheckpointWhitelist() map[uint64]common.Hash
PurgeCheckpointWhitelist()
ProcessMilestone(endBlockNum uint64, endBlockHash common.Hash)
ProcessFutureMilestone(num uint64, hash common.Hash)
PurgeWhitelistedCheckpoint()
PurgeWhitelistedMilestone()
LockMutex(endBlockNum uint64) bool
UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash)
UnlockSprint(endBlockNum uint64)
RemoveMilestoneID(milestoneId string)
GetMilestoneIDsList() []string
}

View file

@ -5,17 +5,21 @@ import (
"errors"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/cli/flagset"
"github.com/ethereum/go-ethereum/internal/cli/server"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/prometheus"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
@ -26,14 +30,16 @@ import (
type BootnodeCommand struct {
UI cli.Ui
listenAddr string
v5 bool
verbosity int
logLevel string
nat string
nodeKey string
saveKey string
dryRun bool
listenAddr string
enableMetrics bool
prometheusAddr string
v5 bool
verbosity int
logLevel string
nat string
nodeKey string
saveKey string
dryRun bool
}
// Help implements the cli.Command interface
@ -60,6 +66,18 @@ func (b *BootnodeCommand) Flags() *flagset.Flagset {
Usage: "listening address of bootnode (<ip>:<port>)",
Value: &b.listenAddr,
})
flags.BoolFlag(&flagset.BoolFlag{
Name: "metrics",
Usage: "Enable metrics collection and reporting",
Value: &b.enableMetrics,
Default: true,
})
flags.StringFlag(&flagset.StringFlag{
Name: "prometheus-addr",
Default: "127.0.0.1:7071",
Usage: "listening address of bootnode (<ip>:<port>)",
Value: &b.prometheusAddr,
})
flags.BoolFlag(&flagset.BoolFlag{
Name: "v5",
Default: false,
@ -237,6 +255,26 @@ func (b *BootnodeCommand) Run(args []string) int {
}
}
if b.enableMetrics {
prometheusMux := http.NewServeMux()
prometheusMux.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry))
promServer := &http.Server{
Addr: b.prometheusAddr,
Handler: prometheusMux,
ReadHeaderTimeout: 30 * time.Second,
}
go func() {
if err := promServer.ListenAndServe(); err != nil {
log.Error("Failure in running Prometheus server", "err", err)
}
}()
log.Info("Enabling metrics export to prometheus", "path", fmt.Sprintf("http://%s/debug/metrics/prometheus", b.prometheusAddr))
}
signalCh := make(chan os.Signal, 4)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)

View file

@ -42,7 +42,7 @@ func (c *DumpconfigCommand) Synopsis() string {
func (c *DumpconfigCommand) Run(args []string) int {
// Initialize an empty command instance to get flags
command := server.Command{}
flags := command.Flags()
flags := command.Flags(nil)
if err := flags.Parse(args); err != nil {
c.UI.Error(err.Error())

View file

@ -5,33 +5,42 @@ import (
"fmt"
"math/big"
"sort"
"strconv"
"strings"
"time"
)
type Flagset struct {
flags []*FlagVar
flags map[string]*FlagVar
set *flag.FlagSet
}
func NewFlagSet(name string) *Flagset {
f := &Flagset{
flags: []*FlagVar{},
flags: make(map[string]*FlagVar, 0),
set: flag.NewFlagSet(name, flag.ContinueOnError),
}
return f
}
// Updatable is a minimalistic representation of a flag which has
// the method `UpdateValue` implemented which can be called while
// overwriting flags.
type Updatable interface {
UpdateValue(string)
}
type FlagVar struct {
Name string
Usage string
Group string
Default any
Value Updatable
}
func (f *Flagset) addFlag(fl *FlagVar) {
f.flags = append(f.flags, fl)
f.flags[fl.Name] = fl
}
func (f *Flagset) Help() string {
@ -51,9 +60,12 @@ func (f *Flagset) Help() string {
}
func (f *Flagset) GetAllFlags() []string {
flags := []string{}
for _, flag := range f.flags {
flags = append(flags, flag.Name)
i := 0
flags := make([]string, 0, len(f.flags))
for name := range f.flags {
flags[i] = name
i++
}
return flags
@ -110,6 +122,33 @@ func (f *Flagset) Args() []string {
return f.set.Args()
}
// UpdateValue updates the underlying value of a flag
// given the flag name and value to update using pointer.
func (f *Flagset) UpdateValue(names []string, values []string) {
for i, name := range names {
if flag, ok := f.flags[name]; ok {
value := values[i]
// Call the underlying flag's `UpdateValue` method
flag.Value.UpdateValue(value)
}
}
}
// Visit visits all the set flags and returns the name and value
// in string to set later.
func (f *Flagset) Visit() ([]string, []string) {
names := make([]string, 0, len(f.flags))
values := make([]string, 0, len(f.flags))
f.set.Visit(func(flag *flag.Flag) {
names = append(names, flag.Name)
values = append(values, flag.Value.String())
})
return names, values
}
type BoolFlag struct {
Name string
Usage string
@ -118,12 +157,19 @@ type BoolFlag struct {
Group string
}
func (b *BoolFlag) UpdateValue(value string) {
v, _ := strconv.ParseBool(value)
*b.Value = v
}
func (f *Flagset) BoolFlag(b *BoolFlag) {
f.addFlag(&FlagVar{
Name: b.Name,
Usage: b.Usage,
Group: b.Group,
Default: b.Default,
Value: b,
})
f.set.BoolVar(b.Value, b.Name, b.Default, b.Usage)
}
@ -137,6 +183,10 @@ type StringFlag struct {
HideDefaultFromDoc bool
}
func (b *StringFlag) UpdateValue(value string) {
*b.Value = value
}
func (f *Flagset) StringFlag(b *StringFlag) {
if b.Default == "" || b.HideDefaultFromDoc {
f.addFlag(&FlagVar{
@ -144,6 +194,7 @@ func (f *Flagset) StringFlag(b *StringFlag) {
Usage: b.Usage,
Group: b.Group,
Default: nil,
Value: b,
})
} else {
f.addFlag(&FlagVar{
@ -151,6 +202,7 @@ func (f *Flagset) StringFlag(b *StringFlag) {
Usage: b.Usage,
Group: b.Group,
Default: b.Default,
Value: b,
})
}
@ -165,12 +217,19 @@ type IntFlag struct {
Group string
}
func (b *IntFlag) UpdateValue(value string) {
v, _ := strconv.ParseInt(value, 10, 64)
*b.Value = int(v)
}
func (f *Flagset) IntFlag(i *IntFlag) {
f.addFlag(&FlagVar{
Name: i.Name,
Usage: i.Usage,
Group: i.Group,
Default: i.Default,
Value: i,
})
f.set.IntVar(i.Value, i.Name, i.Default, i.Usage)
}
@ -183,12 +242,19 @@ type Uint64Flag struct {
Group string
}
func (b *Uint64Flag) UpdateValue(value string) {
v, _ := strconv.ParseUint(value, 10, 64)
*b.Value = v
}
func (f *Flagset) Uint64Flag(i *Uint64Flag) {
f.addFlag(&FlagVar{
Name: i.Name,
Usage: i.Usage,
Group: i.Group,
Default: fmt.Sprintf("%d", i.Default),
Value: i,
})
f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage)
}
@ -209,31 +275,47 @@ func (b *BigIntFlag) String() string {
return b.Value.String()
}
func (b *BigIntFlag) Set(value string) error {
func parseBigInt(value string) *big.Int {
num := new(big.Int)
var ok bool
if strings.HasPrefix(value, "0x") {
num, ok = num.SetString(value[2:], 16)
*b.Value = *num
num, _ = num.SetString(value[2:], 16)
} else {
num, ok = num.SetString(value, 10)
*b.Value = *num
num, _ = num.SetString(value, 10)
}
if !ok {
return num
}
func (b *BigIntFlag) Set(value string) error {
num := parseBigInt(value)
if num == nil {
return fmt.Errorf("failed to set big int")
}
*b.Value = *num
return nil
}
func (b *BigIntFlag) UpdateValue(value string) {
num := parseBigInt(value)
if num == nil {
return
}
*b.Value = *num
}
func (f *Flagset) BigIntFlag(b *BigIntFlag) {
f.addFlag(&FlagVar{
Name: b.Name,
Usage: b.Usage,
Group: b.Group,
Default: b.Default,
Value: b,
})
f.set.Var(b, b.Name, b.Usage)
}
@ -273,6 +355,10 @@ func (i *SliceStringFlag) Set(value string) error {
return nil
}
func (i *SliceStringFlag) UpdateValue(value string) {
*i.Value = SplitAndTrim(value)
}
func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
if s.Default == nil || len(s.Default) == 0 {
f.addFlag(&FlagVar{
@ -280,6 +366,7 @@ func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
Usage: s.Usage,
Group: s.Group,
Default: nil,
Value: s,
})
} else {
f.addFlag(&FlagVar{
@ -287,6 +374,7 @@ func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
Usage: s.Usage,
Group: s.Group,
Default: strings.Join(s.Default, ","),
Value: s,
})
}
@ -301,12 +389,19 @@ type DurationFlag struct {
Group string
}
func (d *DurationFlag) UpdateValue(value string) {
v, _ := time.ParseDuration(value)
*d.Value = v
}
func (f *Flagset) DurationFlag(d *DurationFlag) {
f.addFlag(&FlagVar{
Name: d.Name,
Usage: d.Usage,
Group: d.Group,
Default: d.Default,
Value: d,
})
f.set.DurationVar(d.Value, d.Name, d.Default, "")
}
@ -336,24 +431,38 @@ func (m *MapStringFlag) String() string {
return formatMapString(*m.Value)
}
func (m *MapStringFlag) Set(value string) error {
if m.Value == nil {
m.Value = &map[string]string{}
}
func parseMap(value string) map[string]string {
m := make(map[string]string)
for _, t := range strings.Split(value, ",") {
if t != "" {
kv := strings.Split(t, "=")
if len(kv) == 2 {
(*m.Value)[kv[0]] = kv[1]
m[kv[0]] = kv[1]
}
}
}
return m
}
func (m *MapStringFlag) Set(value string) error {
if m.Value == nil {
m.Value = &map[string]string{}
}
m2 := parseMap(value)
*m.Value = m2
return nil
}
func (m *MapStringFlag) UpdateValue(value string) {
m2 := parseMap(value)
*m.Value = m2
}
func (f *Flagset) MapStringFlag(m *MapStringFlag) {
if m.Default == nil || len(m.Default) == 0 {
f.addFlag(&FlagVar{
@ -361,6 +470,7 @@ func (f *Flagset) MapStringFlag(m *MapStringFlag) {
Usage: m.Usage,
Group: m.Group,
Default: nil,
Value: m,
})
} else {
f.addFlag(&FlagVar{
@ -368,6 +478,7 @@ func (f *Flagset) MapStringFlag(m *MapStringFlag) {
Usage: m.Usage,
Group: m.Group,
Default: formatMapString(m.Default),
Value: m,
})
}
@ -382,12 +493,19 @@ type Float64Flag struct {
Group string
}
func (f *Float64Flag) UpdateValue(value string) {
v, _ := strconv.ParseFloat(value, 64)
*f.Value = v
}
func (f *Flagset) Float64Flag(i *Float64Flag) {
f.addFlag(&FlagVar{
Name: i.Name,
Usage: i.Usage,
Group: i.Group,
Default: i.Default,
Value: i,
})
f.set.Float64Var(i.Value, i.Name, i.Default, "")
}

View file

@ -1,26 +1,176 @@
package flagset
import (
"math/big"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFlagsetBool(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := false
value := true
f.BoolFlag(&BoolFlag{
Name: "flag",
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "true"}))
assert.Equal(t, value, true)
// Parse no value, should have default (of datatype)
require.NoError(t, f.Parse([]string{}))
require.Equal(t, false, value)
// Parse --flag true
require.NoError(t, f.Parse([]string{"--flag", "true"}))
require.Equal(t, true, value)
// Parse --flag=true
require.NoError(t, f.Parse([]string{"--flag=true"}))
require.Equal(t, true, value)
// Parse --flag false: won't parse false
require.NoError(t, f.Parse([]string{"--flag", "false"}))
require.Equal(t, true, value)
// Parse --flag=false
require.NoError(t, f.Parse([]string{"--flag=false"}))
require.Equal(t, false, value)
// Parse --flag
require.NoError(t, f.Parse([]string{"--flag"}))
require.Equal(t, true, value)
}
func TestFlagsetString(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := "hello"
f.StringFlag(&StringFlag{
Name: "flag",
Value: &value,
})
// Parse no value, should have default
require.NoError(t, f.Parse([]string{}))
require.Equal(t, "", value)
// Parse --flag value
require.NoError(t, f.Parse([]string{"--flag", "value"}))
require.Equal(t, "value", value)
// Parse --flag ""
require.NoError(t, f.Parse([]string{"--flag", ""}))
require.Equal(t, "", value)
// Parse --flag=newvalue
require.NoError(t, f.Parse([]string{"--flag=newvalue"}))
require.Equal(t, "newvalue", value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}
func TestFlagsetInt(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := 10
f.IntFlag(&IntFlag{
Name: "flag",
Value: &value,
})
// Parse no value, should have default
require.NoError(t, f.Parse([]string{}))
require.Equal(t, 0, value)
// Parse --flag 20
require.NoError(t, f.Parse([]string{"--flag", "20"}))
require.Equal(t, 20, value)
// Parse --flag 0
require.NoError(t, f.Parse([]string{"--flag", "0"}))
require.Equal(t, 0, value)
// Parse --flag=30
require.NoError(t, f.Parse([]string{"--flag=30"}))
require.Equal(t, 30, value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}
func TestFlagsetFloat64(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := 10.0
f.Float64Flag(&Float64Flag{
Name: "flag",
Value: &value,
})
// Parse no value, should have default
require.NoError(t, f.Parse([]string{}))
require.Equal(t, 0.0, value)
// Parse --flag 20
require.NoError(t, f.Parse([]string{"--flag", "20.1"}))
require.Equal(t, 20.1, value)
// Parse --flag 0
require.NoError(t, f.Parse([]string{"--flag", "0"}))
require.Equal(t, 0.0, value)
// Parse --flag 0.0
require.NoError(t, f.Parse([]string{"--flag", "0.0"}))
require.Equal(t, 0.0, value)
// Parse --flag=30.1
require.NoError(t, f.Parse([]string{"--flag=30.1"}))
require.Equal(t, 30.1, value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}
func TestFlagsetBigInt(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := big.NewInt(0)
f.BigIntFlag(&BigIntFlag{
Name: "flag",
Value: value,
})
// Parse no value, should have initial value (0 here)
require.NoError(t, f.Parse([]string{}))
require.Equal(t, big.NewInt(0), value)
// Parse --flag 20
require.NoError(t, f.Parse([]string{"--flag", "20"}))
require.Equal(t, big.NewInt(20), value)
// Parse --flag=30
require.NoError(t, f.Parse([]string{"--flag=30"}))
require.Equal(t, big.NewInt(30), value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}
func TestFlagsetSliceString(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := []string{"a", "b", "c"}
@ -30,13 +180,25 @@ func TestFlagsetSliceString(t *testing.T) {
Default: value,
})
assert.NoError(t, f.Parse([]string{}))
assert.Equal(t, value, []string{"a", "b", "c"})
assert.NoError(t, f.Parse([]string{"--flag", "a,b"}))
assert.Equal(t, value, []string{"a", "b"})
// Parse no value, should have initial value
require.NoError(t, f.Parse([]string{}))
require.Equal(t, []string{"a", "b", "c"}, value)
// Parse --flag a,b
require.NoError(t, f.Parse([]string{"--flag", "a,b"}))
require.Equal(t, []string{"a", "b"}, value)
// Parse --flag ""
require.NoError(t, f.Parse([]string{"--flag", ""}))
require.Equal(t, []string(nil), value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}
func TestFlagsetDuration(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := time.Duration(0)
@ -45,11 +207,25 @@ func TestFlagsetDuration(t *testing.T) {
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "1m"}))
assert.Equal(t, value, 1*time.Minute)
// Parse no value, should have initial value
require.NoError(t, f.Parse([]string{}))
require.Equal(t, time.Duration(0), value)
// Parse --flag 1m
require.NoError(t, f.Parse([]string{"--flag", "1m"}))
require.Equal(t, time.Minute, value)
// Parse --flag=1h
require.NoError(t, f.Parse([]string{"--flag=1h"}))
require.Equal(t, time.Hour, value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}
func TestFlagsetMapString(t *testing.T) {
t.Parallel()
f := NewFlagSet("")
value := map[string]string{}
@ -58,6 +234,18 @@ func TestFlagsetMapString(t *testing.T) {
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "a=b,c=d"}))
assert.Equal(t, value, map[string]string{"a": "b", "c": "d"})
// Parse no value, should have initial value
require.NoError(t, f.Parse([]string{}))
require.Equal(t, map[string]string{}, value)
// Parse --flag a=b,c=d
require.NoError(t, f.Parse([]string{"--flag", "a=b,c=d"}))
require.Equal(t, map[string]string{"a": "b", "c": "d"}, value)
// Parse --flag=x=y
require.NoError(t, f.Parse([]string{"--flag=x=y"}))
require.Equal(t, map[string]string{"x": "y"}, value)
// Parse --flag: should fail due to no args
require.Error(t, f.Parse([]string{"--flag"}))
}

View file

@ -85,7 +85,7 @@ var mainnetBor = &Chain{
Alloc: readPrealloc("allocs/mainnet.json"),
},
Bootnodes: []string{
"enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303",
"enode://88116f4295f5a31538ae409e4d44ad40d22e44ee9342869e7d68bdec55b0f83c1530355ce8b41fbec0928a7d75a5745d528450d30aec92066ab6ba1ee351d710@159.203.9.164:30303",
"enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303",
"enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303",
},
}

View file

@ -78,7 +78,7 @@ var mumbaiTestnet = &Chain{
Alloc: readPrealloc("allocs/mumbai.json"),
},
Bootnodes: []string{
"enode://320553cda00dfc003f499a3ce9598029f364fbb3ed1222fdc20a94d97dcc4d8ba0cd0bfa996579dcc6d17a534741fb0a5da303a90579431259150de66b597251@54.147.31.250:30303",
"enode://f0f48a8781629f95ff02606081e6e43e4aebd503f3d07fc931fad7dd5ca1ba52bd849a6f6c3be0e375cf13c9ae04d859c4a9ae3546dc8ed4f10aa5dbb47d4998@34.226.134.117:30303",
"enode://bdcd4786a616a853b8a041f53496d853c68d99d54ff305615cd91c03cd56895e0a7f6e9f35dbf89131044e2114a9a782b792b5661e3aff07faf125a98606a071@43.200.206.40:30303",
"enode://209aaf7ed549cf4a5700fd833da25413f80a1248bd3aa7fe2a87203e3f7b236dd729579e5c8df61c97bf508281bae4969d6de76a7393bcbd04a0af70270333b3@54.216.248.9:30303",
},
}

View file

@ -35,7 +35,7 @@ func (c *Command) MarkDown() string {
items := []string{
"# Server",
"The ```bor server``` command runs the Bor client.",
c.Flags().MarkDown(),
c.Flags(nil).MarkDown(),
}
return strings.Join(items, "\n\n")
@ -46,7 +46,7 @@ func (c *Command) Help() string {
return `Usage: bor [options]
Run the Bor server.
` + c.Flags().Help()
` + c.Flags(nil).Help()
}
// Synopsis implements the cli.Command interface
@ -54,40 +54,69 @@ func (c *Command) Synopsis() string {
return "Run the Bor server"
}
// checkConfigFlag checks if the config flag is set or not. If set,
// it returns the value else an empty string.
func checkConfigFlag(args []string) string {
for i := 0; i < len(args); i++ {
arg := args[i]
// Check for single or double dashes
if strings.HasPrefix(arg, "-config") || strings.HasPrefix(arg, "--config") {
parts := strings.SplitN(arg, "=", 2)
if len(parts) == 2 {
return parts[1]
}
// If there's no equal sign, check the next argument
if i+1 < len(args) {
return args[i+1]
}
}
}
return ""
}
func (c *Command) extractFlags(args []string) error {
config := *DefaultConfig()
// Check if config file is provided or not
configFilePath := checkConfigFlag(args)
flags := c.Flags()
if err := flags.Parse(args); err != nil {
c.UI.Error(err.Error())
c.config = &config
if configFilePath != "" {
log.Info("Reading config file", "path", configFilePath)
return err
}
// TODO: Check if this can be removed or not
// read cli flags
if err := config.Merge(c.cliConfig); err != nil {
c.UI.Error(err.Error())
c.config = &config
return err
}
// read if config file is provided, this will overwrite the cli flags, if provided
if c.configFile != "" {
log.Warn("Config File provided, this will overwrite the cli flags", "path", c.configFile)
cfg, err := readConfigFile(c.configFile)
// Parse the config file
cfg, err := readConfigFile(configFilePath)
if err != nil {
c.UI.Error(err.Error())
c.config = &config
return err
}
if err := config.Merge(cfg); err != nil {
log.Warn("Config set via config file will be overridden by cli flags")
// Initialse a flagset based on the config created above
flags := c.Flags(cfg)
// Check for explicit cli args
cmd := Command{} // use a new variable to keep the original config intact
cliFlags := cmd.Flags(nil)
if err := cliFlags.Parse(args); err != nil {
c.UI.Error(err.Error())
return err
}
// Get the list of flags set explicitly
names, values := cliFlags.Visit()
// Set these flags using the flagset created earlier
flags.UpdateValue(names, values)
} else {
flags := c.Flags(nil)
if err := flags.Parse(args); err != nil {
c.UI.Error(err.Error())
c.config = &config
return err
}
@ -95,33 +124,33 @@ func (c *Command) extractFlags(args []string) error {
// nolint: nestif
// check for log-level and verbosity here
if c.configFile != "" {
data, _ := toml.LoadFile(c.configFile)
if configFilePath != "" {
data, _ := toml.LoadFile(configFilePath)
if data.Has("verbosity") && data.Has("log-level") {
log.Warn("Config contains both, verbosity and log-level, log-level will be deprecated soon. Use verbosity only.", "using", data.Get("verbosity"))
} else if !data.Has("verbosity") && data.Has("log-level") {
log.Warn("Config contains log-level only, note that log-level will be deprecated soon. Use verbosity instead.", "using", data.Get("log-level"))
config.Verbosity = VerbosityStringToInt(strings.ToLower(data.Get("log-level").(string)))
c.cliConfig.Verbosity = VerbosityStringToInt(strings.ToLower(data.Get("log-level").(string)))
}
} else {
tempFlag := 0
for _, val := range args {
if (strings.HasPrefix(val, "-verbosity") || strings.HasPrefix(val, "--verbosity")) && config.LogLevel != "" {
if (strings.HasPrefix(val, "-verbosity") || strings.HasPrefix(val, "--verbosity")) && c.cliConfig.LogLevel != "" {
tempFlag = 1
break
}
}
if tempFlag == 1 {
log.Warn("Both, verbosity and log-level flags are provided, log-level will be deprecated soon. Use verbosity only.", "using", config.Verbosity)
} else if tempFlag == 0 && config.LogLevel != "" {
log.Warn("Only log-level flag is provided, note that log-level will be deprecated soon. Use verbosity instead.", "using", config.LogLevel)
config.Verbosity = VerbosityStringToInt(strings.ToLower(config.LogLevel))
log.Warn("Both, verbosity and log-level flags are provided, log-level will be deprecated soon. Use verbosity only.", "using", c.cliConfig.Verbosity)
} else if tempFlag == 0 && c.cliConfig.LogLevel != "" {
log.Warn("Only log-level flag is provided, note that log-level will be deprecated soon. Use verbosity instead.", "using", c.cliConfig.LogLevel)
c.cliConfig.Verbosity = VerbosityStringToInt(strings.ToLower(c.cliConfig.LogLevel))
}
}
c.config = &config
c.config = c.cliConfig
return nil
}

View file

@ -8,43 +8,135 @@ import (
"github.com/stretchr/testify/require"
)
func TestFlags(t *testing.T) {
// TestFlagsWithoutConfig tests all types of flags passed only
// via cli args.
func TestFlagsWithoutConfig(t *testing.T) {
t.Parallel()
var c Command
args := []string{
"--txpool.rejournal", "30m0s",
"--txpool.lifetime", "30m0s",
"--miner.gasprice", "20000000000",
"--gpo.maxprice", "70000000000",
"--gpo.ignoreprice", "1",
"--cache.trie.rejournal", "40m0s",
"--dev",
"--dev.period", "2",
"--identity", "",
"--datadir", "./data",
"--maxpeers", "30",
"--verbosity", "3",
"--rpc.batchlimit", "0",
"--snapshot",
"--bor.logs=false",
"--eth.requiredblocks", "a=b",
"--http.api", "eth,web3,bor",
"--miner.gasprice", "30000000000",
"--miner.recommit", "20s",
"--rpc.evmtimeout", "5s",
"--rpc.txfeecap", "6.0",
"--http.api", "eth,bor",
"--ws.api", "",
"--gpo.maxprice", "5000000000000",
}
err := c.extractFlags(args)
require.NoError(t, err)
txRe, _ := time.ParseDuration("30m0s")
txLt, _ := time.ParseDuration("30m0s")
caRe, _ := time.ParseDuration("40m0s")
recommit, _ := time.ParseDuration("20s")
evmTimeout, _ := time.ParseDuration("5s")
require.Equal(t, c.config.Identity, "")
require.Equal(t, c.config.DataDir, "./data")
require.Equal(t, c.config.Developer.Enabled, true)
require.Equal(t, c.config.Developer.Period, uint64(2))
require.Equal(t, c.config.TxPool.Rejournal, txRe)
require.Equal(t, c.config.TxPool.LifeTime, txLt)
require.Equal(t, c.config.Sealer.GasPrice, big.NewInt(20000000000))
require.Equal(t, c.config.Gpo.MaxPrice, big.NewInt(70000000000))
require.Equal(t, c.config.Gpo.IgnorePrice, big.NewInt(1))
require.Equal(t, c.config.Cache.Rejournal, caRe)
require.Equal(t, c.config.P2P.MaxPeers, uint64(30))
require.Equal(t, c.config.Verbosity, 3)
require.Equal(t, c.config.RPCBatchLimit, uint64(0))
require.Equal(t, c.config.Snapshot, true)
require.Equal(t, c.config.BorLogs, false)
require.Equal(t, c.config.RequiredBlocks, map[string]string{"a": "b"})
require.Equal(t, c.config.JsonRPC.Http.API, []string{"eth", "web3", "bor"})
require.Equal(t, c.config.Sealer.GasPrice, big.NewInt(30000000000))
require.Equal(t, c.config.Sealer.Recommit, recommit)
require.Equal(t, c.config.JsonRPC.RPCEVMTimeout, evmTimeout)
require.Equal(t, c.config.JsonRPC.Http.API, []string{"eth", "bor"})
require.Equal(t, c.config.JsonRPC.Ws.API, []string(nil))
require.Equal(t, c.config.Gpo.MaxPrice, big.NewInt(5000000000000))
}
// TestFlagsWithoutConfig tests all types of flags passed only
// via config file.
func TestFlagsWithConfig(t *testing.T) {
t.Parallel()
var c Command
args := []string{
"--config", "./testdata/test.toml",
}
err := c.extractFlags(args)
require.NoError(t, err)
recommit, _ := time.ParseDuration("20s")
evmTimeout, _ := time.ParseDuration("5s")
require.Equal(t, c.config.Identity, "")
require.Equal(t, c.config.DataDir, "./data")
require.Equal(t, c.config.Verbosity, 3)
require.Equal(t, c.config.RPCBatchLimit, uint64(0))
require.Equal(t, c.config.Snapshot, true)
require.Equal(t, c.config.BorLogs, false)
require.Equal(t, c.config.RequiredBlocks,
map[string]string{
"31000000": "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e",
"32000000": "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68",
},
)
require.Equal(t, c.config.Sealer.GasPrice, big.NewInt(30000000000))
require.Equal(t, c.config.Sealer.Recommit, recommit)
require.Equal(t, c.config.JsonRPC.RPCEVMTimeout, evmTimeout)
require.Equal(t, c.config.JsonRPC.Http.API, []string{"eth", "bor"})
require.Equal(t, c.config.JsonRPC.Ws.API, []string{""})
require.Equal(t, c.config.Gpo.MaxPrice, big.NewInt(5000000000000))
}
// TestFlagsWithConfig tests all types of flags passed via both
// config file and cli args. The cli args should overwrite the
// value of flag.
func TestFlagsWithConfigAndFlags(t *testing.T) {
t.Parallel()
var c Command
// Set the config and also override
args := []string{
"--config", "./testdata/test.toml",
"--identity", "Anon",
"--datadir", "",
"--verbosity", "0",
"--rpc.batchlimit", "5",
"--snapshot=false",
"--bor.logs=true",
"--eth.requiredblocks", "x=y",
"--miner.gasprice", "60000000000",
"--miner.recommit", "30s",
"--rpc.evmtimeout", "0s",
"--rpc.txfeecap", "0",
"--http.api", "",
"--ws.api", "eth,bor,web3",
"--gpo.maxprice", "0",
}
err := c.extractFlags(args)
require.NoError(t, err)
recommit, _ := time.ParseDuration("30s")
evmTimeout, _ := time.ParseDuration("0s")
require.Equal(t, c.config.Identity, "Anon")
require.Equal(t, c.config.DataDir, "")
require.Equal(t, c.config.Verbosity, 0)
require.Equal(t, c.config.RPCBatchLimit, uint64(5))
require.Equal(t, c.config.Snapshot, false)
require.Equal(t, c.config.BorLogs, true)
require.Equal(t, c.config.RequiredBlocks, map[string]string{"x": "y"})
require.Equal(t, c.config.Sealer.GasPrice, big.NewInt(60000000000))
require.Equal(t, c.config.Sealer.Recommit, recommit)
require.Equal(t, c.config.JsonRPC.RPCEVMTimeout, evmTimeout)
require.Equal(t, c.config.JsonRPC.Http.API, []string(nil))
require.Equal(t, c.config.JsonRPC.Ws.API, []string{"eth", "bor", "web3"})
require.Equal(t, c.config.Gpo.MaxPrice, big.NewInt(0))
}

View file

@ -67,6 +67,9 @@ type Config struct {
// Ancient is the directory to store the state in
Ancient string `hcl:"ancient,optional" toml:"ancient,optional"`
// DBEngine is used to select leveldb or pebble as database
DBEngine string `hcl:"db.engine,optional" toml:"db.engine,optional"`
// KeyStoreDir is the directory to store keystores
KeyStoreDir string `hcl:"keystore,optional" toml:"keystore,optional"`
@ -118,6 +121,8 @@ type Config struct {
// Cache has the cache related settings
Cache *CacheConfig `hcl:"cache,block" toml:"cache,block"`
ExtraDB *ExtraDBConfig `hcl:"leveldb,block" toml:"leveldb,block"`
// Account has the validator account related settings
Accounts *AccountsConfig `hcl:"accounts,block" toml:"accounts,block"`
@ -548,6 +553,13 @@ type CacheConfig struct {
FDLimit int `hcl:"fdlimit,optional" toml:"fdlimit,optional"`
}
type ExtraDBConfig struct {
LevelDbCompactionTableSize uint64 `hcl:"compactiontablesize,optional" toml:"compactiontablesize,optional"`
LevelDbCompactionTableSizeMultiplier float64 `hcl:"compactiontablesizemultiplier,optional" toml:"compactiontablesizemultiplier,optional"`
LevelDbCompactionTotalSize uint64 `hcl:"compactiontotalsize,optional" toml:"compactiontotalsize,optional"`
LevelDbCompactionTotalSizeMultiplier float64 `hcl:"compactiontotalsizemultiplier,optional" toml:"compactiontotalsizemultiplier,optional"`
}
type AccountsConfig struct {
// Unlock is the list of addresses to unlock in the node
Unlock []string `hcl:"unlock,optional" toml:"unlock,optional"`
@ -592,6 +604,7 @@ func DefaultConfig() *Config {
EnablePreimageRecording: false,
DataDir: DefaultDataDir(),
Ancient: "",
DBEngine: "leveldb",
Logging: &LoggingConfig{
Vmodule: "",
Json: false,
@ -738,6 +751,14 @@ func DefaultConfig() *Config {
TrieTimeout: 60 * time.Minute,
FDLimit: 0,
},
ExtraDB: &ExtraDBConfig{
// These are LevelDB defaults, specifying here for clarity in code and in logging.
// See: https://github.com/syndtr/goleveldb/blob/126854af5e6d8295ef8e8bee3040dd8380ae72e8/leveldb/opt/options.go
LevelDbCompactionTableSize: 2, // MiB
LevelDbCompactionTableSizeMultiplier: 1,
LevelDbCompactionTotalSize: 10, // MiB
LevelDbCompactionTotalSizeMultiplier: 10,
},
Accounts: &AccountsConfig{
Unlock: []string{},
PasswordFile: "",
@ -1097,6 +1118,14 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
n.TriesInMemory = c.Cache.TriesInMemory
}
// LevelDB
{
n.LevelDbCompactionTableSize = c.ExtraDB.LevelDbCompactionTableSize
n.LevelDbCompactionTableSizeMultiplier = c.ExtraDB.LevelDbCompactionTableSizeMultiplier
n.LevelDbCompactionTotalSize = c.ExtraDB.LevelDbCompactionTotalSize
n.LevelDbCompactionTotalSizeMultiplier = c.ExtraDB.LevelDbCompactionTotalSizeMultiplier
}
n.RPCGasCap = c.JsonRPC.GasCap
if n.RPCGasCap != 0 {
log.Info("Set global gas cap", "cap", n.RPCGasCap)
@ -1278,6 +1307,7 @@ func (c *Config) buildNode() (*node.Config, error) {
cfg := &node.Config{
Name: clientIdentifier,
DataDir: c.DataDir,
DBEngine: c.DBEngine,
KeyStoreDir: c.KeyStoreDir,
UseLightweightKDF: c.Accounts.UseLightweightKDF,
InsecureUnlockAllowed: c.Accounts.AllowInsecureUnlock,

View file

@ -15,21 +15,23 @@ func TestConfigLegacy(t *testing.T) {
testConfig := DefaultConfig()
testConfig.Identity = ""
testConfig.DataDir = "./data"
testConfig.Snapshot = false
testConfig.Verbosity = 3
testConfig.RPCBatchLimit = 0
testConfig.Snapshot = true
testConfig.BorLogs = false
testConfig.RequiredBlocks = map[string]string{
"31000000": "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e",
"32000000": "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68",
}
testConfig.P2P.MaxPeers = 30
testConfig.TxPool.Locals = []string{}
testConfig.TxPool.LifeTime = time.Second
testConfig.Sealer.Enabled = true
testConfig.Sealer.GasCeil = 30000000
testConfig.Sealer.GasPrice = big.NewInt(1000000000)
testConfig.Gpo.IgnorePrice = big.NewInt(4)
testConfig.Cache.Cache = 1024
testConfig.Cache.Rejournal = time.Second
testConfig.Sealer.GasPrice = big.NewInt(30000000000)
testConfig.Sealer.Recommit = 20 * time.Second
testConfig.JsonRPC.RPCEVMTimeout = 5 * time.Second
testConfig.JsonRPC.TxFeeCap = 6.0
testConfig.JsonRPC.Http.API = []string{"eth", "bor"}
testConfig.JsonRPC.Ws.API = []string{""}
testConfig.Gpo.MaxPrice = big.NewInt(5000000000000)
assert.Equal(t, expectedConfig, testConfig)
}

View file

@ -4,8 +4,12 @@ import (
"github.com/ethereum/go-ethereum/internal/cli/flagset"
)
func (c *Command) Flags() *flagset.Flagset {
c.cliConfig = DefaultConfig()
func (c *Command) Flags(config *Config) *flagset.Flagset {
if config != nil {
c.cliConfig = config
} else {
c.cliConfig = DefaultConfig()
}
f := flagset.NewFlagSet("server")
@ -53,6 +57,12 @@ func (c *Command) Flags() *flagset.Flagset {
Value: &c.cliConfig.Ancient,
Default: c.cliConfig.Ancient,
})
f.StringFlag(&flagset.StringFlag{
Name: "db.engine",
Usage: "Backing database implementation to use ('leveldb' or 'pebble')",
Value: &c.cliConfig.DBEngine,
Default: c.cliConfig.DBEngine,
})
f.StringFlag(&flagset.StringFlag{
Name: "keystore",
Usage: "Path of the directory where keystores are located",
@ -442,6 +452,36 @@ func (c *Command) Flags() *flagset.Flagset {
Group: "Cache",
})
// LevelDB options
f.Uint64Flag(&flagset.Uint64Flag{
Name: "leveldb.compaction.table.size",
Usage: "LevelDB SSTable/file size in mebibytes",
Value: &c.cliConfig.ExtraDB.LevelDbCompactionTableSize,
Default: c.cliConfig.ExtraDB.LevelDbCompactionTableSize,
Group: "ExtraDB",
})
f.Float64Flag(&flagset.Float64Flag{
Name: "leveldb.compaction.table.size.multiplier",
Usage: "Multiplier on LevelDB SSTable/file size. Size for a level is determined by: `leveldb.compaction.table.size * (leveldb.compaction.table.size.multiplier ^ Level)`",
Value: &c.cliConfig.ExtraDB.LevelDbCompactionTableSizeMultiplier,
Default: c.cliConfig.ExtraDB.LevelDbCompactionTableSizeMultiplier,
Group: "ExtraDB",
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "leveldb.compaction.total.size",
Usage: "Total size in mebibytes of SSTables in a given LevelDB level. Size for a level is determined by: `leveldb.compaction.total.size * (leveldb.compaction.total.size.multiplier ^ Level)`",
Value: &c.cliConfig.ExtraDB.LevelDbCompactionTotalSize,
Default: c.cliConfig.ExtraDB.LevelDbCompactionTotalSize,
Group: "ExtraDB",
})
f.Float64Flag(&flagset.Float64Flag{
Name: "leveldb.compaction.total.size.multiplier",
Usage: "Multiplier on level size on LevelDB levels. Size for a level is determined by: `leveldb.compaction.total.size * (leveldb.compaction.total.size.multiplier ^ Level)`",
Value: &c.cliConfig.ExtraDB.LevelDbCompactionTotalSizeMultiplier,
Default: c.cliConfig.ExtraDB.LevelDbCompactionTotalSizeMultiplier,
Group: "ExtraDB",
})
// rpc options
f.Uint64Flag(&flagset.Uint64Flag{
Name: "rpc.gascap",

View file

@ -1,25 +1,25 @@
identity = ""
datadir = "./data"
snapshot = false
verbosity = 3
"rpc.batchlimit" = 0
snapshot = true
"bor.logs" = false
["eth.requiredblocks"]
"31000000" = "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e"
"32000000" = "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68"
[p2p]
maxpeers = 30
[txpool]
locals = []
lifetime = "1s"
[miner]
mine = true
gaslimit = 30000000
gasprice = "1000000000"
gasprice = "30000000000"
recommit = "20s"
[jsonrpc]
evmtimeout = "5s"
txfeecap = 6.0
[jsonrpc.http]
api = ["eth", "bor"]
[jsonrpc.ws]
api = [""]
[gpo]
ignoreprice = "4"
[cache]
cache = 1024
rejournal = "1s"
maxprice = "5000000000000"

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