mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
merge develop
This commit is contained in:
commit
043b71d466
71 changed files with 1934 additions and 393 deletions
134
README.md
134
README.md
|
|
@ -1,111 +1,71 @@
|
||||||
# Bor Overview
|
# Bor Overview
|
||||||
Bor is the Official Golang implementation of the Matic protocol. It is a fork of Go Ethereum - https://github.com/ethereum/go-ethereum and EVM 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).
|
||||||
|
|
||||||

|
[
|
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
|
||||||

|
)](https://pkg.go.dev/github.com/maticnetwork/bor)
|
||||||

|
[](https://goreportcard.com/report/github.com/maticnetwork/bor)
|
||||||

|
|
||||||

|

|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
[](https://discord.com/invite/0xPolygonDevs)
|
[](https://discord.com/invite/0xPolygonDevs)
|
||||||
[](https://twitter.com/0xPolygon)
|
[](https://twitter.com/0xPolygon)
|
||||||
|
|
||||||
## How to contribute
|
### Installing bor using packaging
|
||||||
|
|
||||||
### Contribution Guidelines
|
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.
|
||||||
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:
|
|
||||||
|
|
||||||
* 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
|
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/bor.sh | bash -s -- v0.4.0 <network> <node_type>
|
||||||
* Size: The Polygon project’s 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
|
|
||||||
|
|
||||||
- Create a [new issue](https://github.com/maticnetwork/bor/issues/new/choose)
|
The network accepts `mainnet` or `mumbai` and the node type accepts `validator` or `sentry` or `archive`. The installation script does the following things:
|
||||||
- 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/).
|
- Create a new user named `bor`.
|
||||||
- If you do not have a specific contribution in mind, you can also browse the issues labelled as `help wanted`
|
- Install the bor binary at `/usr/bin/bor`.
|
||||||
- Issues that additionally have the `good first issue` label are considered ideal for first-timers
|
- 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)
|
### Building from source
|
||||||
|
|
||||||
- If this is your first time forking our repo, this is all you need to do for this step:
|
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
```
|
### Documentation
|
||||||
$ git remote add upstream https://github.com/maticnetwork/bor
|
|
||||||
```
|
|
||||||
|
|
||||||
- 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/)
|
||||||
|
|
||||||
```
|
### Contribution guidelines
|
||||||
$ git checkout master
|
|
||||||
$ git fetch upstream
|
|
||||||
$ git merge upstream/master
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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
|
From the outset we defined some guidelines to ensure new contributions only ever enhance the project:
|
||||||
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.com/invite/0xPolygonDevs)?
|
|
||||||
|
|
||||||
### 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;">
|
|
||||||
|
|
||||||
|
* 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 project’s 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
|
## 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
|
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
|
||||||
included in our repository in the `COPYING` file.
|
included in our repository in the `COPYING` file.
|
||||||
|
|
||||||
<hr style="margin-top: 3em; margin-bottom: 3em;">
|
|
||||||
|
|
||||||
## Join our Discord server
|
## Join our Discord server
|
||||||
|
|
||||||
Join Polygon community – share your ideas or just say hi over [on Discord](https://discord.com/invite/0xPolygonDevs).
|
Join Polygon community – share your ideas or just say hi over [on Discord](https://discord.com/invite/0xPolygonDevs).
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,9 @@ syncmode = "full"
|
||||||
# nodekeyhex = ""
|
# nodekeyhex = ""
|
||||||
[p2p.discovery]
|
[p2p.discovery]
|
||||||
# v5disc = false
|
# 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
|
# 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 = []
|
# bootnodesv4 = []
|
||||||
# bootnodesv5 = []
|
# bootnodesv5 = []
|
||||||
# static-nodes = []
|
# static-nodes = []
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
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 {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to open database: %v", err)
|
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
|
// dump whatever already exists in the datadir
|
||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
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 err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
|
||||||
// Retrieve the DAO config flag from the database
|
// Retrieve the DAO config flag from the database
|
||||||
path := filepath.Join(datadir, "geth", "chaindata")
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -461,6 +461,31 @@ var (
|
||||||
Value: 50,
|
Value: 50,
|
||||||
Category: flags.PerfCategory,
|
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{
|
CacheTrieFlag = &cli.IntFlag{
|
||||||
Name: "cache.trie",
|
Name: "cache.trie",
|
||||||
Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)",
|
Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)",
|
||||||
|
|
@ -2170,7 +2195,7 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
|
||||||
LogCacheSize: ethcfg.FilterLogCacheSize,
|
LogCacheSize: ethcfg.FilterLogCacheSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
filterAPI := filters.NewFilterAPI(filterSystem, isLightClient, ethconfig.Defaults.BorLogs)
|
filterAPI := filters.NewFilterAPI(filterSystem, isLightClient, ethcfg.BorLogs)
|
||||||
stack.RegisterAPIs([]rpc.API{{
|
stack.RegisterAPIs([]rpc.API{{
|
||||||
Namespace: "eth",
|
Namespace: "eth",
|
||||||
Service: filterAPI,
|
Service: filterAPI,
|
||||||
|
|
@ -2291,6 +2316,8 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
||||||
|
|
||||||
err error
|
err error
|
||||||
chainDb ethdb.Database
|
chainDb ethdb.Database
|
||||||
|
|
||||||
|
dbOptions = resolveExtraDBConfig(ctx)
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -2304,9 +2331,9 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
||||||
|
|
||||||
chainDb = remotedb.New(client)
|
chainDb = remotedb.New(client)
|
||||||
case ctx.String(SyncModeFlag.Name) == "light":
|
case ctx.String(SyncModeFlag.Name) == "light":
|
||||||
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly)
|
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly, dbOptions)
|
||||||
default:
|
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 {
|
if err != nil {
|
||||||
|
|
@ -2316,6 +2343,15 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
|
||||||
return chainDb
|
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 {
|
func IsNetworkPreset(ctx *cli.Context) bool {
|
||||||
for _, flag := range NetworkFlags {
|
for _, flag := range NetworkFlags {
|
||||||
bFlag, _ := flag.(*cli.BoolFlag)
|
bFlag, _ := flag.(*cli.BoolFlag)
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,9 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lengths of hashes and addresses in bytes.
|
// 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.
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
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.
|
// Bytes gets the byte representation of the underlying hash.
|
||||||
func (h Hash) Bytes() []byte { return h[:] }
|
func (h Hash) Bytes() []byte { return h[:] }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -341,10 +341,6 @@ func (api *API) GetRootHash(start uint64, end uint64) (string, error) {
|
||||||
return root, nil
|
return root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetVoteOnHash(starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) initializeRootHashCache() error {
|
func (api *API) initializeRootHashCache() error {
|
||||||
var err error
|
var err error
|
||||||
if api.rootHashCache == nil {
|
if api.rootHashCache == nil {
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
} else {
|
} else {
|
||||||
dir := b.TempDir()
|
dir := b.TempDir()
|
||||||
|
|
||||||
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false)
|
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false, rawdb.ExtraDBConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("cannot create temporary database: %v", err)
|
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) {
|
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
dir := b.TempDir()
|
dir := b.TempDir()
|
||||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
|
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
|
|
@ -310,7 +310,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
func benchReadChain(b *testing.B, full bool, count uint64) {
|
func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
dir := b.TempDir()
|
dir := b.TempDir()
|
||||||
|
|
||||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
|
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +325,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
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 {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,8 +321,8 @@ func NewMemoryDatabaseWithCap(size int) ethdb.Database {
|
||||||
|
|
||||||
// NewLevelDBDatabase creates a persistent key-value database without a freezer
|
// NewLevelDBDatabase creates a persistent key-value database without a freezer
|
||||||
// moving immutable chain segments into cold storage.
|
// moving immutable chain segments into cold storage.
|
||||||
func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
|
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)
|
db, err := leveldb.New(file, cache, handles, namespace, readonly, resolveLevelDBConfig(extraDBConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -332,6 +332,15 @@ func NewLevelDBDatabase(file string, cache int, handles int, namespace string, r
|
||||||
return NewDatabase(db), nil
|
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 (
|
const (
|
||||||
dbPebble = "pebble"
|
dbPebble = "pebble"
|
||||||
dbLeveldb = "leveldb"
|
dbLeveldb = "leveldb"
|
||||||
|
|
@ -366,6 +375,14 @@ type OpenOptions struct {
|
||||||
Cache int // the capacity(in megabytes) of the data caching
|
Cache int // the capacity(in megabytes) of the data caching
|
||||||
Handles int // number of files to be open simultaneously
|
Handles int // number of files to be open simultaneously
|
||||||
ReadOnly bool
|
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.
|
// 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)
|
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
|
// 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
|
// Open opens both a disk-based key-value database such as leveldb or pebble, but also
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,11 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"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")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1640,6 +1640,39 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre
|
||||||
return s.accessList.Contains(addr, slot)
|
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.
|
// 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{} {
|
func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.Hash]struct{} {
|
||||||
ret := make(map[common.Hash]struct{})
|
ret := make(map[common.Hash]struct{})
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
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/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
@ -563,6 +564,32 @@ func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactio
|
||||||
return removed, invalids
|
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
|
// Cap places a hard limit on the number of items, returning all transactions
|
||||||
// exceeding that limit.
|
// exceeding that limit.
|
||||||
func (l *list) Cap(threshold int) types.Transactions {
|
func (l *list) Cap(threshold int) types.Transactions {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,11 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/holiman/uint256"
|
"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/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"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())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -825,6 +825,9 @@ func (pool *TxPool) validateTxBasics(tx *types.Transaction, local bool) error {
|
||||||
// return core.ErrInsufficientFunds
|
// return core.ErrInsufficientFunds
|
||||||
// }
|
// }
|
||||||
// Verify that replacing transactions will not result in overdraft
|
// Verify that replacing transactions will not result in overdraft
|
||||||
|
pool.pendingMu.RLock()
|
||||||
|
defer pool.pendingMu.RUnlock()
|
||||||
|
|
||||||
list := pool.pending[from]
|
list := pool.pending[from]
|
||||||
if list != nil { // Sender already has pending txs
|
if list != nil { // Sender already has pending txs
|
||||||
sum := new(big.Int).Add(tx.Cost(), list.totalcost)
|
sum := new(big.Int).Add(tx.Cost(), list.totalcost)
|
||||||
|
|
@ -2303,10 +2306,19 @@ func (pool *TxPool) demoteUnexecutables() {
|
||||||
pool.enqueueTx(hash, tx, false, false)
|
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) {
|
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 there's a gap in front, alert (should never happen) and postpone all transactions
|
||||||
if list.Len() > 0 && list.txs.Get(nonce) == nil {
|
if list.Len() > 0 && list.txs.Get(nonce) == nil {
|
||||||
|
|
|
||||||
|
|
@ -1993,6 +1993,7 @@ func TestUnderpricing(t *testing.T) {
|
||||||
keys[i], _ = crypto.GenerateKey()
|
keys[i], _ = crypto.GenerateKey()
|
||||||
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate and queue a batch of transactions, both pending and queued
|
// Generate and queue a batch of transactions, both pending and queued
|
||||||
txs := types.Transactions{}
|
txs := types.Transactions{}
|
||||||
|
|
||||||
|
|
@ -2023,6 +2024,7 @@ func TestUnderpricing(t *testing.T) {
|
||||||
if err := validatePoolInternals(pool); err != nil {
|
if err := validatePoolInternals(pool); err != nil {
|
||||||
t.Fatalf("pool internal state corrupted: %v", err)
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that adding an underpriced transaction on block limit fails
|
// 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) {
|
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)
|
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 {
|
if err := validatePoolInternals(pool); err != nil {
|
||||||
t.Fatalf("pool internal state corrupted: %v", err)
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that adding local transactions can push out even higher priced ones
|
// Ensure that adding local transactions can push out even higher priced ones
|
||||||
ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
|
ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
|
||||||
if err := pool.AddLocal(ltx); err != nil {
|
if err := pool.AddLocal(ltx); err != nil {
|
||||||
|
|
@ -2263,6 +2266,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
|
||||||
if err := validatePoolInternals(pool); err != nil {
|
if err := validatePoolInternals(pool); err != nil {
|
||||||
t.Fatalf("pool internal state corrupted: %v", err)
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that adding local transactions can push out even higher priced ones
|
// Ensure that adding local transactions can push out even higher priced ones
|
||||||
ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2])
|
ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2])
|
||||||
if err := pool.AddLocal(ltx); err != nil {
|
if err := pool.AddLocal(ltx); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,44 @@ func (h *Header) EmptyReceipts() bool {
|
||||||
return h.ReceiptHash == EmptyReceiptsHash
|
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
|
// Body is a simple (mutable, non-safe) data container for storing and moving
|
||||||
// a block's data contents (transactions and uncles) together.
|
// a block's data contents (transactions and uncles) together.
|
||||||
type Body struct {
|
type Body struct {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||||
ContractAddress common.Address `json:"contractAddress"`
|
ContractAddress common.Address `json:"contractAddress"`
|
||||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"`
|
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
|
||||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||||
|
|
@ -40,7 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
enc.TxHash = r.TxHash
|
enc.TxHash = r.TxHash
|
||||||
enc.ContractAddress = r.ContractAddress
|
enc.ContractAddress = r.ContractAddress
|
||||||
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
||||||
enc.EffectiveGasPrice = r.EffectiveGasPrice
|
enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice)
|
||||||
enc.BlockHash = r.BlockHash
|
enc.BlockHash = r.BlockHash
|
||||||
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
||||||
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
||||||
|
|
@ -59,7 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
||||||
ContractAddress *common.Address `json:"contractAddress"`
|
ContractAddress *common.Address `json:"contractAddress"`
|
||||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"`
|
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
|
||||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
||||||
|
|
@ -101,7 +101,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
}
|
}
|
||||||
r.GasUsed = uint64(*dec.GasUsed)
|
r.GasUsed = uint64(*dec.GasUsed)
|
||||||
if dec.EffectiveGasPrice != nil {
|
if dec.EffectiveGasPrice != nil {
|
||||||
r.EffectiveGasPrice = dec.EffectiveGasPrice
|
r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice)
|
||||||
}
|
}
|
||||||
if dec.BlockHash != nil {
|
if dec.BlockHash != nil {
|
||||||
r.BlockHash = *dec.BlockHash
|
r.BlockHash = *dec.BlockHash
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ type receiptMarshaling struct {
|
||||||
PostState hexutil.Bytes
|
PostState hexutil.Bytes
|
||||||
Status hexutil.Uint64
|
Status hexutil.Uint64
|
||||||
CumulativeGasUsed hexutil.Uint64
|
CumulativeGasUsed hexutil.Uint64
|
||||||
|
EffectiveGasPrice *hexutil.Big
|
||||||
GasUsed hexutil.Uint64
|
GasUsed hexutil.Uint64
|
||||||
BlockNumber *hexutil.Big
|
BlockNumber *hexutil.Big
|
||||||
TransactionIndex hexutil.Uint
|
TransactionIndex hexutil.Uint
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ type Transaction struct {
|
||||||
inner TxData // Consensus contents of a transaction
|
inner TxData // Consensus contents of a transaction
|
||||||
time time.Time // Time first seen locally (spam avoidance)
|
time time.Time // Time first seen locally (spam avoidance)
|
||||||
|
|
||||||
|
// knownAccounts (EIP-4337)
|
||||||
|
optionsAA4337 *OptionsAA4337
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Pointer[common.Hash]
|
hash atomic.Pointer[common.Hash]
|
||||||
size atomic.Pointer[uint64]
|
size atomic.Pointer[uint64]
|
||||||
|
|
@ -101,6 +104,16 @@ type TxData interface {
|
||||||
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
|
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
|
// EncodeRLP implements rlp.Encoder
|
||||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||||
if tx.Type() == LegacyTxType {
|
if tx.Type() == LegacyTxType {
|
||||||
|
|
|
||||||
146
core/types/transaction_conditional.go
Normal file
146
core/types/transaction_conditional.go
Normal 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
|
||||||
|
}
|
||||||
31
core/types/transaction_conditional_test.go
Normal file
31
core/types/transaction_conditional_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -126,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)
|
- ```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
|
### JsonRPC Options
|
||||||
|
|
||||||
- ```rpc.gascap```: Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite) (default: 50000000)
|
- ```rpc.gascap```: Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite) (default: 50000000)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
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)
|
err := b.eth.txPool.AddLocal(signedTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if unwrapped := errors.Unwrap(err); unwrapped != nil {
|
if unwrapped := errors.Unwrap(err); unwrapped != nil {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
|
||||||
|
|
||||||
// Assemble the Ethereum object
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -332,6 +333,15 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
return ethereum, nil
|
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 {
|
func makeExtraData(extra []byte) []byte {
|
||||||
if len(extra) == 0 {
|
if len(extra) == 0 {
|
||||||
// create default extradata
|
// create default extradata
|
||||||
|
|
@ -351,6 +361,7 @@ func makeExtraData(extra []byte) []byte {
|
||||||
return extra
|
return extra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerCount returns the number of connected peers.
|
||||||
func (s *Ethereum) PeerCount() int {
|
func (s *Ethereum) PeerCount() int {
|
||||||
return s.p2pServer.PeerCount()
|
return s.p2pServer.PeerCount()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,12 @@ type Config struct {
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
DatabaseFreezer string
|
DatabaseFreezer string
|
||||||
|
|
||||||
|
// Database - LevelDB options
|
||||||
|
LevelDbCompactionTableSize uint64
|
||||||
|
LevelDbCompactionTableSizeMultiplier float64
|
||||||
|
LevelDbCompactionTotalSize uint64
|
||||||
|
LevelDbCompactionTotalSizeMultiplier float64
|
||||||
|
|
||||||
TrieCleanCache int
|
TrieCleanCache int
|
||||||
TrieCleanCacheJournal string `toml:",omitempty"` // Disk journal directory for trie cache to survive node restarts
|
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
|
TrieCleanCacheRejournal time.Duration `toml:",omitempty"` // Time interval to regenerate the journal for clean cache
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
|
||||||
|
|
||||||
b.Log("Running bloombits benchmark section size:", sectionSize)
|
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 {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", benchDataDir, err)
|
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++ {
|
for i := 0; i < benchFilterCnt; i++ {
|
||||||
if i%20 == 0 {
|
if i%20 == 0 {
|
||||||
db.Close()
|
db.Close()
|
||||||
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
|
db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false, rawdb.ExtraDBConfig{})
|
||||||
backend = &testBackend{db: db, sections: cnt}
|
backend = &testBackend{db: db, sections: cnt}
|
||||||
sys = NewFilterSystem(backend, Config{})
|
sys = NewFilterSystem(backend, Config{})
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +187,7 @@ func BenchmarkNoBloomBits(b *testing.B) {
|
||||||
|
|
||||||
b.Log("Running benchmark without bloombits")
|
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 {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", benchDataDir, err)
|
b.Fatalf("error opening database at %v: %v", benchDataDir, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func makeReceipt(addr common.Address) *types.Receipt {
|
||||||
|
|
||||||
func BenchmarkFilters(b *testing.B) {
|
func BenchmarkFilters(b *testing.B) {
|
||||||
var (
|
var (
|
||||||
db, _ = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false)
|
db, _ = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false, rawdb.ExtraDBConfig{})
|
||||||
_, sys = newTestFilterSystem(b, db, Config{})
|
_, sys = newTestFilterSystem(b, db, Config{})
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
|
@ -106,7 +106,7 @@ func BenchmarkFilters(b *testing.B) {
|
||||||
|
|
||||||
func TestFilters(t *testing.T) {
|
func TestFilters(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db, _ = rawdb.NewLevelDBDatabase(t.TempDir(), 0, 0, "", false)
|
db, _ = rawdb.NewLevelDBDatabase(t.TempDir(), 0, 0, "", false, rawdb.ExtraDBConfig{})
|
||||||
_, sys = newTestFilterSystem(t, db, Config{})
|
_, sys = newTestFilterSystem(t, db, Config{})
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr = crypto.PubkeyToAddress(key1.PublicKey)
|
addr = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,10 @@ func (p *Peer) broadcastTransactions() {
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
|
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)
|
txs = append(txs, tx)
|
||||||
size += common.StorageSize(tx.Size())
|
size += common.StorageSize(tx.Size())
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +161,10 @@ func (p *Peer) announceTransactions() {
|
||||||
)
|
)
|
||||||
|
|
||||||
for count = 0; count < len(queue) && size < maxTxPacketSize; count++ {
|
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])
|
pending = append(pending, queue[count])
|
||||||
pendingTypes = append(pendingTypes, tx.Type())
|
pendingTypes = append(pendingTypes, tx.Type())
|
||||||
pendingSizes = append(pendingSizes, uint32(tx.Size()))
|
pendingSizes = append(pendingSizes, uint32(tx.Size()))
|
||||||
|
|
|
||||||
|
|
@ -83,9 +83,16 @@ type Database struct {
|
||||||
log log.Logger // Contextual logger tracking the database path
|
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
|
// New returns a wrapped LevelDB object. The namespace is the prefix that the
|
||||||
// metrics reporting should use for surfacing internal stats.
|
// 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) {
|
return NewCustom(file, namespace, func(options *opt.Options) {
|
||||||
// Ensure we have some minimal caching and file guarantees
|
// Ensure we have some minimal caching and file guarantees
|
||||||
if cache < minCache {
|
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.BlockCacheCapacity = cache / 2 * opt.MiB
|
||||||
options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally
|
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 {
|
if readonly {
|
||||||
options.ReadOnly = true
|
options.ReadOnly = true
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +137,14 @@ func NewCustom(file string, namespace string, customize func(options *opt.Option
|
||||||
logger := log.New("database", file)
|
logger := log.New("database", file)
|
||||||
usedCache := options.GetBlockCacheCapacity() + options.GetWriteBuffer()*2
|
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 {
|
if options.ReadOnly {
|
||||||
logCtx = append(logCtx, "readonly", "true")
|
logCtx = append(logCtx, "readonly", "true")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,21 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/internal/cli/flagset"
|
"github.com/ethereum/go-ethereum/internal/cli/flagset"
|
||||||
"github.com/ethereum/go-ethereum/internal/cli/server"
|
"github.com/ethereum/go-ethereum/internal/cli/server"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"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/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
|
|
@ -26,14 +30,16 @@ import (
|
||||||
type BootnodeCommand struct {
|
type BootnodeCommand struct {
|
||||||
UI cli.Ui
|
UI cli.Ui
|
||||||
|
|
||||||
listenAddr string
|
listenAddr string
|
||||||
v5 bool
|
enableMetrics bool
|
||||||
verbosity int
|
prometheusAddr string
|
||||||
logLevel string
|
v5 bool
|
||||||
nat string
|
verbosity int
|
||||||
nodeKey string
|
logLevel string
|
||||||
saveKey string
|
nat string
|
||||||
dryRun bool
|
nodeKey string
|
||||||
|
saveKey string
|
||||||
|
dryRun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Help implements the cli.Command interface
|
// Help implements the cli.Command interface
|
||||||
|
|
@ -60,6 +66,18 @@ func (b *BootnodeCommand) Flags() *flagset.Flagset {
|
||||||
Usage: "listening address of bootnode (<ip>:<port>)",
|
Usage: "listening address of bootnode (<ip>:<port>)",
|
||||||
Value: &b.listenAddr,
|
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{
|
flags.BoolFlag(&flagset.BoolFlag{
|
||||||
Name: "v5",
|
Name: "v5",
|
||||||
Default: false,
|
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)
|
signalCh := make(chan os.Signal, 4)
|
||||||
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func (c *DumpconfigCommand) Synopsis() string {
|
||||||
func (c *DumpconfigCommand) Run(args []string) int {
|
func (c *DumpconfigCommand) Run(args []string) int {
|
||||||
// Initialize an empty command instance to get flags
|
// Initialize an empty command instance to get flags
|
||||||
command := server.Command{}
|
command := server.Command{}
|
||||||
flags := command.Flags()
|
flags := command.Flags(nil)
|
||||||
|
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
|
|
|
||||||
|
|
@ -5,33 +5,42 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Flagset struct {
|
type Flagset struct {
|
||||||
flags []*FlagVar
|
flags map[string]*FlagVar
|
||||||
set *flag.FlagSet
|
set *flag.FlagSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFlagSet(name string) *Flagset {
|
func NewFlagSet(name string) *Flagset {
|
||||||
f := &Flagset{
|
f := &Flagset{
|
||||||
flags: []*FlagVar{},
|
flags: make(map[string]*FlagVar, 0),
|
||||||
set: flag.NewFlagSet(name, flag.ContinueOnError),
|
set: flag.NewFlagSet(name, flag.ContinueOnError),
|
||||||
}
|
}
|
||||||
|
|
||||||
return f
|
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 {
|
type FlagVar struct {
|
||||||
Name string
|
Name string
|
||||||
Usage string
|
Usage string
|
||||||
Group string
|
Group string
|
||||||
Default any
|
Default any
|
||||||
|
Value Updatable
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Flagset) addFlag(fl *FlagVar) {
|
func (f *Flagset) addFlag(fl *FlagVar) {
|
||||||
f.flags = append(f.flags, fl)
|
f.flags[fl.Name] = fl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Flagset) Help() string {
|
func (f *Flagset) Help() string {
|
||||||
|
|
@ -51,9 +60,12 @@ func (f *Flagset) Help() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Flagset) GetAllFlags() []string {
|
func (f *Flagset) GetAllFlags() []string {
|
||||||
flags := []string{}
|
i := 0
|
||||||
for _, flag := range f.flags {
|
flags := make([]string, 0, len(f.flags))
|
||||||
flags = append(flags, flag.Name)
|
|
||||||
|
for name := range f.flags {
|
||||||
|
flags[i] = name
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags
|
return flags
|
||||||
|
|
@ -110,6 +122,33 @@ func (f *Flagset) Args() []string {
|
||||||
return f.set.Args()
|
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 {
|
type BoolFlag struct {
|
||||||
Name string
|
Name string
|
||||||
Usage string
|
Usage string
|
||||||
|
|
@ -118,12 +157,19 @@ type BoolFlag struct {
|
||||||
Group string
|
Group string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BoolFlag) UpdateValue(value string) {
|
||||||
|
v, _ := strconv.ParseBool(value)
|
||||||
|
|
||||||
|
*b.Value = v
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) BoolFlag(b *BoolFlag) {
|
func (f *Flagset) BoolFlag(b *BoolFlag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: b.Name,
|
Name: b.Name,
|
||||||
Usage: b.Usage,
|
Usage: b.Usage,
|
||||||
Group: b.Group,
|
Group: b.Group,
|
||||||
Default: b.Default,
|
Default: b.Default,
|
||||||
|
Value: b,
|
||||||
})
|
})
|
||||||
f.set.BoolVar(b.Value, b.Name, b.Default, b.Usage)
|
f.set.BoolVar(b.Value, b.Name, b.Default, b.Usage)
|
||||||
}
|
}
|
||||||
|
|
@ -137,6 +183,10 @@ type StringFlag struct {
|
||||||
HideDefaultFromDoc bool
|
HideDefaultFromDoc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *StringFlag) UpdateValue(value string) {
|
||||||
|
*b.Value = value
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) StringFlag(b *StringFlag) {
|
func (f *Flagset) StringFlag(b *StringFlag) {
|
||||||
if b.Default == "" || b.HideDefaultFromDoc {
|
if b.Default == "" || b.HideDefaultFromDoc {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
|
|
@ -144,6 +194,7 @@ func (f *Flagset) StringFlag(b *StringFlag) {
|
||||||
Usage: b.Usage,
|
Usage: b.Usage,
|
||||||
Group: b.Group,
|
Group: b.Group,
|
||||||
Default: nil,
|
Default: nil,
|
||||||
|
Value: b,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
|
|
@ -151,6 +202,7 @@ func (f *Flagset) StringFlag(b *StringFlag) {
|
||||||
Usage: b.Usage,
|
Usage: b.Usage,
|
||||||
Group: b.Group,
|
Group: b.Group,
|
||||||
Default: b.Default,
|
Default: b.Default,
|
||||||
|
Value: b,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,12 +217,19 @@ type IntFlag struct {
|
||||||
Group string
|
Group string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *IntFlag) UpdateValue(value string) {
|
||||||
|
v, _ := strconv.ParseInt(value, 10, 64)
|
||||||
|
|
||||||
|
*b.Value = int(v)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) IntFlag(i *IntFlag) {
|
func (f *Flagset) IntFlag(i *IntFlag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: i.Name,
|
Name: i.Name,
|
||||||
Usage: i.Usage,
|
Usage: i.Usage,
|
||||||
Group: i.Group,
|
Group: i.Group,
|
||||||
Default: i.Default,
|
Default: i.Default,
|
||||||
|
Value: i,
|
||||||
})
|
})
|
||||||
f.set.IntVar(i.Value, i.Name, i.Default, i.Usage)
|
f.set.IntVar(i.Value, i.Name, i.Default, i.Usage)
|
||||||
}
|
}
|
||||||
|
|
@ -183,12 +242,19 @@ type Uint64Flag struct {
|
||||||
Group string
|
Group string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Uint64Flag) UpdateValue(value string) {
|
||||||
|
v, _ := strconv.ParseUint(value, 10, 64)
|
||||||
|
|
||||||
|
*b.Value = v
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) Uint64Flag(i *Uint64Flag) {
|
func (f *Flagset) Uint64Flag(i *Uint64Flag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: i.Name,
|
Name: i.Name,
|
||||||
Usage: i.Usage,
|
Usage: i.Usage,
|
||||||
Group: i.Group,
|
Group: i.Group,
|
||||||
Default: fmt.Sprintf("%d", i.Default),
|
Default: fmt.Sprintf("%d", i.Default),
|
||||||
|
Value: i,
|
||||||
})
|
})
|
||||||
f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage)
|
f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage)
|
||||||
}
|
}
|
||||||
|
|
@ -209,31 +275,47 @@ func (b *BigIntFlag) String() string {
|
||||||
return b.Value.String()
|
return b.Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BigIntFlag) Set(value string) error {
|
func parseBigInt(value string) *big.Int {
|
||||||
num := new(big.Int)
|
num := new(big.Int)
|
||||||
|
|
||||||
var ok bool
|
|
||||||
if strings.HasPrefix(value, "0x") {
|
if strings.HasPrefix(value, "0x") {
|
||||||
num, ok = num.SetString(value[2:], 16)
|
num, _ = num.SetString(value[2:], 16)
|
||||||
*b.Value = *num
|
|
||||||
} else {
|
} else {
|
||||||
num, ok = num.SetString(value, 10)
|
num, _ = num.SetString(value, 10)
|
||||||
*b.Value = *num
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
return fmt.Errorf("failed to set big int")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*b.Value = *num
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BigIntFlag) UpdateValue(value string) {
|
||||||
|
num := parseBigInt(value)
|
||||||
|
|
||||||
|
if num == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*b.Value = *num
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) BigIntFlag(b *BigIntFlag) {
|
func (f *Flagset) BigIntFlag(b *BigIntFlag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: b.Name,
|
Name: b.Name,
|
||||||
Usage: b.Usage,
|
Usage: b.Usage,
|
||||||
Group: b.Group,
|
Group: b.Group,
|
||||||
Default: b.Default,
|
Default: b.Default,
|
||||||
|
Value: b,
|
||||||
})
|
})
|
||||||
f.set.Var(b, b.Name, b.Usage)
|
f.set.Var(b, b.Name, b.Usage)
|
||||||
}
|
}
|
||||||
|
|
@ -273,6 +355,10 @@ func (i *SliceStringFlag) Set(value string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *SliceStringFlag) UpdateValue(value string) {
|
||||||
|
*i.Value = SplitAndTrim(value)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
|
func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
|
||||||
if s.Default == nil || len(s.Default) == 0 {
|
if s.Default == nil || len(s.Default) == 0 {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
|
|
@ -280,6 +366,7 @@ func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
|
||||||
Usage: s.Usage,
|
Usage: s.Usage,
|
||||||
Group: s.Group,
|
Group: s.Group,
|
||||||
Default: nil,
|
Default: nil,
|
||||||
|
Value: s,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
|
|
@ -287,6 +374,7 @@ func (f *Flagset) SliceStringFlag(s *SliceStringFlag) {
|
||||||
Usage: s.Usage,
|
Usage: s.Usage,
|
||||||
Group: s.Group,
|
Group: s.Group,
|
||||||
Default: strings.Join(s.Default, ","),
|
Default: strings.Join(s.Default, ","),
|
||||||
|
Value: s,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,12 +389,19 @@ type DurationFlag struct {
|
||||||
Group string
|
Group string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DurationFlag) UpdateValue(value string) {
|
||||||
|
v, _ := time.ParseDuration(value)
|
||||||
|
|
||||||
|
*d.Value = v
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) DurationFlag(d *DurationFlag) {
|
func (f *Flagset) DurationFlag(d *DurationFlag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
Usage: d.Usage,
|
Usage: d.Usage,
|
||||||
Group: d.Group,
|
Group: d.Group,
|
||||||
Default: d.Default,
|
Default: d.Default,
|
||||||
|
Value: d,
|
||||||
})
|
})
|
||||||
f.set.DurationVar(d.Value, d.Name, d.Default, "")
|
f.set.DurationVar(d.Value, d.Name, d.Default, "")
|
||||||
}
|
}
|
||||||
|
|
@ -336,24 +431,38 @@ func (m *MapStringFlag) String() string {
|
||||||
return formatMapString(*m.Value)
|
return formatMapString(*m.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapStringFlag) Set(value string) error {
|
func parseMap(value string) map[string]string {
|
||||||
if m.Value == nil {
|
m := make(map[string]string)
|
||||||
m.Value = &map[string]string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range strings.Split(value, ",") {
|
for _, t := range strings.Split(value, ",") {
|
||||||
if t != "" {
|
if t != "" {
|
||||||
kv := strings.Split(t, "=")
|
kv := strings.Split(t, "=")
|
||||||
|
|
||||||
if len(kv) == 2 {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MapStringFlag) UpdateValue(value string) {
|
||||||
|
m2 := parseMap(value)
|
||||||
|
*m.Value = m2
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) MapStringFlag(m *MapStringFlag) {
|
func (f *Flagset) MapStringFlag(m *MapStringFlag) {
|
||||||
if m.Default == nil || len(m.Default) == 0 {
|
if m.Default == nil || len(m.Default) == 0 {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
|
|
@ -361,6 +470,7 @@ func (f *Flagset) MapStringFlag(m *MapStringFlag) {
|
||||||
Usage: m.Usage,
|
Usage: m.Usage,
|
||||||
Group: m.Group,
|
Group: m.Group,
|
||||||
Default: nil,
|
Default: nil,
|
||||||
|
Value: m,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
|
|
@ -368,6 +478,7 @@ func (f *Flagset) MapStringFlag(m *MapStringFlag) {
|
||||||
Usage: m.Usage,
|
Usage: m.Usage,
|
||||||
Group: m.Group,
|
Group: m.Group,
|
||||||
Default: formatMapString(m.Default),
|
Default: formatMapString(m.Default),
|
||||||
|
Value: m,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,12 +493,19 @@ type Float64Flag struct {
|
||||||
Group string
|
Group string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Float64Flag) UpdateValue(value string) {
|
||||||
|
v, _ := strconv.ParseFloat(value, 64)
|
||||||
|
|
||||||
|
*f.Value = v
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Flagset) Float64Flag(i *Float64Flag) {
|
func (f *Flagset) Float64Flag(i *Float64Flag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: i.Name,
|
Name: i.Name,
|
||||||
Usage: i.Usage,
|
Usage: i.Usage,
|
||||||
Group: i.Group,
|
Group: i.Group,
|
||||||
Default: i.Default,
|
Default: i.Default,
|
||||||
|
Value: i,
|
||||||
})
|
})
|
||||||
f.set.Float64Var(i.Value, i.Name, i.Default, "")
|
f.set.Float64Var(i.Value, i.Name, i.Default, "")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,176 @@
|
||||||
package flagset
|
package flagset
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFlagsetBool(t *testing.T) {
|
func TestFlagsetBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
f := NewFlagSet("")
|
f := NewFlagSet("")
|
||||||
|
|
||||||
value := false
|
value := true
|
||||||
f.BoolFlag(&BoolFlag{
|
f.BoolFlag(&BoolFlag{
|
||||||
Name: "flag",
|
Name: "flag",
|
||||||
Value: &value,
|
Value: &value,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NoError(t, f.Parse([]string{"--flag", "true"}))
|
// Parse no value, should have default (of datatype)
|
||||||
assert.Equal(t, value, true)
|
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) {
|
func TestFlagsetSliceString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
f := NewFlagSet("")
|
f := NewFlagSet("")
|
||||||
|
|
||||||
value := []string{"a", "b", "c"}
|
value := []string{"a", "b", "c"}
|
||||||
|
|
@ -30,13 +180,25 @@ func TestFlagsetSliceString(t *testing.T) {
|
||||||
Default: value,
|
Default: value,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NoError(t, f.Parse([]string{}))
|
// Parse no value, should have initial value
|
||||||
assert.Equal(t, value, []string{"a", "b", "c"})
|
require.NoError(t, f.Parse([]string{}))
|
||||||
assert.NoError(t, f.Parse([]string{"--flag", "a,b"}))
|
require.Equal(t, []string{"a", "b", "c"}, value)
|
||||||
assert.Equal(t, value, []string{"a", "b"})
|
|
||||||
|
// 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) {
|
func TestFlagsetDuration(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
f := NewFlagSet("")
|
f := NewFlagSet("")
|
||||||
|
|
||||||
value := time.Duration(0)
|
value := time.Duration(0)
|
||||||
|
|
@ -45,11 +207,25 @@ func TestFlagsetDuration(t *testing.T) {
|
||||||
Value: &value,
|
Value: &value,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NoError(t, f.Parse([]string{"--flag", "1m"}))
|
// Parse no value, should have initial value
|
||||||
assert.Equal(t, value, 1*time.Minute)
|
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) {
|
func TestFlagsetMapString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
f := NewFlagSet("")
|
f := NewFlagSet("")
|
||||||
|
|
||||||
value := map[string]string{}
|
value := map[string]string{}
|
||||||
|
|
@ -58,6 +234,18 @@ func TestFlagsetMapString(t *testing.T) {
|
||||||
Value: &value,
|
Value: &value,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NoError(t, f.Parse([]string{"--flag", "a=b,c=d"}))
|
// Parse no value, should have initial value
|
||||||
assert.Equal(t, value, map[string]string{"a": "b", "c": "d"})
|
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"}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ var mainnetBor = &Chain{
|
||||||
Alloc: readPrealloc("allocs/mainnet.json"),
|
Alloc: readPrealloc("allocs/mainnet.json"),
|
||||||
},
|
},
|
||||||
Bootnodes: []string{
|
Bootnodes: []string{
|
||||||
"enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303",
|
"enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303",
|
||||||
"enode://88116f4295f5a31538ae409e4d44ad40d22e44ee9342869e7d68bdec55b0f83c1530355ce8b41fbec0928a7d75a5745d528450d30aec92066ab6ba1ee351d710@159.203.9.164:30303",
|
"enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ var mumbaiTestnet = &Chain{
|
||||||
Alloc: readPrealloc("allocs/mumbai.json"),
|
Alloc: readPrealloc("allocs/mumbai.json"),
|
||||||
},
|
},
|
||||||
Bootnodes: []string{
|
Bootnodes: []string{
|
||||||
"enode://320553cda00dfc003f499a3ce9598029f364fbb3ed1222fdc20a94d97dcc4d8ba0cd0bfa996579dcc6d17a534741fb0a5da303a90579431259150de66b597251@54.147.31.250:30303",
|
"enode://bdcd4786a616a853b8a041f53496d853c68d99d54ff305615cd91c03cd56895e0a7f6e9f35dbf89131044e2114a9a782b792b5661e3aff07faf125a98606a071@43.200.206.40:30303",
|
||||||
"enode://f0f48a8781629f95ff02606081e6e43e4aebd503f3d07fc931fad7dd5ca1ba52bd849a6f6c3be0e375cf13c9ae04d859c4a9ae3546dc8ed4f10aa5dbb47d4998@34.226.134.117:30303",
|
"enode://209aaf7ed549cf4a5700fd833da25413f80a1248bd3aa7fe2a87203e3f7b236dd729579e5c8df61c97bf508281bae4969d6de76a7393bcbd04a0af70270333b3@54.216.248.9:30303",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func (c *Command) MarkDown() string {
|
||||||
items := []string{
|
items := []string{
|
||||||
"# Server",
|
"# Server",
|
||||||
"The ```bor server``` command runs the Bor client.",
|
"The ```bor server``` command runs the Bor client.",
|
||||||
c.Flags().MarkDown(),
|
c.Flags(nil).MarkDown(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(items, "\n\n")
|
return strings.Join(items, "\n\n")
|
||||||
|
|
@ -46,7 +46,7 @@ func (c *Command) Help() string {
|
||||||
return `Usage: bor [options]
|
return `Usage: bor [options]
|
||||||
|
|
||||||
Run the Bor server.
|
Run the Bor server.
|
||||||
` + c.Flags().Help()
|
` + c.Flags(nil).Help()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synopsis implements the cli.Command interface
|
// Synopsis implements the cli.Command interface
|
||||||
|
|
@ -54,40 +54,69 @@ func (c *Command) Synopsis() string {
|
||||||
return "Run the Bor server"
|
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 {
|
func (c *Command) extractFlags(args []string) error {
|
||||||
config := *DefaultConfig()
|
// Check if config file is provided or not
|
||||||
|
configFilePath := checkConfigFlag(args)
|
||||||
|
|
||||||
flags := c.Flags()
|
if configFilePath != "" {
|
||||||
if err := flags.Parse(args); err != nil {
|
log.Info("Reading config file", "path", configFilePath)
|
||||||
c.UI.Error(err.Error())
|
|
||||||
c.config = &config
|
|
||||||
|
|
||||||
return err
|
// Parse the config file
|
||||||
}
|
cfg, err := readConfigFile(configFilePath)
|
||||||
|
|
||||||
// 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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
c.config = &config
|
|
||||||
|
|
||||||
return err
|
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.UI.Error(err.Error())
|
||||||
c.config = &config
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -95,33 +124,33 @@ func (c *Command) extractFlags(args []string) error {
|
||||||
|
|
||||||
// nolint: nestif
|
// nolint: nestif
|
||||||
// check for log-level and verbosity here
|
// check for log-level and verbosity here
|
||||||
if c.configFile != "" {
|
if configFilePath != "" {
|
||||||
data, _ := toml.LoadFile(c.configFile)
|
data, _ := toml.LoadFile(configFilePath)
|
||||||
if data.Has("verbosity") && data.Has("log-level") {
|
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"))
|
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") {
|
} 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"))
|
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 {
|
} else {
|
||||||
tempFlag := 0
|
tempFlag := 0
|
||||||
|
|
||||||
for _, val := range args {
|
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
|
tempFlag = 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tempFlag == 1 {
|
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)
|
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 && config.LogLevel != "" {
|
} 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", config.LogLevel)
|
log.Warn("Only log-level flag is provided, note that log-level will be deprecated soon. Use verbosity instead.", "using", c.cliConfig.LogLevel)
|
||||||
config.Verbosity = VerbosityStringToInt(strings.ToLower(config.LogLevel))
|
c.cliConfig.Verbosity = VerbosityStringToInt(strings.ToLower(c.cliConfig.LogLevel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.config = &config
|
c.config = c.cliConfig
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,43 +8,135 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"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()
|
t.Parallel()
|
||||||
|
|
||||||
var c Command
|
var c Command
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--txpool.rejournal", "30m0s",
|
"--identity", "",
|
||||||
"--txpool.lifetime", "30m0s",
|
|
||||||
"--miner.gasprice", "20000000000",
|
|
||||||
"--gpo.maxprice", "70000000000",
|
|
||||||
"--gpo.ignoreprice", "1",
|
|
||||||
"--cache.trie.rejournal", "40m0s",
|
|
||||||
"--dev",
|
|
||||||
"--dev.period", "2",
|
|
||||||
"--datadir", "./data",
|
"--datadir", "./data",
|
||||||
"--maxpeers", "30",
|
"--verbosity", "3",
|
||||||
|
"--rpc.batchlimit", "0",
|
||||||
|
"--snapshot",
|
||||||
|
"--bor.logs=false",
|
||||||
"--eth.requiredblocks", "a=b",
|
"--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)
|
err := c.extractFlags(args)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
txRe, _ := time.ParseDuration("30m0s")
|
recommit, _ := time.ParseDuration("20s")
|
||||||
txLt, _ := time.ParseDuration("30m0s")
|
evmTimeout, _ := time.ParseDuration("5s")
|
||||||
caRe, _ := time.ParseDuration("40m0s")
|
|
||||||
|
|
||||||
|
require.Equal(t, c.config.Identity, "")
|
||||||
require.Equal(t, c.config.DataDir, "./data")
|
require.Equal(t, c.config.DataDir, "./data")
|
||||||
require.Equal(t, c.config.Developer.Enabled, true)
|
require.Equal(t, c.config.Verbosity, 3)
|
||||||
require.Equal(t, c.config.Developer.Period, uint64(2))
|
require.Equal(t, c.config.RPCBatchLimit, uint64(0))
|
||||||
require.Equal(t, c.config.TxPool.Rejournal, txRe)
|
require.Equal(t, c.config.Snapshot, true)
|
||||||
require.Equal(t, c.config.TxPool.LifeTime, txLt)
|
require.Equal(t, c.config.BorLogs, false)
|
||||||
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.RequiredBlocks, map[string]string{"a": "b"})
|
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))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,8 @@ type Config struct {
|
||||||
// Cache has the cache related settings
|
// Cache has the cache related settings
|
||||||
Cache *CacheConfig `hcl:"cache,block" toml:"cache,block"`
|
Cache *CacheConfig `hcl:"cache,block" toml:"cache,block"`
|
||||||
|
|
||||||
|
ExtraDB *ExtraDBConfig `hcl:"leveldb,block" toml:"leveldb,block"`
|
||||||
|
|
||||||
// Account has the validator account related settings
|
// Account has the validator account related settings
|
||||||
Accounts *AccountsConfig `hcl:"accounts,block" toml:"accounts,block"`
|
Accounts *AccountsConfig `hcl:"accounts,block" toml:"accounts,block"`
|
||||||
|
|
||||||
|
|
@ -551,6 +553,13 @@ type CacheConfig struct {
|
||||||
FDLimit int `hcl:"fdlimit,optional" toml:"fdlimit,optional"`
|
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 {
|
type AccountsConfig struct {
|
||||||
// Unlock is the list of addresses to unlock in the node
|
// Unlock is the list of addresses to unlock in the node
|
||||||
Unlock []string `hcl:"unlock,optional" toml:"unlock,optional"`
|
Unlock []string `hcl:"unlock,optional" toml:"unlock,optional"`
|
||||||
|
|
@ -742,6 +751,14 @@ func DefaultConfig() *Config {
|
||||||
TrieTimeout: 60 * time.Minute,
|
TrieTimeout: 60 * time.Minute,
|
||||||
FDLimit: 0,
|
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{
|
Accounts: &AccountsConfig{
|
||||||
Unlock: []string{},
|
Unlock: []string{},
|
||||||
PasswordFile: "",
|
PasswordFile: "",
|
||||||
|
|
@ -1101,6 +1118,14 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
|
||||||
n.TriesInMemory = c.Cache.TriesInMemory
|
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
|
n.RPCGasCap = c.JsonRPC.GasCap
|
||||||
if n.RPCGasCap != 0 {
|
if n.RPCGasCap != 0 {
|
||||||
log.Info("Set global gas cap", "cap", n.RPCGasCap)
|
log.Info("Set global gas cap", "cap", n.RPCGasCap)
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,23 @@ func TestConfigLegacy(t *testing.T) {
|
||||||
|
|
||||||
testConfig := DefaultConfig()
|
testConfig := DefaultConfig()
|
||||||
|
|
||||||
|
testConfig.Identity = ""
|
||||||
testConfig.DataDir = "./data"
|
testConfig.DataDir = "./data"
|
||||||
testConfig.Snapshot = false
|
testConfig.Verbosity = 3
|
||||||
|
testConfig.RPCBatchLimit = 0
|
||||||
|
testConfig.Snapshot = true
|
||||||
|
testConfig.BorLogs = false
|
||||||
testConfig.RequiredBlocks = map[string]string{
|
testConfig.RequiredBlocks = map[string]string{
|
||||||
"31000000": "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e",
|
"31000000": "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e",
|
||||||
"32000000": "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68",
|
"32000000": "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68",
|
||||||
}
|
}
|
||||||
testConfig.P2P.MaxPeers = 30
|
testConfig.Sealer.GasPrice = big.NewInt(30000000000)
|
||||||
testConfig.TxPool.Locals = []string{}
|
testConfig.Sealer.Recommit = 20 * time.Second
|
||||||
testConfig.TxPool.LifeTime = time.Second
|
testConfig.JsonRPC.RPCEVMTimeout = 5 * time.Second
|
||||||
testConfig.Sealer.Enabled = true
|
testConfig.JsonRPC.TxFeeCap = 6.0
|
||||||
testConfig.Sealer.GasCeil = 30000000
|
testConfig.JsonRPC.Http.API = []string{"eth", "bor"}
|
||||||
testConfig.Sealer.GasPrice = big.NewInt(1000000000)
|
testConfig.JsonRPC.Ws.API = []string{""}
|
||||||
testConfig.Gpo.IgnorePrice = big.NewInt(4)
|
testConfig.Gpo.MaxPrice = big.NewInt(5000000000000)
|
||||||
testConfig.Cache.Cache = 1024
|
|
||||||
testConfig.Cache.Rejournal = time.Second
|
|
||||||
|
|
||||||
assert.Equal(t, expectedConfig, testConfig)
|
assert.Equal(t, expectedConfig, testConfig)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/internal/cli/flagset"
|
"github.com/ethereum/go-ethereum/internal/cli/flagset"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Command) Flags() *flagset.Flagset {
|
func (c *Command) Flags(config *Config) *flagset.Flagset {
|
||||||
c.cliConfig = DefaultConfig()
|
if config != nil {
|
||||||
|
c.cliConfig = config
|
||||||
|
} else {
|
||||||
|
c.cliConfig = DefaultConfig()
|
||||||
|
}
|
||||||
|
|
||||||
f := flagset.NewFlagSet("server")
|
f := flagset.NewFlagSet("server")
|
||||||
|
|
||||||
|
|
@ -448,6 +452,36 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Group: "Cache",
|
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
|
// rpc options
|
||||||
f.Uint64Flag(&flagset.Uint64Flag{
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
Name: "rpc.gascap",
|
Name: "rpc.gascap",
|
||||||
|
|
|
||||||
32
internal/cli/server/testdata/test.toml
vendored
32
internal/cli/server/testdata/test.toml
vendored
|
|
@ -1,25 +1,25 @@
|
||||||
|
identity = ""
|
||||||
datadir = "./data"
|
datadir = "./data"
|
||||||
snapshot = false
|
verbosity = 3
|
||||||
|
"rpc.batchlimit" = 0
|
||||||
|
snapshot = true
|
||||||
|
"bor.logs" = false
|
||||||
|
|
||||||
["eth.requiredblocks"]
|
["eth.requiredblocks"]
|
||||||
"31000000" = "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e"
|
"31000000" = "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e"
|
||||||
"32000000" = "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68"
|
"32000000" = "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68"
|
||||||
|
|
||||||
[p2p]
|
|
||||||
maxpeers = 30
|
|
||||||
|
|
||||||
[txpool]
|
|
||||||
locals = []
|
|
||||||
lifetime = "1s"
|
|
||||||
|
|
||||||
[miner]
|
[miner]
|
||||||
mine = true
|
gasprice = "30000000000"
|
||||||
gaslimit = 30000000
|
recommit = "20s"
|
||||||
gasprice = "1000000000"
|
|
||||||
|
[jsonrpc]
|
||||||
|
evmtimeout = "5s"
|
||||||
|
txfeecap = 6.0
|
||||||
|
[jsonrpc.http]
|
||||||
|
api = ["eth", "bor"]
|
||||||
|
[jsonrpc.ws]
|
||||||
|
api = [""]
|
||||||
|
|
||||||
[gpo]
|
[gpo]
|
||||||
ignoreprice = "4"
|
maxprice = "5000000000000"
|
||||||
|
|
||||||
[cache]
|
|
||||||
cache = 1024
|
|
||||||
rejournal = "1s"
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state/pruner"
|
"github.com/ethereum/go-ethereum/core/state/pruner"
|
||||||
"github.com/ethereum/go-ethereum/internal/cli/flagset"
|
"github.com/ethereum/go-ethereum/internal/cli/flagset"
|
||||||
"github.com/ethereum/go-ethereum/internal/cli/server"
|
"github.com/ethereum/go-ethereum/internal/cli/server"
|
||||||
|
|
@ -161,7 +162,7 @@ func (c *PruneStateCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
chaindb, err := node.OpenDatabaseWithFreezer(chaindataPath, int(c.cache), dbHandles, c.datadirAncient, "", false)
|
chaindb, err := node.OpenDatabaseWithFreezer(chaindataPath, int(c.cache), dbHandles, c.datadirAncient, "", false, rawdb.ExtraDBConfig{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
|
|
|
||||||
|
|
@ -2105,6 +2105,10 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
|
||||||
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if receipt.EffectiveGasPrice == nil {
|
||||||
|
fields["effectiveGasPrice"] = new(hexutil.Big)
|
||||||
|
}
|
||||||
|
|
||||||
// Assign receipt status or post state.
|
// Assign receipt status or post state.
|
||||||
if len(receipt.PostState) > 0 {
|
if len(receipt.PostState) > 0 {
|
||||||
fields["root"] = hexutil.Bytes(receipt.PostState)
|
fields["root"] = hexutil.Bytes(receipt.PostState)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,59 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func testTransactionMarshal(t *testing.T, tests []txData, config *params.ChainConfig) {
|
||||||
|
t.Helper()
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
signer = types.LatestSigner(config)
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
var tx2 types.Transaction
|
||||||
|
|
||||||
|
tx, err := types.SignNewTx(key, signer, tt.Tx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %d: signing failed: %v", i, err)
|
||||||
|
}
|
||||||
|
// Regular transaction
|
||||||
|
if data, err := json.Marshal(tx); err != nil {
|
||||||
|
t.Fatalf("test %d: marshalling failed; %v", i, err)
|
||||||
|
} else if err = tx2.UnmarshalJSON(data); err != nil {
|
||||||
|
t.Fatalf("test %d: sunmarshal failed: %v", i, err)
|
||||||
|
} else if want, have := tx.Hash(), tx2.Hash(); want != have {
|
||||||
|
t.Fatalf("test %d: stx changed, want %x have %x", i, want, have)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpcTransaction
|
||||||
|
rpcTx := newRPCTransaction(tx, common.Hash{}, 0, 0, big.NewInt(0), config)
|
||||||
|
if data, err := json.Marshal(rpcTx); err != nil {
|
||||||
|
t.Fatalf("test %d: marshalling failed; %v", i, err)
|
||||||
|
} else if err = tx2.UnmarshalJSON(data); err != nil {
|
||||||
|
t.Fatalf("test %d: unmarshal failed: %v", i, err)
|
||||||
|
} else if want, have := tx.Hash(), tx2.Hash(); want != have {
|
||||||
|
t.Fatalf("test %d: tx changed, want %x have %x", i, want, have)
|
||||||
|
} else {
|
||||||
|
want, have := tt.Want, string(data)
|
||||||
|
require.JSONEqf(t, want, have, "test %d: rpc json not match, want %s have %s", i, want, have)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type txData struct {
|
||||||
|
Tx types.TxData
|
||||||
|
Want string
|
||||||
|
}
|
||||||
|
|
||||||
func TestTransaction_RoundTripRpcJSON(t *testing.T) {
|
func TestTransaction_RoundTripRpcJSON(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
config = params.AllEthashProtocolChanges
|
config = params.AllEthashProtocolChanges
|
||||||
|
|
@ -35,12 +82,12 @@ func TestTransaction_RoundTripRpcJSON(t *testing.T) {
|
||||||
tests = allTransactionTypes(common.Address{0xde, 0xad}, config)
|
tests = allTransactionTypes(common.Address{0xde, 0xad}, config)
|
||||||
)
|
)
|
||||||
|
|
||||||
t.Parallel()
|
testTransactionMarshal(t, tests, config)
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
var tx2 types.Transaction
|
var tx2 types.Transaction
|
||||||
|
|
||||||
tx, err := types.SignNewTx(key, signer, tt)
|
tx, err := types.SignNewTx(key, signer, tt.Tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: signing failed: %v", i, err)
|
t.Fatalf("test %d: signing failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -65,98 +112,243 @@ func TestTransaction_RoundTripRpcJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func allTransactionTypes(addr common.Address, config *params.ChainConfig) []types.TxData {
|
func allTransactionTypes(addr common.Address, config *params.ChainConfig) []txData {
|
||||||
return []types.TxData{
|
return []txData{
|
||||||
&types.LegacyTx{
|
{
|
||||||
Nonce: 5,
|
Tx: &types.LegacyTx{
|
||||||
GasPrice: big.NewInt(6),
|
Nonce: 5,
|
||||||
Gas: 7,
|
GasPrice: big.NewInt(6),
|
||||||
To: &addr,
|
Gas: 7,
|
||||||
Value: big.NewInt(8),
|
To: &addr,
|
||||||
Data: []byte{0, 1, 2, 3, 4},
|
Value: big.NewInt(8),
|
||||||
V: big.NewInt(9),
|
Data: []byte{0, 1, 2, 3, 4},
|
||||||
R: big.NewInt(10),
|
V: big.NewInt(9),
|
||||||
S: big.NewInt(11),
|
R: big.NewInt(10),
|
||||||
},
|
S: big.NewInt(11),
|
||||||
&types.LegacyTx{
|
|
||||||
Nonce: 5,
|
|
||||||
GasPrice: big.NewInt(6),
|
|
||||||
Gas: 7,
|
|
||||||
To: nil,
|
|
||||||
Value: big.NewInt(8),
|
|
||||||
Data: []byte{0, 1, 2, 3, 4},
|
|
||||||
V: big.NewInt(32),
|
|
||||||
R: big.NewInt(10),
|
|
||||||
S: big.NewInt(11),
|
|
||||||
},
|
|
||||||
&types.AccessListTx{
|
|
||||||
ChainID: config.ChainID,
|
|
||||||
Nonce: 5,
|
|
||||||
GasPrice: big.NewInt(6),
|
|
||||||
Gas: 7,
|
|
||||||
To: &addr,
|
|
||||||
Value: big.NewInt(8),
|
|
||||||
Data: []byte{0, 1, 2, 3, 4},
|
|
||||||
AccessList: types.AccessList{
|
|
||||||
types.AccessTuple{
|
|
||||||
Address: common.Address{0x2},
|
|
||||||
StorageKeys: []common.Hash{types.EmptyRootHash},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
V: big.NewInt(32),
|
Want: `{
|
||||||
R: big.NewInt(10),
|
"blockHash": null,
|
||||||
S: big.NewInt(11),
|
"blockNumber": null,
|
||||||
},
|
"from": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
&types.AccessListTx{
|
"gas": "0x7",
|
||||||
ChainID: config.ChainID,
|
"gasPrice": "0x6",
|
||||||
Nonce: 5,
|
"hash": "0x5f3240454cd09a5d8b1c5d651eefae7a339262875bcd2d0e6676f3d989967008",
|
||||||
GasPrice: big.NewInt(6),
|
"input": "0x0001020304",
|
||||||
Gas: 7,
|
"nonce": "0x5",
|
||||||
To: nil,
|
"to": "0xdead000000000000000000000000000000000000",
|
||||||
Value: big.NewInt(8),
|
"transactionIndex": null,
|
||||||
Data: []byte{0, 1, 2, 3, 4},
|
"value": "0x8",
|
||||||
AccessList: types.AccessList{
|
"type": "0x0",
|
||||||
types.AccessTuple{
|
"chainId": "0x539",
|
||||||
Address: common.Address{0x2},
|
"v": "0xa96",
|
||||||
StorageKeys: []common.Hash{types.EmptyRootHash},
|
"r": "0xbc85e96592b95f7160825d837abb407f009df9ebe8f1b9158a4b8dd093377f75",
|
||||||
},
|
"s": "0x1b55ea3af5574c536967b039ba6999ef6c89cf22fc04bcb296e0e8b0b9b576f5"
|
||||||
|
}`,
|
||||||
|
}, {
|
||||||
|
Tx: &types.LegacyTx{
|
||||||
|
Nonce: 5,
|
||||||
|
GasPrice: big.NewInt(6),
|
||||||
|
Gas: 7,
|
||||||
|
To: nil,
|
||||||
|
Value: big.NewInt(8),
|
||||||
|
Data: []byte{0, 1, 2, 3, 4},
|
||||||
|
V: big.NewInt(32),
|
||||||
|
R: big.NewInt(10),
|
||||||
|
S: big.NewInt(11),
|
||||||
},
|
},
|
||||||
V: big.NewInt(32),
|
Want: `{
|
||||||
R: big.NewInt(10),
|
"blockHash": null,
|
||||||
S: big.NewInt(11),
|
"blockNumber": null,
|
||||||
|
"from": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
|
"gas": "0x7",
|
||||||
|
"gasPrice": "0x6",
|
||||||
|
"hash": "0x806e97f9d712b6cb7e781122001380a2837531b0fc1e5f5d78174ad4cb699873",
|
||||||
|
"input": "0x0001020304",
|
||||||
|
"nonce": "0x5",
|
||||||
|
"to": null,
|
||||||
|
"transactionIndex": null,
|
||||||
|
"value": "0x8",
|
||||||
|
"type": "0x0",
|
||||||
|
"chainId": "0x539",
|
||||||
|
"v": "0xa96",
|
||||||
|
"r": "0x9dc28b267b6ad4e4af6fe9289668f9305c2eb7a3241567860699e478af06835a",
|
||||||
|
"s": "0xa0b51a071aa9bed2cd70aedea859779dff039e3630ea38497d95202e9b1fec7"
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
&types.DynamicFeeTx{
|
{
|
||||||
ChainID: config.ChainID,
|
Tx: &types.AccessListTx{
|
||||||
Nonce: 5,
|
ChainID: config.ChainID,
|
||||||
GasTipCap: big.NewInt(6),
|
Nonce: 5,
|
||||||
GasFeeCap: big.NewInt(9),
|
GasPrice: big.NewInt(6),
|
||||||
Gas: 7,
|
Gas: 7,
|
||||||
To: &addr,
|
To: &addr,
|
||||||
Value: big.NewInt(8),
|
Value: big.NewInt(8),
|
||||||
Data: []byte{0, 1, 2, 3, 4},
|
Data: []byte{0, 1, 2, 3, 4},
|
||||||
AccessList: types.AccessList{
|
AccessList: types.AccessList{
|
||||||
types.AccessTuple{
|
types.AccessTuple{
|
||||||
Address: common.Address{0x2},
|
Address: common.Address{0x2},
|
||||||
StorageKeys: []common.Hash{types.EmptyRootHash},
|
StorageKeys: []common.Hash{types.EmptyRootHash},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
V: big.NewInt(32),
|
||||||
|
R: big.NewInt(10),
|
||||||
|
S: big.NewInt(11),
|
||||||
},
|
},
|
||||||
V: big.NewInt(32),
|
Want: `{
|
||||||
R: big.NewInt(10),
|
"blockHash": null,
|
||||||
S: big.NewInt(11),
|
"blockNumber": null,
|
||||||
},
|
"from": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
&types.DynamicFeeTx{
|
"gas": "0x7",
|
||||||
ChainID: config.ChainID,
|
"gasPrice": "0x6",
|
||||||
Nonce: 5,
|
"hash": "0x121347468ee5fe0a29f02b49b4ffd1c8342bc4255146bb686cd07117f79e7129",
|
||||||
GasTipCap: big.NewInt(6),
|
"input": "0x0001020304",
|
||||||
GasFeeCap: big.NewInt(9),
|
"nonce": "0x5",
|
||||||
Gas: 7,
|
"to": "0xdead000000000000000000000000000000000000",
|
||||||
To: nil,
|
"transactionIndex": null,
|
||||||
Value: big.NewInt(8),
|
"value": "0x8",
|
||||||
Data: []byte{0, 1, 2, 3, 4},
|
"type": "0x1",
|
||||||
AccessList: types.AccessList{},
|
"accessList": [
|
||||||
V: big.NewInt(32),
|
{
|
||||||
R: big.NewInt(10),
|
"address": "0x0200000000000000000000000000000000000000",
|
||||||
S: big.NewInt(11),
|
"storageKeys": [
|
||||||
|
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chainId": "0x539",
|
||||||
|
"v": "0x0",
|
||||||
|
"r": "0xf372ad499239ae11d91d34c559ffc5dab4daffc0069e03afcabdcdf231a0c16b",
|
||||||
|
"s": "0x28573161d1f9472fa0fd4752533609e72f06414f7ab5588699a7141f65d2abf"
|
||||||
|
}`,
|
||||||
|
}, {
|
||||||
|
Tx: &types.AccessListTx{
|
||||||
|
ChainID: config.ChainID,
|
||||||
|
Nonce: 5,
|
||||||
|
GasPrice: big.NewInt(6),
|
||||||
|
Gas: 7,
|
||||||
|
To: nil,
|
||||||
|
Value: big.NewInt(8),
|
||||||
|
Data: []byte{0, 1, 2, 3, 4},
|
||||||
|
AccessList: types.AccessList{
|
||||||
|
types.AccessTuple{
|
||||||
|
Address: common.Address{0x2},
|
||||||
|
StorageKeys: []common.Hash{types.EmptyRootHash},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
V: big.NewInt(32),
|
||||||
|
R: big.NewInt(10),
|
||||||
|
S: big.NewInt(11),
|
||||||
|
},
|
||||||
|
Want: `{
|
||||||
|
"blockHash": null,
|
||||||
|
"blockNumber": null,
|
||||||
|
"from": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
|
"gas": "0x7",
|
||||||
|
"gasPrice": "0x6",
|
||||||
|
"hash": "0x067c3baebede8027b0f828a9d933be545f7caaec623b00684ac0659726e2055b",
|
||||||
|
"input": "0x0001020304",
|
||||||
|
"nonce": "0x5",
|
||||||
|
"to": null,
|
||||||
|
"transactionIndex": null,
|
||||||
|
"value": "0x8",
|
||||||
|
"type": "0x1",
|
||||||
|
"accessList": [
|
||||||
|
{
|
||||||
|
"address": "0x0200000000000000000000000000000000000000",
|
||||||
|
"storageKeys": [
|
||||||
|
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chainId": "0x539",
|
||||||
|
"v": "0x1",
|
||||||
|
"r": "0x542981b5130d4613897fbab144796cb36d3cb3d7807d47d9c7f89ca7745b085c",
|
||||||
|
"s": "0x7425b9dd6c5deaa42e4ede35d0c4570c4624f68c28d812c10d806ffdf86ce63"
|
||||||
|
}`,
|
||||||
|
}, {
|
||||||
|
Tx: &types.DynamicFeeTx{
|
||||||
|
ChainID: config.ChainID,
|
||||||
|
Nonce: 5,
|
||||||
|
GasTipCap: big.NewInt(6),
|
||||||
|
GasFeeCap: big.NewInt(9),
|
||||||
|
Gas: 7,
|
||||||
|
To: &addr,
|
||||||
|
Value: big.NewInt(8),
|
||||||
|
Data: []byte{0, 1, 2, 3, 4},
|
||||||
|
AccessList: types.AccessList{
|
||||||
|
types.AccessTuple{
|
||||||
|
Address: common.Address{0x2},
|
||||||
|
StorageKeys: []common.Hash{types.EmptyRootHash},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
V: big.NewInt(32),
|
||||||
|
R: big.NewInt(10),
|
||||||
|
S: big.NewInt(11),
|
||||||
|
},
|
||||||
|
Want: `{
|
||||||
|
"blockHash": null,
|
||||||
|
"blockNumber": null,
|
||||||
|
"from": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
|
"gas": "0x7",
|
||||||
|
"gasPrice": "0x9",
|
||||||
|
"maxFeePerGas": "0x9",
|
||||||
|
"maxPriorityFeePerGas": "0x6",
|
||||||
|
"hash": "0xb63e0b146b34c3e9cb7fbabb5b3c081254a7ded6f1b65324b5898cc0545d79ff",
|
||||||
|
"input": "0x0001020304",
|
||||||
|
"nonce": "0x5",
|
||||||
|
"to": "0xdead000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": null,
|
||||||
|
"value": "0x8",
|
||||||
|
"type": "0x2",
|
||||||
|
"accessList": [
|
||||||
|
{
|
||||||
|
"address": "0x0200000000000000000000000000000000000000",
|
||||||
|
"storageKeys": [
|
||||||
|
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chainId": "0x539",
|
||||||
|
"v": "0x1",
|
||||||
|
"r": "0x3b167e05418a8932cd53d7578711fe1a76b9b96c48642402bb94978b7a107e80",
|
||||||
|
"s": "0x22f98a332d15ea2cc80386c1ebaa31b0afebfa79ebc7d039a1e0074418301fef"
|
||||||
|
}`,
|
||||||
|
}, {
|
||||||
|
Tx: &types.DynamicFeeTx{
|
||||||
|
ChainID: config.ChainID,
|
||||||
|
Nonce: 5,
|
||||||
|
GasTipCap: big.NewInt(6),
|
||||||
|
GasFeeCap: big.NewInt(9),
|
||||||
|
Gas: 7,
|
||||||
|
To: nil,
|
||||||
|
Value: big.NewInt(8),
|
||||||
|
Data: []byte{0, 1, 2, 3, 4},
|
||||||
|
AccessList: types.AccessList{},
|
||||||
|
V: big.NewInt(32),
|
||||||
|
R: big.NewInt(10),
|
||||||
|
S: big.NewInt(11),
|
||||||
|
},
|
||||||
|
Want: `{
|
||||||
|
"blockHash": null,
|
||||||
|
"blockNumber": null,
|
||||||
|
"from": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
|
"gas": "0x7",
|
||||||
|
"gasPrice": "0x9",
|
||||||
|
"maxFeePerGas": "0x9",
|
||||||
|
"maxPriorityFeePerGas": "0x6",
|
||||||
|
"hash": "0xcbab17ee031a9d5b5a09dff909f0a28aedb9b295ac0635d8710d11c7b806ec68",
|
||||||
|
"input": "0x0001020304",
|
||||||
|
"nonce": "0x5",
|
||||||
|
"to": null,
|
||||||
|
"transactionIndex": null,
|
||||||
|
"value": "0x8",
|
||||||
|
"type": "0x2",
|
||||||
|
"accessList": [],
|
||||||
|
"chainId": "0x539",
|
||||||
|
"v": "0x0",
|
||||||
|
"r": "0x6446b8a682db7e619fc6b4f6d1f708f6a17351a41c7fbd63665f469bc78b41b9",
|
||||||
|
"s": "0x7626abc15834f391a117c63450047309dbf84c5ce3e8e609b607062641e2de43"
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,9 @@ func GetAPIs(apiBackend Backend) []rpc.API {
|
||||||
}, {
|
}, {
|
||||||
Namespace: "personal",
|
Namespace: "personal",
|
||||||
Service: NewPersonalAccountAPI(apiBackend, nonceLock),
|
Service: NewPersonalAccountAPI(apiBackend, nonceLock),
|
||||||
|
}, {
|
||||||
|
Namespace: "bor",
|
||||||
|
Service: NewBorAPI(apiBackend),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRootHash returns root hash for given start and end block
|
// GetRootHash returns root hash for given start and end block
|
||||||
|
|
@ -51,3 +53,54 @@ func (s *BlockChainAPI) appendRPCMarshalBorTransaction(ctx context.Context, bloc
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BorAPI provides an API to access Bor related information.
|
||||||
|
type BorAPI struct {
|
||||||
|
b Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBorAPI creates a new Bor protocol API.
|
||||||
|
func NewBorAPI(b Backend) *BorAPI {
|
||||||
|
return &BorAPI{b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendRawTransactionConditional will add the signed transaction to the transaction pool.
|
||||||
|
// The sender/bundler is responsible for signing the transaction
|
||||||
|
func (api *BorAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) {
|
||||||
|
tx := new(types.Transaction)
|
||||||
|
if err := tx.UnmarshalBinary(input); err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
currentHeader := api.b.CurrentHeader()
|
||||||
|
currentState, _, _ := api.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentHeader.Number.Int64()))
|
||||||
|
|
||||||
|
// check block number range
|
||||||
|
if err := currentHeader.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil {
|
||||||
|
return common.Hash{}, &rpc.OptionsValidateError{Message: "out of block range. err: " + err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check timestamp range
|
||||||
|
if err := currentHeader.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil {
|
||||||
|
return common.Hash{}, &rpc.OptionsValidateError{Message: "out of time range. err: " + err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check knownAccounts length (number of slots/accounts) should be less than 1000
|
||||||
|
if err := options.KnownAccounts.ValidateLength(); err != nil {
|
||||||
|
return common.Hash{}, &rpc.KnownAccountsLimitExceededError{Message: "limit exceeded. err: " + err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check knownAccounts
|
||||||
|
if err := currentState.ValidateKnownAccounts(options.KnownAccounts); err != nil {
|
||||||
|
return common.Hash{}, &rpc.OptionsValidateError{Message: "storage error. err: " + err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// put options data in Tx, to use it later while block building
|
||||||
|
tx.PutOptions(&options)
|
||||||
|
|
||||||
|
return SubmitTransaction(ctx, api.b, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *BorAPI) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
|
||||||
|
return api.b.GetVoteOnHash(ctx, starBlockNr, endBlockNr, hash, milestoneId)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5383,6 +5383,13 @@ var methods = function () {
|
||||||
inputFormatter: [null]
|
inputFormatter: [null]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var sendRawTransactionConditional = new Method({
|
||||||
|
name: 'sendRawTransactionConditional',
|
||||||
|
call: 'eth_sendRawTransactionConditional',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null]
|
||||||
|
});
|
||||||
|
|
||||||
var sendTransaction = new Method({
|
var sendTransaction = new Method({
|
||||||
name: 'sendTransaction',
|
name: 'sendTransaction',
|
||||||
call: 'eth_sendTransaction',
|
call: 'eth_sendTransaction',
|
||||||
|
|
@ -5471,6 +5478,7 @@ var methods = function () {
|
||||||
call,
|
call,
|
||||||
estimateGas,
|
estimateGas,
|
||||||
sendRawTransaction,
|
sendRawTransaction,
|
||||||
|
sendRawTransactionConditional,
|
||||||
signTransaction,
|
signTransaction,
|
||||||
sendTransaction,
|
sendTransaction,
|
||||||
sign,
|
sign,
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ web3._extend({
|
||||||
call: 'bor_getVoteOnHash',
|
call: 'bor_getVoteOnHash',
|
||||||
params: 4,
|
params: 4,
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'sendRawTransactionConditional',
|
||||||
|
call: 'bor_sendRawTransactionConditional',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,14 @@ type LightEthereum struct {
|
||||||
|
|
||||||
// New creates an instance of the light client.
|
// New creates an instance of the light client.
|
||||||
func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
|
func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
|
||||||
chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false)
|
extraDBConfig := resolveExtraDBConfig(config)
|
||||||
|
|
||||||
|
chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false, extraDBConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lesDb, err := stack.OpenDatabase("les.client", 0, 0, "eth/db/lesclient/", false)
|
lesDb, err := stack.OpenDatabase("les.client", 0, 0, "eth/db/lesclient/", false, extraDBConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -227,6 +229,15 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
|
||||||
return leth, nil
|
return leth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveExtraDBConfig(config *ethconfig.Config) rawdb.ExtraDBConfig {
|
||||||
|
return rawdb.ExtraDBConfig{
|
||||||
|
LevelDBCompactionTableSize: config.LevelDbCompactionTableSize,
|
||||||
|
LevelDBCompactionTableSizeMultiplier: config.LevelDbCompactionTableSizeMultiplier,
|
||||||
|
LevelDBCompactionTotalSize: config.LevelDbCompactionTotalSize,
|
||||||
|
LevelDBCompactionTotalSizeMultiplier: config.LevelDbCompactionTotalSizeMultiplier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VfluxRequest sends a batch of requests to the given node through discv5 UDP TalkRequest and returns the responses
|
// VfluxRequest sends a batch of requests to the given node through discv5 UDP TalkRequest and returns the responses
|
||||||
func (s *LightEthereum) VfluxRequest(n *enode.Node, reqs vflux.Requests) vflux.Replies {
|
func (s *LightEthereum) VfluxRequest(n *enode.Node, reqs vflux.Requests) vflux.Replies {
|
||||||
if !s.udpEnabled {
|
if !s.udpEnabled {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,8 @@ type LesServer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*LesServer, error) {
|
func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*LesServer, error) {
|
||||||
lesDb, err := node.OpenDatabase("les.server", 0, 0, "eth/db/lesserver/", false)
|
dbOptions := resolveExtraDBConfig(config)
|
||||||
|
lesDb, err := node.OpenDatabase("les.server", 0, 0, "eth/db/lesserver/", false, dbOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ type mockBackend struct {
|
||||||
txPool *txpool.TxPool
|
txPool *txpool.TxPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerCount implements Backend.
|
// PeerCount implements mockBackend.
|
||||||
func (*mockBackend) PeerCount() int {
|
func (*mockBackend) PeerCount() int {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,10 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
|
||||||
return miner
|
return miner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (miner *Miner) GetWorker() *worker {
|
||||||
|
return miner.worker
|
||||||
|
}
|
||||||
|
|
||||||
// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
|
// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
|
||||||
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
|
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
|
||||||
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
|
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
|
||||||
|
|
@ -191,7 +195,7 @@ func (miner *Miner) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (miner *Miner) Mining() bool {
|
func (miner *Miner) Mining() bool {
|
||||||
return miner.worker.isRunning()
|
return miner.worker.IsRunning()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (miner *Miner) Hashrate() uint64 {
|
func (miner *Miner) Hashrate() uint64 {
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ type testWorkerBackend struct {
|
||||||
uncleBlock *types.Block
|
uncleBlock *types.Block
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerCount implements Backend.
|
// PeerCount implements testWorkerBackend.
|
||||||
func (*testWorkerBackend) PeerCount() int {
|
func (*testWorkerBackend) PeerCount() int {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +397,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay
|
||||||
// If our sealing block contains less than 2 uncle blocks,
|
// If our sealing block contains less than 2 uncle blocks,
|
||||||
// add the new uncle block if valid and regenerate a new
|
// add the new uncle block if valid and regenerate a new
|
||||||
// sealing block for higher profit.
|
// sealing block for higher profit.
|
||||||
if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
if w.IsRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
|
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
|
||||||
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
|
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
|
||||||
|
|
@ -428,7 +428,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay
|
||||||
// already included in the current sealing block. These transactions will
|
// already included in the current sealing block. These transactions will
|
||||||
// be automatically eliminated.
|
// be automatically eliminated.
|
||||||
// nolint : nestif
|
// nolint : nestif
|
||||||
if !w.isRunning() && w.current != nil {
|
if !w.IsRunning() && w.current != nil {
|
||||||
// If block is already full, abort
|
// If block is already full, abort
|
||||||
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
|
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
|
||||||
continue
|
continue
|
||||||
|
|
@ -486,7 +486,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
||||||
tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
|
tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
|
||||||
// Set the coinbase if the worker is running or it's required
|
// Set the coinbase if the worker is running or it's required
|
||||||
var coinbase common.Address
|
var coinbase common.Address
|
||||||
if w.isRunning() {
|
if w.IsRunning() {
|
||||||
if w.coinbase == (common.Address{}) {
|
if w.coinbase == (common.Address{}) {
|
||||||
log.Error("Refusing to mine without etherbase")
|
log.Error("Refusing to mine without etherbase")
|
||||||
return
|
return
|
||||||
|
|
@ -873,7 +873,7 @@ mainloop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !w.isRunning() && len(coalescedLogs) > 0 {
|
if !w.IsRunning() && len(coalescedLogs) > 0 {
|
||||||
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
||||||
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
|
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
|
||||||
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
|
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
|
||||||
|
|
|
||||||
|
|
@ -487,7 +487,7 @@ func (w *worker) stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// isRunning returns an indicator whether worker is running or not.
|
// isRunning returns an indicator whether worker is running or not.
|
||||||
func (w *worker) isRunning() bool {
|
func (w *worker) IsRunning() bool {
|
||||||
return w.running.Load()
|
return w.running.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -589,7 +589,7 @@ func (w *worker) newWorkLoop(ctx context.Context, recommit time.Duration) {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
// If sealing is running resubmit a new work cycle periodically to pull in
|
// If sealing is running resubmit a new work cycle periodically to pull in
|
||||||
// higher priced transactions. Disable this overhead for pending blocks.
|
// higher priced transactions. Disable this overhead for pending blocks.
|
||||||
if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {
|
if w.IsRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {
|
||||||
// Short circuit if no new transaction arrives.
|
// Short circuit if no new transaction arrives.
|
||||||
if w.newTxs.Load() == 0 {
|
if w.newTxs.Load() == 0 {
|
||||||
timer.Reset(recommit)
|
timer.Reset(recommit)
|
||||||
|
|
@ -691,7 +691,7 @@ func (w *worker) mainLoop(ctx context.Context) {
|
||||||
}
|
}
|
||||||
// If our mining block contains less than 2 uncle blocks,
|
// If our mining block contains less than 2 uncle blocks,
|
||||||
// add the new uncle block if valid and regenerate a mining block.
|
// add the new uncle block if valid and regenerate a mining block.
|
||||||
if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
if w.IsRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
|
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
|
||||||
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
|
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
|
||||||
|
|
@ -721,7 +721,8 @@ func (w *worker) mainLoop(ctx context.Context) {
|
||||||
// Note all transactions received may not be continuous with transactions
|
// Note all transactions received may not be continuous with transactions
|
||||||
// already included in the current sealing block. These transactions will
|
// already included in the current sealing block. These transactions will
|
||||||
// be automatically eliminated.
|
// be automatically eliminated.
|
||||||
if !w.isRunning() && w.current != nil {
|
// nolint : nestif
|
||||||
|
if !w.IsRunning() && w.current != nil {
|
||||||
// If block is already full, abort
|
// If block is already full, abort
|
||||||
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
|
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
|
||||||
continue
|
continue
|
||||||
|
|
@ -1152,6 +1153,31 @@ mainloop:
|
||||||
// during transaction acceptance is the transaction pool.
|
// during transaction acceptance is the transaction pool.
|
||||||
from, _ := types.Sender(env.signer, tx)
|
from, _ := types.Sender(env.signer, tx)
|
||||||
|
|
||||||
|
// not prioritising conditional transaction, yet.
|
||||||
|
//nolint:nestif
|
||||||
|
if options := tx.GetOptions(); options != nil {
|
||||||
|
if err := env.header.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil {
|
||||||
|
log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err)
|
||||||
|
txs.Pop()
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := env.header.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil {
|
||||||
|
log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err)
|
||||||
|
txs.Pop()
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := env.state.ValidateKnownAccounts(options.KnownAccounts); err != nil {
|
||||||
|
log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err)
|
||||||
|
txs.Pop()
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether the tx is replay protected. If we're not in the EIP155 hf
|
// Check whether the tx is replay protected. If we're not in the EIP155 hf
|
||||||
// phase, start ignoring the sender until we do.
|
// phase, start ignoring the sender until we do.
|
||||||
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
|
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
|
||||||
|
|
@ -1217,7 +1243,7 @@ mainloop:
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:nestif
|
// nolint:nestif
|
||||||
if EnableMVHashMap && w.isRunning() {
|
if EnableMVHashMap && w.IsRunning() {
|
||||||
close(chDeps)
|
close(chDeps)
|
||||||
depsWg.Wait()
|
depsWg.Wait()
|
||||||
|
|
||||||
|
|
@ -1278,7 +1304,7 @@ mainloop:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !w.isRunning() && len(coalescedLogs) > 0 {
|
if !w.IsRunning() && len(coalescedLogs) > 0 {
|
||||||
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
||||||
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
|
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
|
||||||
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
|
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
|
||||||
|
|
@ -1692,7 +1718,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *atomic.Int32, noempt
|
||||||
tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
|
tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
|
||||||
// Set the coinbase if the worker is running or it's required
|
// Set the coinbase if the worker is running or it's required
|
||||||
var coinbase common.Address
|
var coinbase common.Address
|
||||||
if w.isRunning() {
|
if w.IsRunning() {
|
||||||
coinbase = w.etherbase()
|
coinbase = w.etherbase()
|
||||||
if coinbase == (common.Address{}) {
|
if coinbase == (common.Address{}) {
|
||||||
log.Error("Refusing to mine without etherbase")
|
log.Error("Refusing to mine without etherbase")
|
||||||
|
|
@ -1807,7 +1833,7 @@ func getInterruptTimer(ctx context.Context, work *environment, current *types.Bl
|
||||||
// Note the assumption is held that the mutation is allowed to the passed env, do
|
// Note the assumption is held that the mutation is allowed to the passed env, do
|
||||||
// the deep copy first.
|
// the deep copy first.
|
||||||
func (w *worker) commit(ctx context.Context, env *environment, interval func(), update bool, start time.Time) error {
|
func (w *worker) commit(ctx context.Context, env *environment, interval func(), update bool, start time.Time) error {
|
||||||
if w.isRunning() {
|
if w.IsRunning() {
|
||||||
ctx, span := tracing.StartSpan(ctx, "commit")
|
ctx, span := tracing.StartSpan(ctx, "commit")
|
||||||
defer tracing.EndSpan(span)
|
defer tracing.EndSpan(span)
|
||||||
|
|
||||||
|
|
|
||||||
18
node/node.go
18
node/node.go
|
|
@ -771,7 +771,7 @@ func (n *Node) EventMux() *event.TypeMux {
|
||||||
// OpenDatabase opens an existing database with the given name (or creates one if no
|
// OpenDatabase opens an existing database with the given name (or creates one if no
|
||||||
// previous can be found) from within the node's instance directory. If the node is
|
// previous can be found) from within the node's instance directory. If the node is
|
||||||
// ephemeral, a memory database is returned.
|
// ephemeral, a memory database is returned.
|
||||||
func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool) (ethdb.Database, error) {
|
func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool, extraDBConfig rawdb.ExtraDBConfig) (ethdb.Database, error) {
|
||||||
n.lock.Lock()
|
n.lock.Lock()
|
||||||
defer n.lock.Unlock()
|
defer n.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -787,12 +787,13 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
} else {
|
} else {
|
||||||
db, err = rawdb.Open(rawdb.OpenOptions{
|
db, err = rawdb.Open(rawdb.OpenOptions{
|
||||||
Type: n.config.DBEngine,
|
Type: n.config.DBEngine,
|
||||||
Directory: n.ResolvePath(name),
|
Directory: n.ResolvePath(name),
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
Handles: handles,
|
Handles: handles,
|
||||||
ReadOnly: readonly,
|
ReadOnly: readonly,
|
||||||
|
ExtraDBConfig: extraDBConfig,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -808,7 +809,7 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r
|
||||||
// also attaching a chain freezer to it that moves ancient chain data from the
|
// also attaching a chain freezer to it that moves ancient chain data from the
|
||||||
// database to immutable append-only files. If the node is an ephemeral one, a
|
// database to immutable append-only files. If the node is an ephemeral one, a
|
||||||
// memory database is returned.
|
// memory database is returned.
|
||||||
func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
|
func (n *Node) OpenDatabaseWithFreezer(name string, cache int, handles int, ancient string, namespace string, readonly bool, extraDBConfig rawdb.ExtraDBConfig) (ethdb.Database, error) {
|
||||||
n.lock.Lock()
|
n.lock.Lock()
|
||||||
defer n.lock.Unlock()
|
defer n.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -831,6 +832,7 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
Handles: handles,
|
Handles: handles,
|
||||||
ReadOnly: readonly,
|
ReadOnly: readonly,
|
||||||
|
ExtraDBConfig: extraDBConfig,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
@ -157,7 +158,7 @@ func TestNodeCloseClosesDB(t *testing.T) {
|
||||||
stack, _ := New(testNodeConfig())
|
stack, _ := New(testNodeConfig())
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
db, err := stack.OpenDatabase("mydb", 0, 0, "", false)
|
db, err := stack.OpenDatabase("mydb", 0, 0, "", false, rawdb.ExtraDBConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("can't open DB:", err)
|
t.Fatal("can't open DB:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +185,7 @@ func TestNodeOpenDatabaseFromLifecycleStart(t *testing.T) {
|
||||||
|
|
||||||
stack.RegisterLifecycle(&InstrumentedService{
|
stack.RegisterLifecycle(&InstrumentedService{
|
||||||
startHook: func() {
|
startHook: func() {
|
||||||
db, err = stack.OpenDatabase("mydb", 0, 0, "", false)
|
db, err = stack.OpenDatabase("mydb", 0, 0, "", false, rawdb.ExtraDBConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("can't open DB:", err)
|
t.Fatal("can't open DB:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +206,7 @@ func TestNodeOpenDatabaseFromLifecycleStop(t *testing.T) {
|
||||||
|
|
||||||
stack.RegisterLifecycle(&InstrumentedService{
|
stack.RegisterLifecycle(&InstrumentedService{
|
||||||
stopHook: func() {
|
stopHook: func() {
|
||||||
db, err := stack.OpenDatabase("mydb", 0, 0, "", false)
|
db, err := stack.OpenDatabase("mydb", 0, 0, "", false, rawdb.ExtraDBConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("can't open DB:", err)
|
t.Fatal("can't open DB:", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor
|
Source: bor
|
||||||
Version: 1.0.0-beta
|
Version: 1.0.4
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor
|
Source: bor
|
||||||
Version: 1.0.0-beta
|
Version: 1.0.4
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 1.0.0-beta
|
Version: 1.0.4
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 1.0.0-beta
|
Version: 1.0.4
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 1.0.0-beta
|
Version: 1.0.4
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 1.0.0-beta
|
Version: 1.0.4
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -70,15 +70,15 @@ var GoerliBootnodes = []string{
|
||||||
// MumbaiBootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
// MumbaiBootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
||||||
// Mumbai test network.
|
// Mumbai test network.
|
||||||
var MumbaiBootnodes = []string{
|
var MumbaiBootnodes = []string{
|
||||||
"enode://320553cda00dfc003f499a3ce9598029f364fbb3ed1222fdc20a94d97dcc4d8ba0cd0bfa996579dcc6d17a534741fb0a5da303a90579431259150de66b597251@54.147.31.250:30303",
|
"enode://bdcd4786a616a853b8a041f53496d853c68d99d54ff305615cd91c03cd56895e0a7f6e9f35dbf89131044e2114a9a782b792b5661e3aff07faf125a98606a071@43.200.206.40:30303",
|
||||||
"enode://f0f48a8781629f95ff02606081e6e43e4aebd503f3d07fc931fad7dd5ca1ba52bd849a6f6c3be0e375cf13c9ae04d859c4a9ae3546dc8ed4f10aa5dbb47d4998@34.226.134.117:30303",
|
"enode://209aaf7ed549cf4a5700fd833da25413f80a1248bd3aa7fe2a87203e3f7b236dd729579e5c8df61c97bf508281bae4969d6de76a7393bcbd04a0af70270333b3@54.216.248.9:30303",
|
||||||
}
|
}
|
||||||
|
|
||||||
// BorMainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
// BorMainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the
|
||||||
// main Bor network.
|
// main Bor network.
|
||||||
var BorMainnetBootnodes = []string{
|
var BorMainnetBootnodes = []string{
|
||||||
"enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303",
|
"enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303",
|
||||||
"enode://88116f4295f5a31538ae409e4d44ad40d22e44ee9342869e7d68bdec55b0f83c1530355ce8b41fbec0928a7d75a5745d528450d30aec92066ab6ba1ee351d710@159.203.9.164:30303",
|
"enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303",
|
||||||
}
|
}
|
||||||
|
|
||||||
var KilnBootnodes = []string{
|
var KilnBootnodes = []string{
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
VersionMinor = 0 // Minor version component of the current release
|
VersionMinor = 0 // Minor version component of the current release
|
||||||
VersionPatch = 0 // Patch version component of the current release
|
VersionPatch = 4 // Patch version component of the current release
|
||||||
VersionMeta = "beta" // Version metadata to append to the version string
|
VersionMeta = "" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
var GitCommit string
|
var GitCommit string
|
||||||
|
|
|
||||||
|
|
@ -133,3 +133,15 @@ type CustomError struct {
|
||||||
func (e *CustomError) ErrorCode() int { return e.Code }
|
func (e *CustomError) ErrorCode() int { return e.Code }
|
||||||
|
|
||||||
func (e *CustomError) Error() string { return e.ValidationError }
|
func (e *CustomError) Error() string { return e.ValidationError }
|
||||||
|
|
||||||
|
type OptionsValidateError struct{ Message string }
|
||||||
|
|
||||||
|
func (e *OptionsValidateError) ErrorCode() int { return -32003 }
|
||||||
|
|
||||||
|
func (e *OptionsValidateError) Error() string { return e.Message }
|
||||||
|
|
||||||
|
type KnownAccountsLimitExceededError struct{ Message string }
|
||||||
|
|
||||||
|
func (e *KnownAccountsLimitExceededError) ErrorCode() int { return -32005 }
|
||||||
|
|
||||||
|
func (e *KnownAccountsLimitExceededError) Error() string { return e.Message }
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ type SafePool struct {
|
||||||
service string // the service using ep
|
service string // the service using ep
|
||||||
processed atomic.Int64 // keeps count of total processed requests
|
processed atomic.Int64 // keeps count of total processed requests
|
||||||
|
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
|
closeOnce sync.Once
|
||||||
|
|
||||||
// Skip sending task to execution pool
|
// Skip sending task to execution pool
|
||||||
fastPath bool
|
fastPath bool
|
||||||
|
|
@ -103,7 +104,9 @@ func (s *SafePool) Size() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SafePool) Stop() {
|
func (s *SafePool) Stop() {
|
||||||
close(s.close)
|
s.closeOnce.Do(func() {
|
||||||
|
close(s.close)
|
||||||
|
})
|
||||||
|
|
||||||
if s.executionPool.Load() != nil {
|
if s.executionPool.Load() != nil {
|
||||||
s.executionPool.Load().Stop()
|
s.executionPool.Load().Stop()
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,7 @@ func (h *handler) close(err error, inflightReq *requestOp) {
|
||||||
h.callWG.Wait()
|
h.callWG.Wait()
|
||||||
h.cancelRoot()
|
h.cancelRoot()
|
||||||
h.cancelServerSubscriptions(err)
|
h.cancelServerSubscriptions(err)
|
||||||
|
h.executionPool.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// addRequestOp registers a request operation.
|
// addRequestOp registers a request operation.
|
||||||
|
|
@ -375,6 +376,7 @@ func (h *handler) startCallProc(fn func(*callProc)) {
|
||||||
h.executionPool.Submit(context.Background(), func() error {
|
h.executionPool.Submit(context.Background(), func() error {
|
||||||
defer h.callWG.Done()
|
defer h.callWG.Done()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
fn(&callProc{ctx: ctx})
|
fn(&callProc{ctx: ctx})
|
||||||
|
|
||||||
h.executionPool.processed.Add(1)
|
h.executionPool.processed.Add(1)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ func (s *Server) ServeListener(l net.Listener) error {
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if netutil.IsTemporaryError(err) {
|
if netutil.IsTemporaryError(err) {
|
||||||
log.Warn("RPC accept error", "err", err)
|
log.Warn("RPC accept error", "err", err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -699,7 +699,7 @@ func main() {
|
||||||
args, ignoreForNow := beautifyArgs(args)
|
args, ignoreForNow := beautifyArgs(args)
|
||||||
|
|
||||||
c := server.Command{}
|
c := server.Command{}
|
||||||
flags := c.Flags()
|
flags := c.Flags(nil)
|
||||||
allFlags := flags.GetAllFlags()
|
allFlags := flags.GetAllFlags()
|
||||||
flagsToCheck := getFlagsToCheck(args)
|
flagsToCheck := getFlagsToCheck(args)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -48,7 +49,8 @@ type Trie struct {
|
||||||
|
|
||||||
// tracer is the tool to track the trie changes.
|
// tracer is the tool to track the trie changes.
|
||||||
// It will be reset after each commit operation.
|
// It will be reset after each commit operation.
|
||||||
tracer *tracer
|
tracer *tracer
|
||||||
|
tracerMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFlag returns the cache flag value for a newly created node.
|
// newFlag returns the cache flag value for a newly created node.
|
||||||
|
|
@ -605,7 +607,9 @@ func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.tracerMutex.Lock()
|
||||||
t.tracer.onRead(prefix, blob)
|
t.tracer.onRead(prefix, blob)
|
||||||
|
t.tracerMutex.Unlock()
|
||||||
|
|
||||||
return mustDecodeNode(n, blob), nil
|
return mustDecodeNode(n, blob), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue