Merge tag 'v1.10.10' into develop

This commit is contained in:
Austin Roberts 2021-10-18 12:06:35 -05:00
commit 3df75af219
95 changed files with 1960 additions and 1348 deletions

View File

@ -263,3 +263,15 @@ jobs:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
script: script:
- go run build/ci.go purge -store gethstore/builds -days 14 - go run build/ci.go purge -store gethstore/builds -days 14
# This builder executes race tests
- stage: build
if: type = cron
os: linux
dist: bionic
go: 1.17.x
env:
- GO111MODULE=on
script:
- go run build/ci.go test -race -coverage $TEST_PACKAGES

678
README.md
View File

@ -1,385 +1,363 @@
# PluGeth ## Go Ethereum
PluGeth is a fork of the [Go Ethereum Client](https://github.com/ethereum/go-ethereum) Official Golang implementation of the Ethereum protocol.
(Geth) that implements a plugin architecture, allowing developers to extend
Geth's capabilities in a number of different ways using plugins, rather than
having to create additional, new forks of Geth.
## WARNING: UNSTABLE API [![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc)
[![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
[![Travis](https://travis-ci.com/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.com/ethereum/go-ethereum)
[![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv)
Right now PluGeth is in early development. We are still settling on some of the Automated builds are available for stable releases and the unstable master branch. Binary
plugin APIs, and are not yet making official releases. From an operational archives are published at https://geth.ethereum.org/downloads/.
perspective, PluGeth should be as stable as upstream Geth less whatever
instability is added by plugins you might run. But if you plan to run PluGeth
today, be aware that future updates will likely break your plugins.
## System Requirements ## Building the source
System requirements will vary depending on which network you are connecting to. For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/install-and-build/installing-geth).
On the Ethereum mainnet, you should have at least 8 GB RAM, 2 CPUs, and 350 GB
of SSD disks.
PluGeth relies on Golang's Plugin implementation, which is only supported on Building `geth` requires both a Go (version 1.14 or later) and a C compiler. You can install
Linux, FreeBSD, and macOS. Windows support is unlikely to be added in the them using your favourite package manager. Once the dependencies are installed, run
foreseeable future.
## Design Goals
The upstream Geth client exists primarily to serve as a client for the Ethereum
mainnet, though it also supports a number of popular testnets. Supporting the
Ethereum mainnet is a big enough challenge in its own right that the Geth team
generally avoids changes to support other networks, or to provide features only
a small handful of users would be interested in.
The result is that many projects have forked Geth. Some implement their own
consensus protocols or alter the behavior of the EVM to support other networks.
Others are designed to extract information from the Ethereum mainnet in ways
the standard Geth client does not support.
Creating numerous different forks to fill a variety of different needs comes
with a number of drawbacks. Forks tend to drift apart from each other. Many
networks that forked from Geth long ago have stopped merging updates from Geth;
this makes some sense, given that those networks have moved in different
directions than Geth and merging upstream changes while properly maintaining
consensus rules of an existing network could prove quite challenging. But not
merging changes from upstream can mean that security updates are easily missed,
especially when the upstream team [obscures security updates as optimizations](https://blog.openrelay.xyz/vulnerability-lifecycle-framework-geth/)
as a matter of process.
PluGeth aims to provide a single Geth fork that developers can choose to extend
rather than forking the Geth project. Out of the box, PluGeth behaves exactly
like upstream Geth, but by installing plugins written in Golang, developers can
extend its functionality in a wide variety of way.
## Anatomy of a Plugin
Plugins for Plugeth use Golang's [Native Plugin System](https://golang.org/pkg/plugin/).
Plugin modules must export variables using specific names and types. These will
be processed by the plugin loader, and invoked at certain points during Geth's
operations.
### API
#### Flags
* **Name**: Flags
* **Type**: [flag.FlagSet](https://golang.org/pkg/flag/#FlagSet)
* **Behavior**: This FlagSet will be parsed and your plugin will be able to
access the resulting flags. Note that if any flags are provided, certain
checks are disabled within Geth to avoid failing due to unexpected flags.
#### Subcommands
* **Name**: Subcommands
* **Type**: map[string]func(ctx [*cli.Context](https://pkg.go.dev/github.com/urfave/cli#Context), args []string) error
* **Behavior**: If Geth is invoked with `geth YOUR_COMMAND`, the plugin loader
will look for `YOUR_COMMAND` within this map, and invoke the corresponding
function. This can be useful for certain behaviors like manipulating Geth's
database without having to build a separate binary.
#### Initialize
* **Name**: Initialize
* **Type**: func(*cli.Context, *PluginLoader)
* **Behavior**: Called as soon as the plugin is loaded, with the cli context
and a reference to the plugin loader. This is your plugin's opportunity to
initialize required variables as needed. Note that using the context object
you can check arguments, and optionally can manipulate arguments if needed
for your plugin.
#### InitializeNode
* **Name**: InitializeNode
* **Type**: func(*node.Node, interfaces.Backend)
* **Behavior**: This is called as soon as the Geth node is initialized. The
`*node.Node` object represents the running node with p2p and RPC capabilities,
while the Backend gives you access to a wide array of data you may need to
access.
#### Tracers
* **Name**: Tracer
* **Type**: map[string]TracerResult
* **Behavior**: When calling debug.traceX functions (such as debug_traceCall
and debug_traceTransaction) the tracer can be specified as a key to this map
and the tracer used will be the TracerResult specified here. TracerResult
objects must match the interface:
```
// CaptureStart is called at the start of each transaction
CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {}
// CaptureState is called for each opcode
CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {}
// CaptureFault is called when an error occurs in the EVM
CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {}
// CaptureEnd is called at the end of each transaction
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
// GetResult should return a JSON serializable result object to respond to the trace call
GetResult() (interface{}, error) {}
```shell
make geth
``` ```
* **Caution**: Modifying of the values passed into tracer functions can alter or, to build the full suite of utilities:
the results of the EVM execution in unpredictable ways. Additionally, some
objects may be reused across calls, so data you wish to capture should be
copied rather than retained by reference.
#### LiveTracer
* **Name**: LiveTracer
* **Type**: vm.Tracer
* **Behavior**: This tracer is used for tracing transactions as they are
processed within blocks. Note that if a block does not validate, some
transactions may be processed that don't end up in blocks, so be sure to
check transactions against finalized blocks.
The interface for a vm.Tracer is similar to a TracerResult (above), but does
not require a `GetResult()` function.
#### GetAPIs
* **Name**: GetAPIs
* **Type**: func(*node.Node, interfaces.Backend) []rpc.API
* **Behavior**: This allows you to register new RPC methods to run within Geth.
* **Example**:
The GetAPIs function itself will generally be fairly brief, and will looks
something like this:
```shell
make all
``` ```
func GetAPIs(stack *node.Node, backend plugins.Backend) []rpc.API {
return []rpc.API{ ## Executables
{
Namespace: "mynamespace", The go-ethereum project comes with several wrappers/executables found in the `cmd`
Version: "1.0", directory.
Service: &MyService{backend},
Public: true, | Command | Description |
}, | :-----------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
} | **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/interface/command-line-options) for command line options. |
| `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. |
| `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/dapp/native-bindings) page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://eth.wiki/en/fundamentals/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
| `puppeth` | a CLI wizard that aids in creating a new Ethereum network. |
## Running `geth`
Going through all the possible command line flags is out of scope here (please consult our
[CLI Wiki page](https://geth.ethereum.org/docs/interface/command-line-options)),
but we've enumerated a few common parameter combos to get you up to speed quickly
on how you can run your own `geth` instance.
### Full node on the main Ethereum network
By far the most common scenario is people wanting to simply interact with the Ethereum
network: create accounts; transfer funds; deploy and interact with contracts. For this
particular use-case the user doesn't care about years-old historical data, so we can
fast-sync quickly to the current state of the network. To do so:
```shell
$ geth console
```
This command will:
* Start `geth` in snap sync mode (default, can be changed with the `--syncmode` flag),
causing it to download more data in exchange for avoiding processing the entire history
of the Ethereum network, which is very CPU intensive.
* Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console),
(via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://web3js.readthedocs.io/)
(note: the `web3` version bundled within `geth` is very old, and not up to date with official docs),
as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server).
This tool is optional and if you leave it out you can always attach to an already running
`geth` instance with `geth attach`.
### A Full node on the Görli test network
Transitioning towards developers, if you'd like to play around with creating Ethereum
contracts, you almost certainly would like to do that without any real money involved until
you get the hang of the entire system. In other words, instead of attaching to the main
network, you want to join the **test** network with your node, which is fully equivalent to
the main network, but with play-Ether only.
```shell
$ geth --goerli console
```
The `console` subcommand has the exact same meaning as above and they are equally
useful on the testnet too. Please, see above for their explanations if you've skipped here.
Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit:
* Instead of connecting the main Ethereum network, the client will connect to the Görli
test network, which uses different P2P bootnodes, different network IDs and genesis
states.
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
requires the use of a custom endpoint since `geth attach` will try to attach to a
production node endpoint by default, e.g.,
`geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by
this.
*Note: Although there are some internal protective measures to prevent transactions from
crossing over between the main network and test network, you should make sure to always
use separate accounts for play-money and real-money. Unless you manually move
accounts, `geth` will by default correctly separate the two networks and will not make any
accounts available between them.*
### Full node on the Rinkeby test network
Go Ethereum also supports connecting to the older proof-of-authority based test network
called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community.
```shell
$ geth --rinkeby console
```
### Full node on the Ropsten test network
In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The
Ropsten test network is based on the Ethash proof-of-work consensus algorithm. As such,
it has certain extra overhead and is more susceptible to reorganization attacks due to the
network's low difficulty/security.
```shell
$ geth --ropsten console
```
*Note: Older Geth configurations store the Ropsten database in the `testnet` subdirectory.*
### Configuration
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a
configuration file via:
```shell
$ geth --config /path/to/your_config.toml
```
To get an idea how the file should look like you can use the `dumpconfig` subcommand to
export your existing configuration:
```shell
$ geth --your-favourite-flags dumpconfig
```
*Note: This works only with `geth` v1.6.0 and above.*
#### Docker quick start
One of the quickest ways to get Ethereum up and running on your machine is by using
Docker:
```shell
docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
-p 8545:8545 -p 30303:30303 \
ethereum/client-go
```
This will start `geth` in fast-sync mode with a DB memory allowance of 1GB just as the
above command does. It will also create a persistent volume in your home directory for
saving your blockchain as well as map the default ports. There is also an `alpine` tag
available for a slim version of the image.
Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers
and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not
accessible from the outside.
### Programmatically interfacing `geth` nodes
As a developer, sooner rather than later you'll want to start interacting with `geth` and the
Ethereum network via your own programs and not manually through the console. To aid
this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://eth.wiki/json-rpc/API)
and [`geth` specific APIs](https://geth.ethereum.org/docs/rpc/server)).
These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based
platforms, and named pipes on Windows).
The IPC interface is enabled by default and exposes all the APIs supported by `geth`,
whereas the HTTP and WS interfaces need to manually be enabled and only expose a
subset of APIs due to security reasons. These can be turned on/off and configured as
you'd expect.
HTTP based JSON-RPC API options:
* `--http` Enable the HTTP-RPC server
* `--http.addr` HTTP-RPC server listening interface (default: `localhost`)
* `--http.port` HTTP-RPC server listening port (default: `8545`)
* `--http.api` API's offered over the HTTP-RPC interface (default: `eth,net,web3`)
* `--http.corsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
* `--ws` Enable the WS-RPC server
* `--ws.addr` WS-RPC server listening interface (default: `localhost`)
* `--ws.port` WS-RPC server listening port (default: `8546`)
* `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`)
* `--ws.origins` Origins from which to accept websockets requests
* `--ipcdisable` Disable the IPC-RPC server
* `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,shh,txpool,web3`)
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to
connect via HTTP, WS or IPC to a `geth` node configured with the above flags and you'll
need to speak [JSON-RPC](https://www.jsonrpc.org/specification) on all transports. You
can reuse the same connection for multiple requests!
**Note: Please understand the security implications of opening up an HTTP/WS based
transport before doing so! Hackers on the internet are actively trying to subvert
Ethereum nodes with exposed APIs! Further, all browser tabs can access locally
running web servers, so malicious web pages could try to subvert locally available
APIs!**
### Operating a private network
Maintaining your own private network is more involved as a lot of configurations taken for
granted in the official networks need to be manually set up.
#### Defining the private genesis state
First, you'll need to create the genesis state of your networks, which all nodes need to be
aware of and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`):
```json
{
"config": {
"chainId": <arbitrary positive integer>,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0
},
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x20000",
"extraData": "",
"gasLimit": "0x2fefd8",
"nonce": "0x0000000000000042",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
} }
``` ```
The bulk of the implementation will be in the `MyService` struct. MyService The above fields should be fine for most purposes, although we'd recommend changing
should be a struct with public functions. These functions can have two the `nonce` to some random value so you prevent unknown remote nodes from being able
different types of signatures: to connect to you. If you'd like to pre-fund some accounts for easier testing, create
the accounts and populate the `alloc` field with their addresses.
* RPC Calls: For straight RPC calls, a function should have a `context.Context` ```json
object as the first argument, followed by an arbitrary number of JSON "alloc": {
marshallable arguments, and return either a single JSON marshal object, or a "0x0000000000000000000000000000000000000001": {
JSON marshallable object and an error. The RPC framework will take care of "balance": "111111111"
decoding inputs to this function and encoding outputs, and if the error is },
non-nil it will serve an error response. "0x0000000000000000000000000000000000000002": {
* Subscriptions: For subscriptions (supported on IPC and websockets), a "balance": "222222222"
function should have a `context.Context` object as the first argument
followed by an arbitrary number of JSON marshallable arguments, and should
return an `*rpc.Subscription` object. The subscription object can be created
with `rpcSub := notifier.CreateSubscription()`, and JSON marshallable data
can be sent to the subscriber with `notifier.Notify(rpcSub.ID, b)`.
A very simple MyService might look like:
```
type MyService struct{}
func (h *MyService) HelloWorld(ctx context.Context) string {
return "Hello World"
}
```
And the client could then access this with an rpc call to `mynaespace_helloWorld`.
#### PreProcessBlock
* **Name**: PreProcessBlock
* **Type**: func(*types.Block)
* **Behavior**: Invoked before the transactions of a block are processed.
#### PreProcessTransaction
* **Name**: PreProcessTransaction
* **Type**: func(*types.Transaction, *types.Block, int)
* **Behavior**: Invoked before each individual transaction of a block is
processed.
#### BlockProcessingError
* **Name**: BlockProcessingError
* **Type**: func(*types.Transaction, *types.Block, error)
* **Behavior**: Invoked if an error occurs while processing a transaction. This
only applies to errors that would invalidate the block were this transaction
included, not errors such as reverts or opcode errors.
#### PostProcessTransaction
* **Name**: PostProcessTransaction
* **Type**: func(*types.Transaction, *types.Block, int, *types.Receipt)
* **Behavior**: Invoked after each individual transaction of a block is processed.
#### PostProcessBlock
* **Name**: PostProcessBlock
* **Type**: func(*types.Block)
* **Behavior**: Invoked after all transactions of a block are processed. Note
that this does not mean that the block can be considered canonical - it may
end up being uncled or side-chained. You should rely on `NewHead` to
determine which blocks are canonical.
#### NewHead
* **Name**: NewHead
* **Type**: func(*types.Block, common.Hash, []*types.Log)
* **Behavior**: Invoked when a new block becomes the canonical latest block.
Note that if several blocks are processed in a group (such as during a reorg)
this may not be called for each block. You should track the prior latest head
if you need to process intermediate blocks.
#### NewSideBlock
* **Name**: NewSideBlock
* **Type**: func(*types.Block, common.Hash, []*types.Log)
* **Behavior**: Invoked when a block is side-chained. Blocks passed to this
method are non-canonical blocks
#### Reorg
* **Name**: Reorg
* **Type**: func(common *types.Block, oldChain, newChain types.Blocks)
* **Behavior**: Invoked when a chain reorg occurs (at least one block is
removed and one block is added). `oldChain` is a list of removed blocks,
`newChain` is a list of newly added blocks, and `common` is the latest block
that is an ancestor to both oldChain and newChain.
#### StateUpdate
* **Name**: StateUpdate
* **Type**: func(root common.Hash, parentRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte)
* **Behavior**: Invoked for each new block, StateUpdate provides the changes to
the blockchain state. `root` corresponds to the state root of the new block.
`parentRoot` corresponds to the state root of the parent block. `destructs`
serves as a set of accounts that self-destructed in this block. `accounts`
maps the hash of each account address to the SlimRLP encoding of the account
data. `storage` maps the hash of each account to a map of that account's
stored data.
* **Warning**: StateUpdate is only called if Geth is running with
`--snapshot=true`. This is the default behavior for Geth, but if you are
explicitly running with `--snapshot=false` this function will not be invoked.
#### AppendAncient
* **Name**: AppendAncient
* **Type**: func(number uint64, hash, header, body, receipts, td []byte)
* **Behavior**: Invoked when the freezer moves a block from LevelDB to the
ancients database. `number` is the number of the block. `hash` is the 32 byte
hash of the block as a raw `[]byte`. `header`, `body`, and `receipts` are the
RLP encoded versions of their respective block elements. `td` is the byte
encoded total difficulty of the block.
## Extending The Plugin API
When extending the plugin API, a primary concern is leaving a minimal footprint
in the core Geth codebase to avoid future merge conflicts. To achieve this,
when we want to add a hook within some existing Geth code, we create a
`plugin_hooks.go` in the same package. For example, in the core/rawdb package
we have:
```
// This file is part of the package we are adding hooks to
package rawdb
// Import whatever is necessary
import (
"github.com/ethereum/go-ethereum/plugins"
"github.com/ethereum/go-ethereum/log"
)
// PluginAppendAncient is the public plugin hook function, available for testing
func PluginAppendAncient(pl *plugins.PluginLoader, number uint64, hash, header, body, receipts, td []byte) {
fnList := pl.Lookup("AppendAncient", func(item interface{}) bool {
_, ok := item.(func(number uint64, hash, header, body, receipts, td []byte))
return ok
})
for _, fni := range fnList {
if fn, ok := fni.(func(number uint64, hash, header, body, receipts, td []byte)); ok {
fn(number, hash, header, body, receipts, td)
}
} }
} }
// pluginAppendAncient is the private plugin hook function
func pluginAppendAncient(number uint64, hash, header, body, receipts, td []byte) {
if plugins.DefaultPluginLoader == nil {
log.Warn("Attempting AppendAncient, but default PluginLoader has not been initialized")
return
}
PluginAppendAncient(plugins.DefaultPluginLoader, number, hash, header, body, receipts, td)
}
``` ```
### The Public Plugin Hook Function With the genesis state defined in the above JSON file, you'll need to initialize **every**
`geth` node with it prior to starting it up to ensure all blockchain parameters are correctly
set:
The public plugin hook function should follow the naming convention ```shell
`Plugin$HookName`. The first argument should be a *plugins.PluginLoader, $ geth init path/to/genesis.json
followed by any arguments required by the functions to be provided by nay ```
plugins implementing this hook.
The plugin hook function should use `PluginLoader.Lookup("$HookName", func(item interface{}) bool` #### Creating the rendezvous point
to get a list of the plugin-provided functions to be invoked. The provided
function should verify that the provided function implements the expected
interface. After the first time a given hook is looked up through the plugin
loader, the PluginLoader will cache references to those hooks.
Given the function list provided by the plugin loader, the public plugin hook With all nodes that you want to run initialized to the desired genesis state, you'll need to
function should iterate over the list, cast the elements to the appropriate start a bootstrap node that others can use to find each other in your network and/or over
type, and call the function with the provided arguments. the internet. The clean way is to configure and run a dedicated bootnode:
Unless there is a clear justification to the contrary, the function should be ```shell
called in the current goroutine. Plugins may choose to spawn off a separate $ bootnode --genkey=boot.key
goroutine as appropriate, but for the sake of thread safety we should generally $ bootnode --nodekey=boot.key
not assume that plugins will be implemented in a threadsafe manner. If a plugin ```
degrades the performance of Geth significantly, that will generally be obvious,
and plugin authors can take appropriate measures to improve performance. If a
plugin introduces thread safety issues, those can go unnoticed during testing.
### The Private Plugin Hook Function With the bootnode online, it will display an [`enode` URL](https://eth.wiki/en/fundamentals/enode-url-format)
that other nodes can use to connect to it and exchange peer information. Make sure to
replace the displayed IP address information (most probably `[::]`) with your externally
accessible IP to get the actual `enode` URL.
The private plugin hook function should bear the same name as the public plugin *Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less
hook function, but with a lower case first letter. The signature should match recommended way.*
the public plugin hook function, except that the first argument referencing the
PluginLoader should be removed. It should invoke the public plugin hook
function on `plugins.DefaultPluginLoader`. It should always verify that the
DefaultPluginLoader is non-nil, log warning and return if the
DefaultPluginLoader has not been initialized.
### In-Line Invocation #### Starting up your member nodes
Within the Geth codebase, the private plugin hook function should be invoked With the bootnode operational and externally reachable (you can try
with the appropriate arguments in a single line, to minimize unexpected `telnet <ip> <port>` to ensure it's indeed reachable), start every subsequent `geth`
conflicts merging the upstream geth codebase into plugeth. node pointed to the bootnode for peer discovery via the `--bootnodes` flag. It will
probably also be desirable to keep the data directory of your private network separated, so
do also specify a custom `--datadir` flag.
### Contact Us ```shell
$ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above>
```
While we can imagine lots of ways plugins might like to extract or change *Note: Since your network will be completely cut off from the main and test networks, you'll
information in Geth, we're trying not to go too crazy with the plugin API based also need to configure a miner to process transactions and create new blocks for you.*
purely on hypotheticals. The Plugin API in its current form reflects the needs
of projects currently building on PluGeth, and we're happy to extend it for
people who are building something. If you're trying to do something that isn't
supported by the current plugin system, we're happy to help. Reach out to us on
[Discord](https://discord.gg/Epf7b7Gr) and we'll help you figure out how to
make it work.
# Licensing Considerations #### Running a private miner
The Geth codebase is licensed under the LGPL. By linking with Geth, you have an Mining on the public Ethereum network is a complex task as it's only feasible using GPUs,
obligation to enable anyone you provide your plugin binaries to run against requiring an OpenCL or CUDA enabled `ethminer` instance. For information on such a
their own modified versions of Geth. Because of how Golang plugins work setup, please consult the [EtherMining subreddit](https://www.reddit.com/r/EtherMining/)
running against updated versions of Geth may require recompiling the plugin. and the [ethminer](https://github.com/ethereum-mining/ethminer) repository.
If you plan to license your plugin under the LGPL or a more permissive license, In a private network setting, however a single CPU miner instance is more than enough for
you should be able to meet these requirements. If you plan to use your plugin practical purposes as it can produce a stable stream of blocks at the correct intervals
privately without distributing it, you should be fine. If you plan to release without needing heavy resources (consider running on a single thread, no need for multiple
your plugin without making the source available, you may find yourself in ones either). To start a `geth` instance for mining, run it with all your usual flags, extended
violation of Geth's license unless you can provide a way to relink it against by:
more recent versions of Geth.
# Existing Plugins ```shell
$ geth <usual-flags> --mine --miner.threads=1 --miner.etherbase=0x0000000000000000000000000000000000000000
```
We currently provide the following plugins: Which will start mining blocks and transactions on a single CPU thread, crediting all
proceedings to the account specified by `--miner.etherbase`. You can further tune the mining
by changing the default gas limit blocks converge to (`--miner.targetgaslimit`) and the price
transactions are accepted at (`--miner.gasprice`).
* [BlockUpdates](./plugins/packages/blockupdates/main.go): A good reference ## Contribution
plugin, which leverages several hooks to provide a new BlockUpdates hook,
which plugins can use to get more cohesive updates about new blocks than can Thank you for considering to help out with the source code! We welcome contributions
easily be achieved with the standard PluGeth hooks. from anyone on the internet, and are grateful for even the smallest of fixes!
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request
for the maintainers to review and merge into the main code base. If you wish to submit
more complex changes though, please check up with the core devs first on [our Discord Server](https://discord.gg/invite/nthXNEv)
to ensure those changes are in line with the general philosophy of the project and/or get
some early feedback which can make both your efforts much lighter as well as our review
and merge procedures quick and simple.
Please make sure your contributions adhere to our coding guidelines:
* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting)
guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary)
guidelines.
* Pull requests need to be based on and opened against the `master` branch.
* Commit messages should be prefixed with the package(s) they modify.
* E.g. "eth, rpc: make trace configs optional"
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide)
for more details on configuring your environment, managing project dependencies, and
testing procedures.
## License
The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
also included in our repository in the `COPYING.LESSER` file.
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the
[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.

View File

@ -34,6 +34,7 @@ type ABI struct {
Constructor Method Constructor Method
Methods map[string]Method Methods map[string]Method
Events map[string]Event Events map[string]Event
Errors map[string]Error
// Additional "special" functions introduced in solidity v0.6.0. // Additional "special" functions introduced in solidity v0.6.0.
// It's separated from the original default fallback. Each contract // It's separated from the original default fallback. Each contract
@ -157,12 +158,13 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
} }
abi.Methods = make(map[string]Method) abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event) abi.Events = make(map[string]Event)
abi.Errors = make(map[string]Error)
for _, field := range fields { for _, field := range fields {
switch field.Type { switch field.Type {
case "constructor": case "constructor":
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil) abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
case "function": case "function":
name := abi.overloadedMethodName(field.Name) name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs) abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
case "fallback": case "fallback":
// New introduced function type in v0.6.0, check more detail // New introduced function type in v0.6.0, check more detail
@ -182,8 +184,10 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
} }
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil) abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "event": case "event":
name := abi.overloadedEventName(field.Name) name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs) abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
case "error":
abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
default: default:
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name) return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
} }
@ -191,36 +195,6 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
return nil return nil
} }
// overloadedMethodName returns the next available name for a given function.
// Needed since solidity allows for function overload.
//
// e.g. if the abi contains Methods send, send1
// overloadedMethodName would return send2 for input send.
func (abi *ABI) overloadedMethodName(rawName string) string {
name := rawName
_, ok := abi.Methods[name]
for idx := 0; ok; idx++ {
name = fmt.Sprintf("%s%d", rawName, idx)
_, ok = abi.Methods[name]
}
return name
}
// overloadedEventName returns the next available name for a given event.
// Needed since solidity allows for event overload.
//
// e.g. if the abi contains events received, received1
// overloadedEventName would return received2 for input received.
func (abi *ABI) overloadedEventName(rawName string) string {
name := rawName
_, ok := abi.Events[name]
for idx := 0; ok; idx++ {
name = fmt.Sprintf("%s%d", rawName, idx)
_, ok = abi.Events[name]
}
return name
}
// MethodById looks up a method by the 4-byte id, // MethodById looks up a method by the 4-byte id,
// returns nil if none found. // returns nil if none found.
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) { func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
@ -277,3 +251,20 @@ func UnpackRevert(data []byte) (string, error) {
} }
return unpacked[0].(string), nil return unpacked[0].(string), nil
} }
// overloadedName returns the next available name for a given thing.
// Needed since solidity allows for overloading.
//
// e.g. if the abi contains Methods send, send1
// overloadedName would return send2 for input send.
//
// overloadedName works for methods, events and errors.
func overloadedName(rawName string, isAvail func(string) bool) string {
name := rawName
ok := isAvail(name)
for idx := 0; ok; idx++ {
name = fmt.Sprintf("%s%d", rawName, idx)
ok = isAvail(name)
}
return name
}

View File

@ -295,6 +295,20 @@ func TestOverloadedMethodSignature(t *testing.T) {
check("bar0", "bar(uint256,uint256)", false) check("bar0", "bar(uint256,uint256)", false)
} }
func TestCustomErrors(t *testing.T) {
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
abi, err := JSON(strings.NewReader(json))
if err != nil {
t.Fatal(err)
}
check := func(name string, expect string) {
if abi.Errors[name].Sig != expect {
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
}
}
check("MyError", "MyError(uint256)")
}
func TestMultiPack(t *testing.T) { func TestMultiPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata)) abi, err := JSON(strings.NewReader(jsondata))
if err != nil { if err != nil {

View File

@ -231,108 +231,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
return c.transact(opts, &c.address, nil) return c.transact(opts, &c.address, nil)
} }
// transact executes an actual transaction invocation, first deriving any missing func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
// authorization fields, and then scheduling the transaction for execution. // Normalize value
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
var err error
// Ensure a valid value field and resolve the account nonce
value := opts.Value value := opts.Value
if value == nil { if value == nil {
value = new(big.Int) value = new(big.Int)
} }
var nonce uint64 // Estimate TipCap
if opts.Nonce == nil { gasTipCap := opts.GasTipCap
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) if gasTipCap == nil {
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) return nil, err
} }
} else { gasTipCap = tip
nonce = opts.Nonce.Uint64()
} }
// Figure out reasonable gas price values // Estimate FeeCap
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { gasFeeCap := opts.GasFeeCap
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") if gasFeeCap == nil {
gasFeeCap = new(big.Int).Add(
gasTipCap,
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
)
} }
head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil) if gasFeeCap.Cmp(gasTipCap) < 0 {
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
}
// Estimate GasLimit
gasLimit := opts.GasLimit
if opts.GasLimit == 0 {
var err error
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
if err != nil {
return nil, err
}
}
// create the transaction
nonce, err := c.getNonce(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if head.BaseFee != nil && opts.GasPrice == nil { baseTx := &types.DynamicFeeTx{
if opts.GasTipCap == nil { To: contract,
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context)) Nonce: nonce,
if err != nil { GasFeeCap: gasFeeCap,
return nil, err GasTipCap: gasTipCap,
} Gas: gasLimit,
opts.GasTipCap = tip Value: value,
} Data: input,
if opts.GasFeeCap == nil {
gasFeeCap := new(big.Int).Add(
opts.GasTipCap,
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
)
opts.GasFeeCap = gasFeeCap
}
if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 {
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap)
}
} else {
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
}
if opts.GasPrice == nil {
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
return nil, err
}
opts.GasPrice = price
}
} }
gasLimit := opts.GasLimit return types.NewTx(baseTx), nil
if gasLimit == 0 { }
// Gas estimation cannot succeed without code for method invocations
if contract != nil { func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { if opts.GasFeeCap != nil || opts.GasTipCap != nil {
return nil, err return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
} else if len(code) == 0 { }
return nil, ErrNoCode // Normalize value
} value := opts.Value
} if value == nil {
// If the contract surely has code (or code is not needed), estimate the transaction value = new(big.Int)
msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input} }
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) // Estimate GasPrice
gasPrice := opts.GasPrice
if gasPrice == nil {
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to estimate gas needed: %v", err) return nil, err
}
gasPrice = price
}
// Estimate GasLimit
gasLimit := opts.GasLimit
if opts.GasLimit == 0 {
var err error
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
if err != nil {
return nil, err
} }
} }
// Create the transaction, sign it and schedule it for execution // create the transaction
var rawTx *types.Transaction nonce, err := c.getNonce(opts)
if opts.GasFeeCap == nil { if err != nil {
baseTx := &types.LegacyTx{ return nil, err
Nonce: nonce, }
GasPrice: opts.GasPrice, baseTx := &types.LegacyTx{
Gas: gasLimit, To: contract,
Value: value, Nonce: nonce,
Data: input, GasPrice: gasPrice,
Gas: gasLimit,
Value: value,
Data: input,
}
return types.NewTx(baseTx), nil
}
func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
if contract != nil {
// Gas estimation cannot succeed without code for method invocations.
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
return 0, err
} else if len(code) == 0 {
return 0, ErrNoCode
} }
if contract != nil { }
baseTx.To = &c.address msg := ethereum.CallMsg{
} From: opts.From,
rawTx = types.NewTx(baseTx) To: contract,
GasPrice: gasPrice,
GasTipCap: gasTipCap,
GasFeeCap: gasFeeCap,
Value: value,
Data: input,
}
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
}
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
if opts.Nonce == nil {
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
} else { } else {
baseTx := &types.DynamicFeeTx{ return opts.Nonce.Uint64(), nil
Nonce: nonce,
GasFeeCap: opts.GasFeeCap,
GasTipCap: opts.GasTipCap,
Gas: gasLimit,
Value: value,
Data: input,
}
if contract != nil {
baseTx.To = &c.address
}
rawTx = types.NewTx(baseTx)
} }
}
// transact executes an actual transaction invocation, first deriving any missing
// authorization fields, and then scheduling the transaction for execution.
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
// Create the transaction
var (
rawTx *types.Transaction
err error
)
if opts.GasPrice != nil {
rawTx, err = c.createLegacyTx(opts, contract, input)
} else {
// Only query for basefee if gasPrice not specified
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); err != nil {
return nil, errHead
} else if head.BaseFee != nil {
rawTx, err = c.createDynamicTx(opts, contract, input, head)
} else {
// Chain is not London ready -> use legacy transaction
rawTx, err = c.createLegacyTx(opts, contract, input)
}
}
if err != nil {
return nil, err
}
// Sign the transaction and schedule it for execution
if opts.Signer == nil { if opts.Signer == nil {
return nil, errors.New("no signer to authorize the transaction with") return nil, errors.New("no signer to authorize the transaction with")
} }

View File

@ -31,8 +31,49 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/assert"
) )
func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
type mockTransactor struct {
baseFee *big.Int
gasTipCap *big.Int
gasPrice *big.Int
suggestGasTipCapCalled bool
suggestGasPriceCalled bool
}
func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
return &types.Header{BaseFee: mt.baseFee}, nil
}
func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
return []byte{1}, nil
}
func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
return 0, nil
}
func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
mt.suggestGasPriceCalled = true
return mt.gasPrice, nil
}
func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
mt.suggestGasTipCapCalled = true
return mt.gasTipCap, nil
}
func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
return 0, nil
}
func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
return nil
}
type mockCaller struct { type mockCaller struct {
codeAtBlockNumber *big.Int codeAtBlockNumber *big.Int
callContractBlockNumber *big.Int callContractBlockNumber *big.Int
@ -226,6 +267,51 @@ func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
unpackAndCheck(t, bc, expectedReceivedMap, mockLog) unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
} }
func TestTransactGasFee(t *testing.T) {
assert := assert.New(t)
// GasTipCap and GasFeeCap
// When opts.GasTipCap and opts.GasFeeCap are nil
mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
opts := &bind.TransactOpts{Signer: mockSign}
tx, err := bc.Transact(opts, "")
assert.Nil(err)
assert.Equal(big.NewInt(5), tx.GasTipCap())
assert.Equal(big.NewInt(205), tx.GasFeeCap())
assert.Nil(opts.GasTipCap)
assert.Nil(opts.GasFeeCap)
assert.True(mt.suggestGasTipCapCalled)
// Second call to Transact should use latest suggested GasTipCap
mt.gasTipCap = big.NewInt(6)
mt.suggestGasTipCapCalled = false
tx, err = bc.Transact(opts, "")
assert.Nil(err)
assert.Equal(big.NewInt(6), tx.GasTipCap())
assert.Equal(big.NewInt(206), tx.GasFeeCap())
assert.True(mt.suggestGasTipCapCalled)
// GasPrice
// When opts.GasPrice is nil
mt = &mockTransactor{gasPrice: big.NewInt(5)}
bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
opts = &bind.TransactOpts{Signer: mockSign}
tx, err = bc.Transact(opts, "")
assert.Nil(err)
assert.Equal(big.NewInt(5), tx.GasPrice())
assert.Nil(opts.GasPrice)
assert.True(mt.suggestGasPriceCalled)
// Second call to Transact should use latest suggested GasPrice
mt.gasPrice = big.NewInt(6)
mt.suggestGasPriceCalled = false
tx, err = bc.Transact(opts, "")
assert.Nil(err)
assert.Equal(big.NewInt(6), tx.GasPrice())
assert.True(mt.suggestGasPriceCalled)
}
func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) { func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
received := make(map[string]interface{}) received := make(map[string]interface{})
if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil { if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {

View File

@ -1850,6 +1850,61 @@ var bindTests = []struct {
if count != 1 { if count != 1 {
t.Fatal("Unexpected contract event number") t.Fatal("Unexpected contract event number")
} }
`,
nil,
nil,
nil,
nil,
},
// Test errors introduced in v0.8.4
{
`NewErrors`,
`
pragma solidity >0.8.4;
contract NewErrors {
error MyError(uint256);
error MyError1(uint256);
error MyError2(uint256, uint256);
error MyError3(uint256 a, uint256 b, uint256 c);
function Error() public pure {
revert MyError3(1,2,3);
}
}
`,
[]string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"},
[]string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`},
`
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/ethconfig"
`,
`
var (
key, _ = crypto.GenerateKey()
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
)
defer sim.Close()
_, tx, contract, err := DeployNewErrors(user, sim)
if err != nil {
t.Fatal(err)
}
sim.Commit()
_, err = bind.WaitDeployed(nil, sim, tx)
if err != nil {
t.Error(err)
}
if err := contract.Error(new(bind.CallOpts)); err == nil {
t.Fatalf("expected contract to throw error")
}
// TODO (MariusVanDerWijden unpack error using abigen
// once that is implemented
`, `,
nil, nil,
nil, nil,

View File

@ -1,4 +1,4 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
@ -17,66 +17,75 @@
package abi package abi
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"reflect" "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
) )
var ( type Error struct {
errBadBool = errors.New("abi: improperly encoded boolean value") Name string
) Inputs Arguments
str string
// formatSliceString formats the reflection kind with the given slice size // Sig contains the string signature according to the ABI spec.
// and returns a formatted string representation. // e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
func formatSliceString(kind reflect.Kind, sliceSize int) string { // Please note that "int" is substitute for its canonical representation "int256"
if sliceSize == -1 { Sig string
return fmt.Sprintf("[]%v", kind) // ID returns the canonical representation of the event's signature used by the
} // abi definition to identify event names and types.
return fmt.Sprintf("[%d]%v", sliceSize, kind) ID common.Hash
} }
// sliceTypeCheck checks that the given slice can by assigned to the reflection func NewError(name string, inputs Arguments) Error {
// type in t. // sanitize inputs to remove inputs without names
func sliceTypeCheck(t Type, val reflect.Value) error { // and precompute string and sig representation.
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { names := make([]string, len(inputs))
return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type()) types := make([]string, len(inputs))
} for i, input := range inputs {
if input.Name == "" {
if t.T == ArrayTy && val.Len() != t.Size { inputs[i] = Argument{
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len())) Name: fmt.Sprintf("arg%d", i),
} Indexed: input.Indexed,
Type: input.Type,
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy { }
if val.Len() > 0 { } else {
return sliceTypeCheck(*t.Elem, val.Index(0)) inputs[i] = input
} }
// string representation
names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
if input.Indexed {
names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
}
// sig representation
types[i] = input.Type.String()
} }
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() { str := fmt.Sprintf("error %v(%v)", name, strings.Join(names, ", "))
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type()) sig := fmt.Sprintf("%v(%v)", name, strings.Join(types, ","))
id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
return Error{
Name: name,
Inputs: inputs,
str: str,
Sig: sig,
ID: id,
} }
return nil
} }
// typeCheck checks that the given reflection value can be assigned to the reflection func (e *Error) String() string {
// type in t. return e.str
func typeCheck(t Type, value reflect.Value) error {
if t.T == SliceTy || t.T == ArrayTy {
return sliceTypeCheck(t, value)
}
// Check base type validity. Element types will be checked later on.
if t.GetType().Kind() != value.Kind() {
return typeErr(t.GetType().Kind(), value.Kind())
} else if t.T == FixedBytesTy && t.Size != value.Len() {
return typeErr(t.GetType(), value.Type())
} else {
return nil
}
} }
// typeErr returns a formatted type casting error. func (e *Error) Unpack(data []byte) (interface{}, error) {
func typeErr(expected, got interface{}) error { if len(data) < 4 {
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected) return "", errors.New("invalid data for unpacking")
}
if !bytes.Equal(data[:4], e.ID[:4]) {
return "", errors.New("invalid data for unpacking")
}
return e.Inputs.Unpack(data[4:])
} }

View File

@ -0,0 +1,82 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"errors"
"fmt"
"reflect"
)
var (
errBadBool = errors.New("abi: improperly encoded boolean value")
)
// formatSliceString formats the reflection kind with the given slice size
// and returns a formatted string representation.
func formatSliceString(kind reflect.Kind, sliceSize int) string {
if sliceSize == -1 {
return fmt.Sprintf("[]%v", kind)
}
return fmt.Sprintf("[%d]%v", sliceSize, kind)
}
// sliceTypeCheck checks that the given slice can by assigned to the reflection
// type in t.
func sliceTypeCheck(t Type, val reflect.Value) error {
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
}
if t.T == ArrayTy && val.Len() != t.Size {
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
}
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
if val.Len() > 0 {
return sliceTypeCheck(*t.Elem, val.Index(0))
}
}
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
}
return nil
}
// typeCheck checks that the given reflection value can be assigned to the reflection
// type in t.
func typeCheck(t Type, value reflect.Value) error {
if t.T == SliceTy || t.T == ArrayTy {
return sliceTypeCheck(t, value)
}
// Check base type validity. Element types will be checked later on.
if t.GetType().Kind() != value.Kind() {
return typeErr(t.GetType().Kind(), value.Kind())
} else if t.T == FixedBytesTy && t.Size != value.Len() {
return typeErr(t.GetType(), value.Type())
} else {
return nil
}
}
// typeErr returns a formatted type casting error.
func typeErr(expected, got interface{}) error {
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
}

View File

@ -123,15 +123,8 @@ func set(dst, src reflect.Value) error {
func setSlice(dst, src reflect.Value) error { func setSlice(dst, src reflect.Value) error {
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len()) slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
for i := 0; i < src.Len(); i++ { for i := 0; i < src.Len(); i++ {
if src.Index(i).Kind() == reflect.Struct { if err := set(slice.Index(i), src.Index(i)); err != nil {
if err := set(slice.Index(i), src.Index(i)); err != nil { return err
return err
}
} else {
// e.g. [][32]uint8 to []common.Hash
if err := set(slice.Index(i), src.Index(i)); err != nil {
return err
}
} }
} }
if dst.CanSet() { if dst.CanSet() {

View File

@ -1,19 +1,19 @@
# This file contains sha256 checksums of optional build dependencies. # This file contains sha256 checksums of optional build dependencies.
3a70e5055509f347c0fb831ca07a2bf3b531068f349b14a3c652e9b5b67beb5d go1.17.src.tar.gz 2255eb3e4e824dd7d5fcdc2e7f84534371c186312e546fb1086a34c17752f431 go1.17.2.src.tar.gz
355bd544ce08d7d484d9d7de05a71b5c6f5bc10aa4b316688c2192aeb3dacfd1 go1.17.darwin-amd64.tar.gz 7914497a302a132a465d33f5ee044ce05568bacdb390ab805cb75a3435a23f94 go1.17.2.darwin-amd64.tar.gz
da4e3e3c194bf9eed081de8842a157120ef44a7a8d7c820201adae7b0e28b20b go1.17.darwin-arm64.tar.gz ce8771bd3edfb5b28104084b56bbb532eeb47fbb7769c3e664c6223712c30904 go1.17.2.darwin-arm64.tar.gz
6819a7a11b8351d5d5768f2fff666abde97577602394f132cb7f85b3a7151f05 go1.17.freebsd-386.tar.gz 8cea5b8d1f8e8cbb58069bfed58954c71c5b1aca2f3c857765dae83bf724d0d7 go1.17.2.freebsd-386.tar.gz
15c184c83d99441d719da201b26256455eee85a808747c404b4183e9aa6c64b4 go1.17.freebsd-amd64.tar.gz c96e57218fb03e74d683ad63b1684d44c89d5e5b994f36102b33dce21b58499a go1.17.2.freebsd-amd64.tar.gz
c19e3227a6ac6329db91d1af77bbf239ccd760a259c16e6b9c932d527ff14848 go1.17.linux-386.tar.gz 8617f2e40d51076983502894181ae639d1d8101bfbc4d7463a2b442f239f5596 go1.17.2.linux-386.tar.gz
6bf89fc4f5ad763871cf7eac80a2d594492de7a818303283f1366a7f6a30372d go1.17.linux-amd64.tar.gz f242a9db6a0ad1846de7b6d94d507915d14062660616a61ef7c808a76e4f1676 go1.17.2.linux-amd64.tar.gz
01a9af009ada22122d3fcb9816049c1d21842524b38ef5d5a0e2ee4b26d7c3e7 go1.17.linux-arm64.tar.gz a5a43c9cdabdb9f371d56951b14290eba8ce2f9b0db48fb5fc657943984fd4fc go1.17.2.linux-arm64.tar.gz
ae89d33f4e4acc222bdb04331933d5ece4ae71039812f6ccd7493cb3e8ddfb4e go1.17.linux-armv6l.tar.gz 04d16105008230a9763005be05606f7eb1c683a3dbf0fbfed4034b23889cb7f2 go1.17.2.linux-armv6l.tar.gz
ee84350114d532bf15f096198c675aafae9ff091dc4cc69eb49e1817ff94dbd7 go1.17.linux-ppc64le.tar.gz 12e2dc7e0ffeebe77083f267ef6705fec1621cdf2ed6489b3af04a13597ed68d go1.17.2.linux-ppc64le.tar.gz
a50aaecf054f393575f969a9105d5c6864dd91afc5287d772449033fbafcf7e3 go1.17.linux-s390x.tar.gz c4b2349a8d11350ca038b8c57f3cc58dc0b31284bcbed4f7fca39aeed28b4a51 go1.17.2.linux-s390x.tar.gz
c5afdd2ea4969f2b44637e913b04f7c15265d7beb60924a28063722670a52feb go1.17.windows-386.zip 8a85257a351996fdf045fe95ed5fdd6917dd48636d562dd11dedf193005a53e0 go1.17.2.windows-386.zip
2a18bd65583e221be8b9b7c2fbe3696c40f6e27c2df689bbdcc939d49651d151 go1.17.windows-amd64.zip fa6da0b829a66f5fab7e4e312fd6aa1b2d8f045c7ecee83b3d00f6fe5306759a go1.17.2.windows-amd64.zip
5256f92f643d9022394ddc84de5c74fe8660c2151daaa199b12e60e542d694ae go1.17.windows-arm64.zip 00575c85dc7a129ba892685a456b27a3f3670f71c8bfde1c5ad151f771d55df7 go1.17.2.windows-arm64.zip
d4bd25b9814eeaa2134197dd2c7671bb791eae786d42010d9d788af20dee4bfa golangci-lint-1.42.0-darwin-amd64.tar.gz d4bd25b9814eeaa2134197dd2c7671bb791eae786d42010d9d788af20dee4bfa golangci-lint-1.42.0-darwin-amd64.tar.gz
e56859c04a2ad5390c6a497b1acb1cc9329ecb1010260c6faae9b5a4c35b35ea golangci-lint-1.42.0-darwin-arm64.tar.gz e56859c04a2ad5390c6a497b1acb1cc9329ecb1010260c6faae9b5a4c35b35ea golangci-lint-1.42.0-darwin-arm64.tar.gz

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build none
// +build none // +build none
/* /*
@ -147,7 +148,7 @@ var (
// This is the version of go that will be downloaded by // This is the version of go that will be downloaded by
// //
// go run ci.go install -dlgo // go run ci.go install -dlgo
dlgoVersion = "1.17" dlgoVersion = "1.17.2"
) )
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@ -259,6 +260,11 @@ func buildFlags(env build.Environment) (flags []string) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
ld = append(ld, "-s") ld = append(ld, "-s")
} }
// Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux.
if runtime.GOOS == "linux" {
ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000")
}
if len(ld) > 0 { if len(ld) > 0 {
flags = append(flags, "-ldflags", strings.Join(ld, " ")) flags = append(flags, "-ldflags", strings.Join(ld, " "))
} }
@ -276,6 +282,7 @@ func doTest(cmdline []string) {
cc = flag.String("cc", "", "Sets C compiler binary") cc = flag.String("cc", "", "Sets C compiler binary")
coverage = flag.Bool("coverage", false, "Whether to record code coverage") coverage = flag.Bool("coverage", false, "Whether to record code coverage")
verbose = flag.Bool("v", false, "Whether to log verbosely") verbose = flag.Bool("v", false, "Whether to log verbosely")
race = flag.Bool("race", false, "Execute the race detector")
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
@ -296,6 +303,9 @@ func doTest(cmdline []string) {
if *verbose { if *verbose {
gotest.Args = append(gotest.Args, "-v") gotest.Args = append(gotest.Args, "-v")
} }
if *race {
gotest.Args = append(gotest.Args, "-race")
}
packages := []string{"./..."} packages := []string{"./..."}
if len(flag.CommandLine.Args()) > 0 { if len(flag.CommandLine.Args()) > 0 {

View File

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"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"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -123,12 +124,22 @@ func Transaction(ctx *cli.Context) error {
results = append(results, result{Error: err}) results = append(results, result{Error: err})
continue continue
} }
sender, err := types.Sender(signer, &tx) r := result{Hash: tx.Hash()}
if err != nil { if sender, err := types.Sender(signer, &tx); err != nil {
results = append(results, result{Error: err}) r.Error = err
results = append(results, r)
continue continue
} else {
r.Address = sender
} }
results = append(results, result{Address: sender, Hash: tx.Hash()})
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil {
r.Error = err
} else if tx.Gas() < gas {
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
}
results = append(results, r)
} }
out, err := json.MarshalIndent(results, "", " ") out, err := json.MarshalIndent(results, "", " ")
fmt.Println(string(out)) fmt.Println(string(out))

View File

@ -419,7 +419,7 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
} }
os.Stdout.Write(b) os.Stdout.Write(b)
os.Stdout.Write([]byte("\n")) os.Stdout.WriteString("\n")
} }
if len(stdErrObject) > 0 { if len(stdErrObject) > 0 {
b, err := json.MarshalIndent(stdErrObject, "", " ") b, err := json.MarshalIndent(stdErrObject, "", " ")
@ -427,7 +427,7 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
} }
os.Stderr.Write(b) os.Stderr.Write(b)
os.Stderr.Write([]byte("\n")) os.Stderr.WriteString("\n")
} }
return nil return nil
} }

View File

@ -233,7 +233,7 @@ func TestT9n(t *testing.T) {
}, },
expOut: "exp.json", expOut: "exp.json",
}, },
{ // London txs on homestead { // London txs on London
base: "./testdata/15", base: "./testdata/15",
input: t9nInput{ input: t9nInput{
inTxs: "signed_txs.rlp", inTxs: "signed_txs.rlp",
@ -249,6 +249,14 @@ func TestT9n(t *testing.T) {
}, },
expOut: "exp3.json", expOut: "exp3.json",
}, },
{ // Transactions with too low gas
base: "./testdata/16",
input: t9nInput{
inTxs: "signed_txs.rlp",
stFork: "London",
},
expOut: "exp.json",
},
} { } {
args := []string{"t9n"} args := []string{"t9n"}

View File

@ -1,8 +1,10 @@
[ [
{ {
"error": "transaction type not supported" "error": "transaction type not supported",
"hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476"
}, },
{ {
"error": "transaction type not supported" "error": "transaction type not supported",
"hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a"
} }
] ]

11
cmd/evm/testdata/16/exp.json vendored Normal file
View File

@ -0,0 +1,11 @@
[
{
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"hash": "0x7cc3d1a8540a44736750f03bb4d85c0113be4b3472a71bf82241a3b261b479e6"
},
{
"error": "intrinsic gas too low: have 82, want 21000",
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"hash": "0x3b2d2609e4361562edb9169314f4c05afc6dbf5d706bf9dda5abe242ab76a22b"
}
]

1
cmd/evm/testdata/16/signed_txs.rlp vendored Normal file
View File

@ -0,0 +1 @@
"0xf8cab86401f8610180018252089411111111111111111111111111111111111111112080c001a0937f65ef1deece46c473b99962678fb7c38425cf303d1e8fa9717eb4b9d012b5a01940c5a5647c4940217ffde1051a5fd92ec8551e275c1787f81f50a2ad84de43b86201f85f018001529411111111111111111111111111111111111111112080c001a0241c3aec732205542a87fef8c76346741e85480bce5a42d05a9a73dac892f84ca04f52e2dfce57f3a02ed10e085e1a154edf38a726da34127c85fc53b4921759c8"

34
cmd/evm/testdata/16/unsigned_txs.json vendored Normal file
View File

@ -0,0 +1,34 @@
[
{
"input" : "0x",
"gas" : "0x5208",
"nonce" : "0x0",
"to" : "0x1111111111111111111111111111111111111111",
"value" : "0x20",
"v" : "0x0",
"r" : "0x0",
"s" : "0x0",
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"chainId" : "0x1",
"type" : "0x1",
"gasPrice": "0x1",
"accessList" : [
]
},
{
"input" : "0x",
"gas" : "0x52",
"nonce" : "0x0",
"to" : "0x1111111111111111111111111111111111111111",
"value" : "0x20",
"v" : "0x0",
"r" : "0x0",
"s" : "0x0",
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"chainId" : "0x1",
"type" : "0x1",
"gasPrice": "0x1",
"accessList" : [
]
}
]

View File

@ -741,7 +741,7 @@ func authTwitter(url string, tokenV1, tokenV2 string) (string, string, string, c
return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund") return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund")
} }
var avatar string var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+twimg.com/profile_images[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 { if parts = regexp.MustCompile(`src="([^"]+twimg\.com/profile_images[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
avatar = parts[1] avatar = parts[1]
} }
return username + "@twitter", username, avatar, address, nil return username + "@twitter", username, avatar, address, nil
@ -867,7 +867,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
return "", "", common.Address{}, errors.New("No Ethereum address found to fund") return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
} }
var avatar string var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+fbcdn.net[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 { if parts = regexp.MustCompile(`src="([^"]+fbcdn\.net[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
avatar = parts[1] avatar = parts[1]
} }
return username + "@facebook", avatar, address, nil return username + "@facebook", avatar, address, nil

View File

@ -176,6 +176,7 @@ var (
utils.IPCPathFlag, utils.IPCPathFlag,
utils.InsecureUnlockAllowedFlag, utils.InsecureUnlockAllowedFlag,
utils.RPCGlobalGasCapFlag, utils.RPCGlobalGasCapFlag,
utils.RPCGlobalEVMTimeoutFlag,
utils.RPCGlobalTxFeeCapFlag, utils.RPCGlobalTxFeeCapFlag,
utils.AllowUnprotectedTxs, utils.AllowUnprotectedTxs,
} }
@ -416,7 +417,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
} }
ethBackend, ok := backend.(*eth.EthAPIBackend) ethBackend, ok := backend.(*eth.EthAPIBackend)
if !ok { if !ok {
utils.Fatalf("Ethereum service not running: %v", err) utils.Fatalf("Ethereum service not running")
} }
// Set the gas price to the limits from the CLI and start mining // Set the gas price to the limits from the CLI and start mining
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)

View File

@ -150,6 +150,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.GraphQLCORSDomainFlag, utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag, utils.GraphQLVirtualHostsFlag,
utils.RPCGlobalGasCapFlag, utils.RPCGlobalGasCapFlag,
utils.RPCGlobalEVMTimeoutFlag,
utils.RPCGlobalTxFeeCapFlag, utils.RPCGlobalTxFeeCapFlag,
utils.AllowUnprotectedTxs, utils.AllowUnprotectedTxs,
utils.JSpathFlag, utils.JSpathFlag,

View File

@ -35,8 +35,8 @@ FROM puppeth/blockscout:latest
ADD genesis.json /genesis.json ADD genesis.json /genesis.json
RUN \ RUN \
echo 'geth --cache 512 init /genesis.json' > explorer.sh && \ echo 'geth --cache 512 init /genesis.json' > explorer.sh && \
echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \ echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug,txpool" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \
echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \ echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug,txpool" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \
echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \ echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \
echo 'sleep 5' >> explorer.sh && \ echo 'sleep 5' >> explorer.sh && \
echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \ echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \

View File

@ -493,6 +493,11 @@ var (
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
Value: ethconfig.Defaults.RPCGasCap, Value: ethconfig.Defaults.RPCGasCap,
} }
RPCGlobalEVMTimeoutFlag = cli.DurationFlag{
Name: "rpc.evmtimeout",
Usage: "Sets a timeout used for eth_call (0=infinite)",
Value: ethconfig.Defaults.RPCEVMTimeout,
}
RPCGlobalTxFeeCapFlag = cli.Float64Flag{ RPCGlobalTxFeeCapFlag = cli.Float64Flag{
Name: "rpc.txfeecap", Name: "rpc.txfeecap",
Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)",
@ -1563,6 +1568,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
} else { } else {
log.Info("Global gas cap disabled") log.Info("Global gas cap disabled")
} }
if ctx.GlobalIsSet(RPCGlobalEVMTimeoutFlag.Name) {
cfg.RPCEVMTimeout = ctx.GlobalDuration(RPCGlobalEVMTimeoutFlag.Name)
}
if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) { if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) {
cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name) cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name)
} }

View File

@ -363,7 +363,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header
} }
} }
// All basic checks passed, verify the seal and return // All basic checks passed, verify the seal and return
return c.verifySeal(chain, header, parents) return c.verifySeal(snap, header, parents)
} }
// snapshot retrieves the authorization snapshot at a given point in time. // snapshot retrieves the authorization snapshot at a given point in time.
@ -460,18 +460,12 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e
// consensus protocol requirements. The method accepts an optional list of parent // consensus protocol requirements. The method accepts an optional list of parent
// headers that aren't yet part of the local blockchain to generate the snapshots // headers that aren't yet part of the local blockchain to generate the snapshots
// from. // from.
func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { func (c *Clique) verifySeal(snap *Snapshot, header *types.Header, parents []*types.Header) error {
// Verifying the genesis block is not supported // Verifying the genesis block is not supported
number := header.Number.Uint64() number := header.Number.Uint64()
if number == 0 { if number == 0 {
return errUnknownBlock return errUnknownBlock
} }
// Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
if err != nil {
return err
}
// Resolve the authorization key and check against signers // Resolve the authorization key and check against signers
signer, err := ecrecover(header, c.signatures) signer, err := ecrecover(header, c.signatures)
if err != nil { if err != nil {

View File

@ -330,8 +330,6 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uin
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1) next := new(big.Int).Add(parent.Number, big1)
switch { switch {
case config.IsCatalyst(next):
return big.NewInt(1)
case config.IsLondon(next): case config.IsLondon(next):
return calcDifficultyEip3554(time, parent) return calcDifficultyEip3554(time, parent)
case config.IsMuirGlacier(next): case config.IsMuirGlacier(next):
@ -639,10 +637,6 @@ var (
// reward. The total reward consists of the static block reward and rewards for // reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded. // included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
// Skip block reward in catalyst mode
if config.IsCatalyst(header.Number) {
return
}
// Select the correct block reward based on chain progression // Select the correct block reward based on chain progression
blockReward := FrontierBlockReward blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) { if config.IsByzantium(header.Number) {

View File

@ -29,24 +29,24 @@ import (
// do not use e.g. SetInt() on the numbers. For testing only // do not use e.g. SetInt() on the numbers. For testing only
func copyConfig(original *params.ChainConfig) *params.ChainConfig { func copyConfig(original *params.ChainConfig) *params.ChainConfig {
return &params.ChainConfig{ return &params.ChainConfig{
ChainID: original.ChainID, ChainID: original.ChainID,
HomesteadBlock: original.HomesteadBlock, HomesteadBlock: original.HomesteadBlock,
DAOForkBlock: original.DAOForkBlock, DAOForkBlock: original.DAOForkBlock,
DAOForkSupport: original.DAOForkSupport, DAOForkSupport: original.DAOForkSupport,
EIP150Block: original.EIP150Block, EIP150Block: original.EIP150Block,
EIP150Hash: original.EIP150Hash, EIP150Hash: original.EIP150Hash,
EIP155Block: original.EIP155Block, EIP155Block: original.EIP155Block,
EIP158Block: original.EIP158Block, EIP158Block: original.EIP158Block,
ByzantiumBlock: original.ByzantiumBlock, ByzantiumBlock: original.ByzantiumBlock,
ConstantinopleBlock: original.ConstantinopleBlock, ConstantinopleBlock: original.ConstantinopleBlock,
PetersburgBlock: original.PetersburgBlock, PetersburgBlock: original.PetersburgBlock,
IstanbulBlock: original.IstanbulBlock, IstanbulBlock: original.IstanbulBlock,
MuirGlacierBlock: original.MuirGlacierBlock, MuirGlacierBlock: original.MuirGlacierBlock,
BerlinBlock: original.BerlinBlock, BerlinBlock: original.BerlinBlock,
LondonBlock: original.LondonBlock, LondonBlock: original.LondonBlock,
CatalystBlock: original.CatalystBlock, TerminalTotalDifficulty: original.TerminalTotalDifficulty,
Ethash: original.Ethash, Ethash: original.Ethash,
Clique: original.Clique, Clique: original.Clique,
} }
} }

View File

@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/syncx"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -80,6 +81,7 @@ var (
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil) blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
errInsertionInterrupted = errors.New("insertion is interrupted") errInsertionInterrupted = errors.New("insertion is interrupted")
errChainStopped = errors.New("blockchain is stopped")
) )
const ( const (
@ -183,7 +185,9 @@ type BlockChain struct {
scope event.SubscriptionScope scope event.SubscriptionScope
genesisBlock *types.Block genesisBlock *types.Block
chainmu sync.RWMutex // blockchain insertion lock // This mutex synchronizes chain write operations.
// Readers don't need to take it, they can just read the database.
chainmu *syncx.ClosableMutex
currentBlock atomic.Value // Current head of the block chain currentBlock atomic.Value // Current head of the block chain
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
@ -196,8 +200,8 @@ type BlockChain struct {
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
futureBlocks *lru.Cache // future blocks are blocks added for later processing futureBlocks *lru.Cache // future blocks are blocks added for later processing
quit chan struct{} // blockchain quit channel wg sync.WaitGroup //
wg sync.WaitGroup // chain processing wait group for shutting down quit chan struct{} // shutdown signal, closed in Stop.
running int32 // 0 if chain is running, 1 when stopped running int32 // 0 if chain is running, 1 when stopped
procInterrupt int32 // interrupt signaler for block processing procInterrupt int32 // interrupt signaler for block processing
@ -235,6 +239,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
Preimages: cacheConfig.Preimages, Preimages: cacheConfig.Preimages,
}), }),
quit: make(chan struct{}), quit: make(chan struct{}),
chainmu: syncx.NewClosableMutex(),
shouldPreserve: shouldPreserve, shouldPreserve: shouldPreserve,
bodyCache: bodyCache, bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache, bodyRLPCache: bodyRLPCache,
@ -278,6 +283,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if err := bc.loadLastState(); err != nil { if err := bc.loadLastState(); err != nil {
return nil, err return nil, err
} }
// Make sure the state associated with the block is available // Make sure the state associated with the block is available
head := bc.CurrentBlock() head := bc.CurrentBlock()
if _, err := state.New(head.Root(), bc.stateCache, bc.snaps); err != nil { if _, err := state.New(head.Root(), bc.stateCache, bc.snaps); err != nil {
@ -306,6 +312,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
} }
} }
} }
// Ensure that a previous crash in SetHead doesn't leave extra ancients // Ensure that a previous crash in SetHead doesn't leave extra ancients
if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 { if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 {
var ( var (
@ -357,6 +364,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
} }
} }
} }
// Load any existing snapshot, regenerating it if loading failed // Load any existing snapshot, regenerating it if loading failed
if bc.cacheConfig.SnapshotLimit > 0 { if bc.cacheConfig.SnapshotLimit > 0 {
// If the chain was rewound past the snapshot persistent layer (causing // If the chain was rewound past the snapshot persistent layer (causing
@ -372,14 +380,19 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
} }
bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover) bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover)
} }
// Take ownership of this particular state
go bc.update() // Start future block processor.
bc.wg.Add(1)
go bc.futureBlocksLoop()
// Start tx indexer/unindexer.
if txLookupLimit != nil { if txLookupLimit != nil {
bc.txLookupLimit = *txLookupLimit bc.txLookupLimit = *txLookupLimit
bc.wg.Add(1) bc.wg.Add(1)
go bc.maintainTxIndex(txIndexBlock) go bc.maintainTxIndex(txIndexBlock)
} }
// If periodic cache journal is required, spin it up. // If periodic cache journal is required, spin it up.
if bc.cacheConfig.TrieCleanRejournal > 0 { if bc.cacheConfig.TrieCleanRejournal > 0 {
if bc.cacheConfig.TrieCleanRejournal < time.Minute { if bc.cacheConfig.TrieCleanRejournal < time.Minute {
@ -488,7 +501,9 @@ func (bc *BlockChain) SetHead(head uint64) error {
// //
// The method returns the block number where the requested root cap was found. // The method returns the block number where the requested root cap was found.
func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) { func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) {
bc.chainmu.Lock() if !bc.chainmu.TryLock() {
return 0, errChainStopped
}
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
// Track the block number of the requested root hash // Track the block number of the requested root hash
@ -633,8 +648,11 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil { if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil {
return err return err
} }
// If all checks out, manually set the head block
bc.chainmu.Lock() // If all checks out, manually set the head block.
if !bc.chainmu.TryLock() {
return errChainStopped
}
bc.currentBlock.Store(block) bc.currentBlock.Store(block)
headBlockGauge.Update(int64(block.NumberU64())) headBlockGauge.Update(int64(block.NumberU64()))
bc.chainmu.Unlock() bc.chainmu.Unlock()
@ -707,7 +725,9 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
if err := bc.SetHead(0); err != nil { if err := bc.SetHead(0); err != nil {
return err return err
} }
bc.chainmu.Lock() if !bc.chainmu.TryLock() {
return errChainStopped
}
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
// Prepare the genesis block and reinitialise the chain // Prepare the genesis block and reinitialise the chain
@ -737,8 +757,10 @@ func (bc *BlockChain) Export(w io.Writer) error {
// ExportN writes a subset of the active chain to the given writer. // ExportN writes a subset of the active chain to the given writer.
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
bc.chainmu.RLock() if !bc.chainmu.TryLock() {
defer bc.chainmu.RUnlock() return errChainStopped
}
defer bc.chainmu.Unlock()
if first > last { if first > last {
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
@ -991,10 +1013,21 @@ func (bc *BlockChain) Stop() {
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
return return
} }
// Unsubscribe all subscriptions registered from blockchain
// Unsubscribe all subscriptions registered from blockchain.
bc.scope.Close() bc.scope.Close()
// Signal shutdown to all goroutines.
close(bc.quit) close(bc.quit)
bc.StopInsert() bc.StopInsert()
// Now wait for all chain modifications to end and persistent goroutines to exit.
//
// Note: Close waits for the mutex to become available, i.e. any running chain
// modification will have exited when Close returns. Since we also called StopInsert,
// the mutex should become available quickly. It cannot be taken again after Close has
// returned.
bc.chainmu.Close()
bc.wg.Wait() bc.wg.Wait()
// Ensure that the entirety of the state snapshot is journalled to disk. // Ensure that the entirety of the state snapshot is journalled to disk.
@ -1005,6 +1038,7 @@ func (bc *BlockChain) Stop() {
log.Error("Failed to journal state snapshot", "err", err) log.Error("Failed to journal state snapshot", "err", err)
} }
} }
// Ensure the state of a recent block is also stored to disk before exiting. // Ensure the state of a recent block is also stored to disk before exiting.
// We're writing three different states to catch different restart scenarios: // We're writing three different states to catch different restart scenarios:
// - HEAD: So we don't need to reprocess any blocks in the general case // - HEAD: So we don't need to reprocess any blocks in the general case
@ -1128,7 +1162,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// updateHead updates the head fast sync block if the inserted blocks are better // updateHead updates the head fast sync block if the inserted blocks are better
// and returns an indicator whether the inserted blocks are canonical. // and returns an indicator whether the inserted blocks are canonical.
updateHead := func(head *types.Block) bool { updateHead := func(head *types.Block) bool {
bc.chainmu.Lock() if !bc.chainmu.TryLock() {
return false
}
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
// Rewind may have occurred, skip in that case. // Rewind may have occurred, skip in that case.
@ -1372,8 +1408,9 @@ var lastWrite uint64
// but does not write any state. This is used to construct competing side forks // but does not write any state. This is used to construct competing side forks
// up to the point where they exceed the canonical total difficulty. // up to the point where they exceed the canonical total difficulty.
func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) { func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) {
bc.wg.Add(1) if bc.insertStopped() {
defer bc.wg.Done() return errInsertionInterrupted
}
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td) rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
@ -1387,9 +1424,6 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
// writeKnownBlock updates the head block flag with a known block // writeKnownBlock updates the head block flag with a known block
// and introduces chain reorg if necessary. // and introduces chain reorg if necessary.
func (bc *BlockChain) writeKnownBlock(block *types.Block) error { func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
bc.wg.Add(1)
defer bc.wg.Done()
current := bc.CurrentBlock() current := bc.CurrentBlock()
if block.ParentHash() != current.Hash() { if block.ParentHash() != current.Hash() {
if err := bc.reorg(current, block); err != nil { if err := bc.reorg(current, block); err != nil {
@ -1402,17 +1436,19 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
// WriteBlockWithState writes the block and all associated state to the database. // WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
bc.chainmu.Lock() if !bc.chainmu.TryLock() {
return NonStatTy, errInsertionInterrupted
}
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent) return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent)
} }
// writeBlockWithState writes the block and all associated state to the database, // writeBlockWithState writes the block and all associated state to the database,
// but is expects the chain mutex to be held. // but is expects the chain mutex to be held.
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
bc.wg.Add(1) if bc.insertStopped() {
defer bc.wg.Done() return NonStatTy, errInsertionInterrupted
}
// Calculate the total difficulty of the block // Calculate the total difficulty of the block
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
@ -1578,31 +1614,28 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
bc.blockProcFeed.Send(true) bc.blockProcFeed.Send(true)
defer bc.blockProcFeed.Send(false) defer bc.blockProcFeed.Send(false)
// Remove already known canon-blocks // Do a sanity check that the provided chain is actually ordered and linked.
var (
block, prev *types.Block
)
// Do a sanity check that the provided chain is actually ordered and linked
for i := 1; i < len(chain); i++ { for i := 1; i < len(chain); i++ {
block = chain[i] block, prev := chain[i], chain[i-1]
prev = chain[i-1]
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
// Chain broke ancestry, log a message (programming error) and skip insertion log.Error("Non contiguous block insert",
log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(), "number", block.Number(),
"parent", block.ParentHash(), "prevnumber", prev.Number(), "prevhash", prev.Hash()) "hash", block.Hash(),
"parent", block.ParentHash(),
"prevnumber", prev.Number(),
"prevhash", prev.Hash(),
)
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, prev.NumberU64(), return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, prev.NumberU64(),
prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4]) prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
} }
} }
// Pre-checks passed, start the full block imports
bc.wg.Add(1)
bc.chainmu.Lock()
n, err := bc.insertChain(chain, true)
bc.chainmu.Unlock()
bc.wg.Done()
return n, err // Pre-check passed, start the full block imports.
if !bc.chainmu.TryLock() {
return 0, errChainStopped
}
defer bc.chainmu.Unlock()
return bc.insertChain(chain, true)
} }
// InsertChainWithoutSealVerification works exactly the same // InsertChainWithoutSealVerification works exactly the same
@ -1611,14 +1644,11 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (in
bc.blockProcFeed.Send(true) bc.blockProcFeed.Send(true)
defer bc.blockProcFeed.Send(false) defer bc.blockProcFeed.Send(false)
// Pre-checks passed, start the full block imports if !bc.chainmu.TryLock() {
bc.wg.Add(1) return 0, errChainStopped
bc.chainmu.Lock() }
n, err := bc.insertChain(types.Blocks([]*types.Block{block}), false) defer bc.chainmu.Unlock()
bc.chainmu.Unlock() return bc.insertChain(types.Blocks([]*types.Block{block}), false)
bc.wg.Done()
return n, err
} }
// insertChain is the internal implementation of InsertChain, which assumes that // insertChain is the internal implementation of InsertChain, which assumes that
@ -1630,10 +1660,11 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (in
// is imported, but then new canon-head is added before the actual sidechain // is imported, but then new canon-head is added before the actual sidechain
// completes, then the historic state could be pruned again // completes, then the historic state could be pruned again
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) { func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) {
// If the chain is terminating, don't even bother starting up // If the chain is terminating, don't even bother starting up.
if atomic.LoadInt32(&bc.procInterrupt) == 1 { if bc.insertStopped() {
return 0, nil return 0, nil
} }
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain) senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
@ -1668,8 +1699,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// First block (and state) is known // First block (and state) is known
// 1. We did a roll-back, and should now do a re-import // 1. We did a roll-back, and should now do a re-import
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot // 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
// from the canonical chain, which has not been verified. // from the canonical chain, which has not been verified.
// Skip all known blocks that are behind us // Skip all known blocks that are behind us.
var ( var (
current = bc.CurrentBlock() current = bc.CurrentBlock()
localTd = bc.GetTd(current.Hash(), current.NumberU64()) localTd = bc.GetTd(current.Hash(), current.NumberU64())
@ -1793,9 +1824,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
lastCanon = block lastCanon = block
continue continue
} }
// Retrieve the parent block and it's state to execute on top // Retrieve the parent block and it's state to execute on top
start := time.Now() start := time.Now()
parent := it.previous() parent := it.previous()
if parent == nil { if parent == nil {
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
@ -1804,6 +1835,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
if err != nil { if err != nil {
return it.index, err return it.index, err
} }
// Enable prefetching to pull in trie node paths while processing transactions // Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain") statedb.StartPrefetcher("chain")
activeState = statedb activeState = statedb
@ -1825,6 +1857,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
}(time.Now(), followup, throwaway, &followupInterrupt) }(time.Now(), followup, throwaway, &followupInterrupt)
} }
} }
// Process block using the parent state as reference point // Process block using the parent state as reference point
substart := time.Now() substart := time.Now()
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
@ -1833,6 +1866,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
atomic.StoreUint32(&followupInterrupt, 1) atomic.StoreUint32(&followupInterrupt, 1)
return it.index, err return it.index, err
} }
// Update the metrics touched during block processing // Update the metrics touched during block processing
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them
@ -1908,6 +1942,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
dirty, _ := bc.stateCache.TrieDB().Size() dirty, _ := bc.stateCache.TrieDB().Size()
stats.report(chain, it.index, dirty) stats.report(chain, it.index, dirty)
} }
// Any blocks remaining here? The only ones we care about are the future ones // Any blocks remaining here? The only ones we care about are the future ones
if block != nil && errors.Is(err, consensus.ErrFutureBlock) { if block != nil && errors.Is(err, consensus.ErrFutureBlock) {
if err := bc.addFutureBlock(block); err != nil { if err := bc.addFutureBlock(block); err != nil {
@ -2218,7 +2253,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
return nil return nil
} }
func (bc *BlockChain) update() { // futureBlocksLoop processes the 'future block' queue.
func (bc *BlockChain) futureBlocksLoop() {
defer bc.wg.Done()
futureTimer := time.NewTicker(5 * time.Second) futureTimer := time.NewTicker(5 * time.Second)
defer futureTimer.Stop() defer futureTimer.Stop()
for { for {
@ -2255,6 +2293,7 @@ func (bc *BlockChain) maintainTxIndex(ancients uint64) {
} }
rawdb.IndexTransactions(bc.db, from, ancients, bc.quit) rawdb.IndexTransactions(bc.db, from, ancients, bc.quit)
} }
// indexBlocks reindexes or unindexes transactions depending on user configuration // indexBlocks reindexes or unindexes transactions depending on user configuration
indexBlocks := func(tail *uint64, head uint64, done chan struct{}) { indexBlocks := func(tail *uint64, head uint64, done chan struct{}) {
defer func() { done <- struct{}{} }() defer func() { done <- struct{}{} }()
@ -2287,6 +2326,7 @@ func (bc *BlockChain) maintainTxIndex(ancients uint64) {
rawdb.UnindexTransactions(bc.db, *tail, head-bc.txLookupLimit+1, bc.quit) rawdb.UnindexTransactions(bc.db, *tail, head-bc.txLookupLimit+1, bc.quit)
} }
} }
// Any reindexing done, start listening to chain events and moving the index window // Any reindexing done, start listening to chain events and moving the index window
var ( var (
done chan struct{} // Non-nil if background unindexing or reindexing routine is active. done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
@ -2354,12 +2394,10 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
return i, err return i, err
} }
// Make sure only one thread manipulates the chain at once if !bc.chainmu.TryLock() {
bc.chainmu.Lock() return 0, errChainStopped
}
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
bc.wg.Add(1)
defer bc.wg.Done()
_, err := bc.hc.InsertHeaderChain(chain, start) _, err := bc.hc.InsertHeaderChain(chain, start)
return 0, err return 0, err
} }
@ -2376,12 +2414,6 @@ func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
return bc.hc.GetTd(hash, number) return bc.hc.GetTd(hash, number)
} }
// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
func (bc *BlockChain) GetTdByHash(hash common.Hash) *big.Int {
return bc.hc.GetTdByHash(hash)
}
// GetHeader retrieves a block header from the database by hash and number, // GetHeader retrieves a block header from the database by hash and number,
// caching it if found. // caching it if found.
func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header { func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
@ -2415,12 +2447,6 @@ func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash {
return bc.hc.GetCanonicalHash(number) return bc.hc.GetCanonicalHash(number)
} }
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
return bc.hc.GetBlockHashesFromHash(hash, max)
}
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or // GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the // a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
// number of blocks to be individually checked before we reach the canonical chain. // number of blocks to be individually checked before we reach the canonical chain.

View File

@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
} }
if basic.snapshotBlock > 0 && basic.snapshotBlock == point { if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
// Flushing the entire snap tree into the disk, the // Flushing the entire snap tree into the disk, the
// relavant (a) snapshot root and (b) snapshot generator // relevant (a) snapshot root and (b) snapshot generator
// will be persisted atomically. // will be persisted atomically.
chain.snaps.Cap(blocks[point-1].Root(), 0) chain.snaps.Cap(blocks[point-1].Root(), 0)
diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root() diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root()

View File

@ -118,17 +118,21 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
var tdPre, tdPost *big.Int var tdPre, tdPost *big.Int
if full { if full {
tdPre = blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()) cur := blockchain.CurrentBlock()
tdPre = blockchain.GetTd(cur.Hash(), cur.NumberU64())
if err := testBlockChainImport(blockChainB, blockchain); err != nil { if err := testBlockChainImport(blockChainB, blockchain); err != nil {
t.Fatalf("failed to import forked block chain: %v", err) t.Fatalf("failed to import forked block chain: %v", err)
} }
tdPost = blockchain.GetTdByHash(blockChainB[len(blockChainB)-1].Hash()) last := blockChainB[len(blockChainB)-1]
tdPost = blockchain.GetTd(last.Hash(), last.NumberU64())
} else { } else {
tdPre = blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()) cur := blockchain.CurrentHeader()
tdPre = blockchain.GetTd(cur.Hash(), cur.Number.Uint64())
if err := testHeaderChainImport(headerChainB, blockchain); err != nil { if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
t.Fatalf("failed to import forked header chain: %v", err) t.Fatalf("failed to import forked header chain: %v", err)
} }
tdPost = blockchain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) last := headerChainB[len(headerChainB)-1]
tdPost = blockchain.GetTd(last.Hash(), last.Number.Uint64())
} }
// Compare the total difficulties of the chains // Compare the total difficulties of the chains
comparator(tdPre, tdPost) comparator(tdPre, tdPost)
@ -163,8 +167,9 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.reportBlock(block, receipts, err) blockchain.reportBlock(block, receipts, err)
return err return err
} }
blockchain.chainmu.Lock()
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) blockchain.chainmu.MustLock()
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash(), block.NumberU64()-1)))
rawdb.WriteBlock(blockchain.db, block) rawdb.WriteBlock(blockchain.db, block)
statedb.Commit(false) statedb.Commit(false)
blockchain.chainmu.Unlock() blockchain.chainmu.Unlock()
@ -181,8 +186,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
return err return err
} }
// Manually insert the header into the database, but don't reorganise (allows subsequent testing) // Manually insert the header into the database, but don't reorganise (allows subsequent testing)
blockchain.chainmu.Lock() blockchain.chainmu.MustLock()
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash, header.Number.Uint64()-1)))
rawdb.WriteHeader(blockchain.db, header) rawdb.WriteHeader(blockchain.db, header)
blockchain.chainmu.Unlock() blockchain.chainmu.Unlock()
} }
@ -435,11 +440,13 @@ func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
// Make sure the chain total difficulty is the correct one // Make sure the chain total difficulty is the correct one
want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td)) want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td))
if full { if full {
if have := blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()); have.Cmp(want) != 0 { cur := blockchain.CurrentBlock()
if have := blockchain.GetTd(cur.Hash(), cur.NumberU64()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want) t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
} }
} else { } else {
if have := blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()); have.Cmp(want) != 0 { cur := blockchain.CurrentHeader()
if have := blockchain.GetTd(cur.Hash(), cur.Number.Uint64()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want) t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
} }
} }
@ -675,10 +682,10 @@ func TestFastVsFullChains(t *testing.T) {
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
num, hash := blocks[i].NumberU64(), blocks[i].Hash() num, hash := blocks[i].NumberU64(), blocks[i].Hash()
if ftd, atd := fast.GetTdByHash(hash), archive.GetTdByHash(hash); ftd.Cmp(atd) != 0 { if ftd, atd := fast.GetTd(hash, num), archive.GetTd(hash, num); ftd.Cmp(atd) != 0 {
t.Errorf("block #%d [%x]: td mismatch: fastdb %v, archivedb %v", num, hash, ftd, atd) t.Errorf("block #%d [%x]: td mismatch: fastdb %v, archivedb %v", num, hash, ftd, atd)
} }
if antd, artd := ancient.GetTdByHash(hash), archive.GetTdByHash(hash); antd.Cmp(artd) != 0 { if antd, artd := ancient.GetTd(hash, num), archive.GetTd(hash, num); antd.Cmp(artd) != 0 {
t.Errorf("block #%d [%x]: td mismatch: ancientdb %v, archivedb %v", num, hash, antd, artd) t.Errorf("block #%d [%x]: td mismatch: ancientdb %v, archivedb %v", num, hash, antd, artd)
} }
if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() { if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() {
@ -2054,6 +2061,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha
// 1. Have a chain [0 ... N .. X] // 1. Have a chain [0 ... N .. X]
// 2. Reorg to shorter but heavier chain [0 ... N ... Y] // 2. Reorg to shorter but heavier chain [0 ... N ... Y]
// 3. Then there should be no canon mapping for the block at height X // 3. Then there should be no canon mapping for the block at height X
// 4. The forked block should still be retrievable by hash
func TestReorgToShorterRemovesCanonMapping(t *testing.T) { func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
chain, canonblocks, sideblocks, err := getLongAndShortChains() chain, canonblocks, sideblocks, err := getLongAndShortChains()
if err != nil { if err != nil {
@ -2063,6 +2071,7 @@ func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", n, err) t.Fatalf("block %d: failed to insert into chain: %v", n, err)
} }
canonNum := chain.CurrentBlock().NumberU64() canonNum := chain.CurrentBlock().NumberU64()
canonHash := chain.CurrentBlock().Hash()
_, err = chain.InsertChain(sideblocks) _, err = chain.InsertChain(sideblocks)
if err != nil { if err != nil {
t.Errorf("Got error, %v", err) t.Errorf("Got error, %v", err)
@ -2078,6 +2087,12 @@ func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil { if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64()) t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
} }
if blockByHash := chain.GetBlockByHash(canonHash); blockByHash == nil {
t.Errorf("expected block to be present: %x", blockByHash.Hash())
}
if headerByHash := chain.GetHeaderByHash(canonHash); headerByHash == nil {
t.Errorf("expected header to be present: %x", headerByHash.Hash())
}
} }
// TestReorgToShorterRemovesCanonMappingHeaderChain is the same scenario // TestReorgToShorterRemovesCanonMappingHeaderChain is the same scenario
@ -2097,6 +2112,7 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
t.Fatalf("header %d: failed to insert into chain: %v", n, err) t.Fatalf("header %d: failed to insert into chain: %v", n, err)
} }
canonNum := chain.CurrentHeader().Number.Uint64() canonNum := chain.CurrentHeader().Number.Uint64()
canonHash := chain.CurrentBlock().Hash()
sideHeaders := make([]*types.Header, len(sideblocks)) sideHeaders := make([]*types.Header, len(sideblocks))
for i, block := range sideblocks { for i, block := range sideblocks {
sideHeaders[i] = block.Header() sideHeaders[i] = block.Header()
@ -2115,6 +2131,12 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil { if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64()) t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
} }
if blockByHash := chain.GetBlockByHash(canonHash); blockByHash == nil {
t.Errorf("expected block to be present: %x", blockByHash.Hash())
}
if headerByHash := chain.GetHeaderByHash(canonHash); headerByHash == nil {
t.Errorf("expected header to be present: %x", headerByHash.Hash())
}
} }
func TestTransactionIndices(t *testing.T) { func TestTransactionIndices(t *testing.T) {

View File

@ -70,7 +70,7 @@ func BenchmarkGenerator(b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("failed to create bloombit generator: %v", err) b.Fatalf("failed to create bloombit generator: %v", err)
} }
for j, bloom := range input { for j, bloom := range &input {
if err := gen.AddBloom(uint(j), bloom); err != nil { if err := gen.AddBloom(uint(j), bloom); err != nil {
b.Fatalf("bloom %d: failed to add: %v", i, err) b.Fatalf("bloom %d: failed to add: %v", i, err)
} }
@ -89,7 +89,7 @@ func BenchmarkGenerator(b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("failed to create bloombit generator: %v", err) b.Fatalf("failed to create bloombit generator: %v", err)
} }
for j, bloom := range input { for j, bloom := range &input {
if err := gen.AddBloom(uint(j), bloom); err != nil { if err := gen.AddBloom(uint(j), bloom); err != nil {
b.Fatalf("bloom %d: failed to add: %v", i, err) b.Fatalf("bloom %d: failed to add: %v", i, err)
} }

View File

@ -394,29 +394,6 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time)
return res.status, err return res.status, err
} }
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
// Get the origin header from which to fetch
header := hc.GetHeaderByHash(hash)
if header == nil {
return nil
}
// Iterate the headers until enough is collected or the genesis reached
chain := make([]common.Hash, 0, max)
for i := uint64(0); i < max; i++ {
next := header.ParentHash
if header = hc.GetHeader(next, header.Number.Uint64()-1); header == nil {
break
}
chain = append(chain, next)
if header.Number.Sign() == 0 {
break
}
}
return chain
}
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or // GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the // a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
// number of blocks to be individually checked before we reach the canonical chain. // number of blocks to be individually checked before we reach the canonical chain.
@ -472,16 +449,6 @@ func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
return td return td
} }
// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int {
number := hc.GetBlockNumber(hash)
if number == nil {
return nil
}
return hc.GetTd(hash, *number)
}
// GetHeader retrieves a block header from the database by hash and number, // GetHeader retrieves a block header from the database by hash and number,
// caching it if found. // caching it if found.
func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header { func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {

View File

@ -33,14 +33,14 @@ type journalEntry interface {
} }
// journal contains the list of state modifications applied since the last state // journal contains the list of state modifications applied since the last state
// commit. These are tracked to be able to be reverted in case of an execution // commit. These are tracked to be able to be reverted in the case of an execution
// exception or revertal request. // exception or request for reversal.
type journal struct { type journal struct {
entries []journalEntry // Current changes tracked by the journal entries []journalEntry // Current changes tracked by the journal
dirties map[common.Address]int // Dirty accounts and the number of changes dirties map[common.Address]int // Dirty accounts and the number of changes
} }
// newJournal create a new initialized journal. // newJournal creates a new initialized journal.
func newJournal() *journal { func newJournal() *journal {
return &journal{ return &journal{
dirties: make(map[common.Address]int), dirties: make(map[common.Address]int),

View File

@ -163,6 +163,9 @@ type Tree struct {
cache int // Megabytes permitted to use for read caches cache int // Megabytes permitted to use for read caches
layers map[common.Hash]snapshot // Collection of all known layers layers map[common.Hash]snapshot // Collection of all known layers
lock sync.RWMutex lock sync.RWMutex
// Test hooks
onFlatten func() // Hook invoked when the bottom most diff layers are flattened
} }
// New attempts to load an already existing snapshot from a persistent key-value // New attempts to load an already existing snapshot from a persistent key-value
@ -463,14 +466,21 @@ func (t *Tree) cap(diff *diffLayer, layers int) *diskLayer {
return nil return nil
case *diffLayer: case *diffLayer:
// Hold the write lock until the flattened parent is linked correctly.
// Otherwise, the stale layer may be accessed by external reads in the
// meantime.
diff.lock.Lock()
defer diff.lock.Unlock()
// Flatten the parent into the grandparent. The flattening internally obtains a // Flatten the parent into the grandparent. The flattening internally obtains a
// write lock on grandparent. // write lock on grandparent.
flattened := parent.flatten().(*diffLayer) flattened := parent.flatten().(*diffLayer)
t.layers[flattened.root] = flattened t.layers[flattened.root] = flattened
diff.lock.Lock() // Invoke the hook if it's registered. Ugly hack.
defer diff.lock.Unlock() if t.onFlatten != nil {
t.onFlatten()
}
diff.parent = flattened diff.parent = flattened
if flattened.memory < aggregatorMemoryLimit { if flattened.memory < aggregatorMemoryLimit {
// Accumulator layer is smaller than the limit, so we can abort, unless // Accumulator layer is smaller than the limit, so we can abort, unless

View File

@ -22,6 +22,7 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"testing" "testing"
"time"
"github.com/VictoriaMetrics/fastcache" "github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -324,7 +325,7 @@ func TestPostCapBasicDataAccess(t *testing.T) {
} }
} }
// TestSnaphots tests the functionality for retrieveing the snapshot // TestSnaphots tests the functionality for retrieving the snapshot
// with given head root and the desired depth. // with given head root and the desired depth.
func TestSnaphots(t *testing.T) { func TestSnaphots(t *testing.T) {
// setAccount is a helper to construct a random account entry and assign it to // setAccount is a helper to construct a random account entry and assign it to
@ -423,3 +424,63 @@ func TestSnaphots(t *testing.T) {
} }
} }
} }
// TestReadStateDuringFlattening tests the scenario that, during the
// bottom diff layers are merging which tags these as stale, the read
// happens via a pre-created top snapshot layer which tries to access
// the state in these stale layers. Ensure this read can retrieve the
// right state back(block until the flattening is finished) instead of
// an unexpected error(snapshot layer is stale).
func TestReadStateDuringFlattening(t *testing.T) {
// setAccount is a helper to construct a random account entry and assign it to
// an account slot in a snapshot
setAccount := func(accKey string) map[common.Hash][]byte {
return map[common.Hash][]byte{
common.HexToHash(accKey): randomAccount(),
}
}
// Create a starting base layer and a snapshot tree out of it
base := &diskLayer{
diskdb: rawdb.NewMemoryDatabase(),
root: common.HexToHash("0x01"),
cache: fastcache.New(1024 * 500),
}
snaps := &Tree{
layers: map[common.Hash]snapshot{
base.root: base,
},
}
// 4 layers in total, 3 diff layers and 1 disk layers
snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
// Obtain the topmost snapshot handler for state accessing
snap := snaps.Snapshot(common.HexToHash("0xa3"))
// Register the testing hook to access the state after flattening
var result = make(chan *Account)
snaps.onFlatten = func() {
// Spin up a thread to read the account from the pre-created
// snapshot handler. It's expected to be blocked.
go func() {
account, _ := snap.Account(common.HexToHash("0xa1"))
result <- account
}()
select {
case res := <-result:
t.Fatalf("Unexpected return %v", res)
case <-time.NewTimer(time.Millisecond * 300).C:
}
}
// Cap the snap tree, which will mark the bottom-most layer as stale.
snaps.Cap(common.HexToHash("0xa3"), 1)
select {
case account := <-result:
if account == nil {
t.Fatal("Failed to retrieve account")
}
case <-time.NewTimer(time.Millisecond * 300).C:
t.Fatal("Unexpected blocker")
}
}

View File

@ -228,7 +228,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
} }
enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes())) enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
} }
// If snapshot unavailable or reading from it failed, load from the database // If the snapshot is unavailable or reading from it fails, load from the database.
if s.db.snap == nil || err != nil { if s.db.snap == nil || err != nil {
if meter != nil { if meter != nil {
// If we already spent time checking the snapshot, account for it // If we already spent time checking the snapshot, account for it

View File

@ -533,7 +533,7 @@ func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.
// The enforceTips parameter can be used to do an extra filtering on the pending // The enforceTips parameter can be used to do an extra filtering on the pending
// transactions and only return those whose **effective** tip is large enough in // transactions and only return those whose **effective** tip is large enough in
// the next pending execution environment. // the next pending execution environment.
func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) { func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
pool.mu.Lock() pool.mu.Lock()
defer pool.mu.Unlock() defer pool.mu.Unlock()
@ -554,7 +554,7 @@ func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transact
pending[addr] = txs pending[addr] = txs
} }
} }
return pending, nil return pending
} }
// Locals retrieves the accounts currently considered local by the pool. // Locals retrieves the accounts currently considered local by the pool.

View File

@ -255,10 +255,6 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
trigger = true trigger = true
<-pool.requestReset(nil, nil) <-pool.requestReset(nil, nil)
_, err := pool.Pending(false)
if err != nil {
t.Fatalf("Could not fetch pending transactions: %v", err)
}
nonce = pool.Nonce(address) nonce = pool.Nonce(address)
if nonce != 2 { if nonce != 2 {
t.Fatalf("Invalid nonce, want 2, got %d", nonce) t.Fatalf("Invalid nonce, want 2, got %d", nonce)

View File

@ -59,7 +59,7 @@ type AccessListTx struct {
func (tx *AccessListTx) copy() TxData { func (tx *AccessListTx) copy() TxData {
cpy := &AccessListTx{ cpy := &AccessListTx{
Nonce: tx.Nonce, Nonce: tx.Nonce,
To: tx.To, // TODO: copy pointed-to address To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data), Data: common.CopyBytes(tx.Data),
Gas: tx.Gas, Gas: tx.Gas,
// These are copied below. // These are copied below.
@ -96,7 +96,6 @@ func (tx *AccessListTx) copy() TxData {
// accessors for innerTx. // accessors for innerTx.
func (tx *AccessListTx) txType() byte { return AccessListTxType } func (tx *AccessListTx) txType() byte { return AccessListTxType }
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
func (tx *AccessListTx) protected() bool { return true }
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data } func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas } func (tx *AccessListTx) gas() uint64 { return tx.Gas }

View File

@ -43,7 +43,7 @@ type DynamicFeeTx struct {
func (tx *DynamicFeeTx) copy() TxData { func (tx *DynamicFeeTx) copy() TxData {
cpy := &DynamicFeeTx{ cpy := &DynamicFeeTx{
Nonce: tx.Nonce, Nonce: tx.Nonce,
To: tx.To, // TODO: copy pointed-to address To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data), Data: common.CopyBytes(tx.Data),
Gas: tx.Gas, Gas: tx.Gas,
// These are copied below. // These are copied below.
@ -84,7 +84,6 @@ func (tx *DynamicFeeTx) copy() TxData {
// accessors for innerTx. // accessors for innerTx.
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
func (tx *DynamicFeeTx) protected() bool { return true }
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data } func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }

View File

@ -62,7 +62,7 @@ func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPric
func (tx *LegacyTx) copy() TxData { func (tx *LegacyTx) copy() TxData {
cpy := &LegacyTx{ cpy := &LegacyTx{
Nonce: tx.Nonce, Nonce: tx.Nonce,
To: tx.To, // TODO: copy pointed-to address To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data), Data: common.CopyBytes(tx.Data),
Gas: tx.Gas, Gas: tx.Gas,
// These are initialized below. // These are initialized below.

View File

@ -144,13 +144,29 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
buf := encodeBufferPool.Get().(*bytes.Buffer) buf := encodeBufferPool.Get().(*bytes.Buffer)
defer encodeBufferPool.Put(buf) defer encodeBufferPool.Put(buf)
buf.Reset() buf.Reset()
buf.WriteByte(r.Type) if err := r.encodeTyped(data, buf); err != nil {
if err := rlp.Encode(buf, data); err != nil {
return err return err
} }
return rlp.Encode(w, buf.Bytes()) return rlp.Encode(w, buf.Bytes())
} }
// encodeTyped writes the canonical encoding of a typed receipt to w.
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
w.WriteByte(r.Type)
return rlp.Encode(w, data)
}
// MarshalBinary returns the consensus encoding of the receipt.
func (r *Receipt) MarshalBinary() ([]byte, error) {
if r.Type == LegacyTxType {
return rlp.EncodeToBytes(r)
}
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
var buf bytes.Buffer
err := r.encodeTyped(data, &buf)
return buf.Bytes(), err
}
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt // DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream. // from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error { func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
@ -189,6 +205,42 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
} }
} }
// UnmarshalBinary decodes the consensus encoding of receipts.
// It supports legacy RLP receipts and EIP-2718 typed receipts.
func (r *Receipt) UnmarshalBinary(b []byte) error {
if len(b) > 0 && b[0] > 0x7f {
// It's a legacy receipt decode the RLP
var data receiptRLP
err := rlp.DecodeBytes(b, &data)
if err != nil {
return err
}
r.Type = LegacyTxType
return r.setFromRLP(data)
}
// It's an EIP2718 typed transaction envelope.
return r.decodeTyped(b)
}
// decodeTyped decodes a typed receipt from the canonical format.
func (r *Receipt) decodeTyped(b []byte) error {
if len(b) == 0 {
return errEmptyTypedReceipt
}
switch b[0] {
case DynamicFeeTxType, AccessListTxType:
var data receiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
return err
}
r.Type = b[0]
return r.setFromRLP(data)
default:
return ErrTxTypeNotSupported
}
}
func (r *Receipt) setFromRLP(data receiptRLP) error { func (r *Receipt) setFromRLP(data receiptRLP) error {
r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
return r.setStatus(data.PostStateOrStatus) return r.setStatus(data.PostStateOrStatus)
@ -354,42 +406,42 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
// DeriveFields fills the receipts with their computed fields based on consensus // DeriveFields fills the receipts with their computed fields based on consensus
// data and contextual infos like containing block and transactions. // data and contextual infos like containing block and transactions.
func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
signer := MakeSigner(config, new(big.Int).SetUint64(number)) signer := MakeSigner(config, new(big.Int).SetUint64(number))
logIndex := uint(0) logIndex := uint(0)
if len(txs) != len(r) { if len(txs) != len(rs) {
return errors.New("transaction and receipt count mismatch") return errors.New("transaction and receipt count mismatch")
} }
for i := 0; i < len(r); i++ { for i := 0; i < len(rs); i++ {
// The transaction type and hash can be retrieved from the transaction itself // The transaction type and hash can be retrieved from the transaction itself
r[i].Type = txs[i].Type() rs[i].Type = txs[i].Type()
r[i].TxHash = txs[i].Hash() rs[i].TxHash = txs[i].Hash()
// block location fields // block location fields
r[i].BlockHash = hash rs[i].BlockHash = hash
r[i].BlockNumber = new(big.Int).SetUint64(number) rs[i].BlockNumber = new(big.Int).SetUint64(number)
r[i].TransactionIndex = uint(i) rs[i].TransactionIndex = uint(i)
// The contract address can be derived from the transaction itself // The contract address can be derived from the transaction itself
if txs[i].To() == nil { if txs[i].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed // Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, txs[i]) from, _ := Sender(signer, txs[i])
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
} }
// The used gas can be calculated based on previous r // The used gas can be calculated based on previous r
if i == 0 { if i == 0 {
r[i].GasUsed = r[i].CumulativeGasUsed rs[i].GasUsed = rs[i].CumulativeGasUsed
} else { } else {
r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
} }
// The derived log fields can simply be set from the block and transaction // The derived log fields can simply be set from the block and transaction
for j := 0; j < len(r[i].Logs); j++ { for j := 0; j < len(rs[i].Logs); j++ {
r[i].Logs[j].BlockNumber = number rs[i].Logs[j].BlockNumber = number
r[i].Logs[j].BlockHash = hash rs[i].Logs[j].BlockHash = hash
r[i].Logs[j].TxHash = r[i].TxHash rs[i].Logs[j].TxHash = rs[i].TxHash
r[i].Logs[j].TxIndex = uint(i) rs[i].Logs[j].TxIndex = uint(i)
r[i].Logs[j].Index = logIndex rs[i].Logs[j].Index = logIndex
logIndex++ logIndex++
} }
} }

View File

@ -29,6 +29,59 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
var (
legacyReceipt = &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
}
accessListReceipt = &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
Type: AccessListTxType,
}
eip1559Receipt = &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
Type: DynamicFeeTxType,
}
)
func TestDecodeEmptyTypedReceipt(t *testing.T) { func TestDecodeEmptyTypedReceipt(t *testing.T) {
input := []byte{0x80} input := []byte{0x80}
var r Receipt var r Receipt
@ -312,6 +365,105 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
} }
} }
func TestReceiptMarshalBinary(t *testing.T) {
// Legacy Receipt
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
have, err := legacyReceipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
}
legacyReceipts := Receipts{legacyReceipt}
buf := new(bytes.Buffer)
legacyReceipts.EncodeIndex(0, buf)
haveEncodeIndex := buf.Bytes()
if !bytes.Equal(have, haveEncodeIndex) {
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
}
buf.Reset()
if err := legacyReceipt.EncodeRLP(buf); err != nil {
t.Fatalf("encode rlp error: %v", err)
}
haveRLPEncode := buf.Bytes()
if !bytes.Equal(have, haveRLPEncode) {
t.Errorf("BinaryMarshal and EncodeRLP mismatch for legacy tx, got %x want %x", have, haveRLPEncode)
}
legacyWant := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
if !bytes.Equal(have, legacyWant) {
t.Errorf("encoded RLP mismatch, got %x want %x", have, legacyWant)
}
// 2930 Receipt
buf.Reset()
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
have, err = accessListReceipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
}
accessListReceipts := Receipts{accessListReceipt}
accessListReceipts.EncodeIndex(0, buf)
haveEncodeIndex = buf.Bytes()
if !bytes.Equal(have, haveEncodeIndex) {
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
}
accessListWant := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
if !bytes.Equal(have, accessListWant) {
t.Errorf("encoded RLP mismatch, got %x want %x", have, accessListWant)
}
// 1559 Receipt
buf.Reset()
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
have, err = eip1559Receipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
}
eip1559Receipts := Receipts{eip1559Receipt}
eip1559Receipts.EncodeIndex(0, buf)
haveEncodeIndex = buf.Bytes()
if !bytes.Equal(have, haveEncodeIndex) {
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
}
eip1559Want := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
if !bytes.Equal(have, eip1559Want) {
t.Errorf("encoded RLP mismatch, got %x want %x", have, eip1559Want)
}
}
func TestReceiptUnmarshalBinary(t *testing.T) {
// Legacy Receipt
legacyBinary := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
gotLegacyReceipt := new(Receipt)
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
}
// 2930 Receipt
accessListBinary := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
gotAccessListReceipt := new(Receipt)
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
}
// 1559 Receipt
eip1559RctBinary := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
got1559Receipt := new(Receipt)
if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt)
}
}
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) { func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
t.Helper() t.Helper()

View File

@ -284,13 +284,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
// To returns the recipient address of the transaction. // To returns the recipient address of the transaction.
// For contract-creation transactions, To returns nil. // For contract-creation transactions, To returns nil.
func (tx *Transaction) To() *common.Address { func (tx *Transaction) To() *common.Address {
// Copy the pointed-to address. return copyAddressPtr(tx.inner.to())
ito := tx.inner.to()
if ito == nil {
return nil
}
cpy := *ito
return &cpy
} }
// Cost returns gas * gasPrice + value. // Cost returns gas * gasPrice + value.
@ -632,3 +626,12 @@ func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data } func (m Message) Data() []byte { return m.data }
func (m Message) AccessList() AccessList { return m.accessList } func (m Message) AccessList() AccessList { return m.accessList }
func (m Message) IsFake() bool { return m.isFake } func (m Message) IsFake() bool { return m.isFake }
// copyAddressPtr copies an address.
func copyAddressPtr(a *common.Address) *common.Address {
if a == nil {
return nil
}
cpy := *a
return &cpy
}

View File

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "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/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
@ -342,7 +343,7 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
} else { } else {
blockRlp = fmt.Sprintf("0x%x", rlpBytes) blockRlp = fmt.Sprintf("0x%x", rlpBytes)
} }
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
blockJSON = map[string]interface{}{"error": err.Error()} blockJSON = map[string]interface{}{"error": err.Error()}
} }
results = append(results, &BadBlockArgs{ results = append(results, &BadBlockArgs{
@ -545,3 +546,64 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
} }
return dirty, nil return dirty, nil
} }
// GetAccessibleState returns the first number where the node has accessible
// state on disk. Note this being the post-state of that block and the pre-state
// of the next block.
// The (from, to) parameters are the sequence of blocks to search, which can go
// either forwards or backwards
func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) {
db := api.eth.ChainDb()
var pivot uint64
if p := rawdb.ReadLastPivotNumber(db); p != nil {
pivot = *p
log.Info("Found fast-sync pivot marker", "number", pivot)
}
var resolveNum = func(num rpc.BlockNumber) (uint64, error) {
// We don't have state for pending (-2), so treat it as latest
if num.Int64() < 0 {
block := api.eth.blockchain.CurrentBlock()
if block == nil {
return 0, fmt.Errorf("current block missing")
}
return block.NumberU64(), nil
}
return uint64(num.Int64()), nil
}
var (
start uint64
end uint64
delta = int64(1)
lastLog time.Time
err error
)
if start, err = resolveNum(from); err != nil {
return 0, err
}
if end, err = resolveNum(to); err != nil {
return 0, err
}
if start == end {
return 0, fmt.Errorf("from and to needs to be different")
}
if start > end {
delta = -1
}
for i := int64(start); i != int64(end); i += delta {
if time.Since(lastLog) > 8*time.Second {
log.Info("Finding roots", "from", start, "to", end, "at", i)
lastLog = time.Now()
}
if i < int64(pivot) {
continue
}
h := api.eth.BlockChain().GetHeaderByNumber(uint64(i))
if h == nil {
return 0, fmt.Errorf("missing header %d", i)
}
if ok, _ := api.eth.ChainDb().Has(h.Root[:]); ok {
return uint64(i), nil
}
}
return 0, fmt.Errorf("No state found")
}

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"errors" "errors"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -194,7 +195,10 @@ func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*typ
} }
func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(hash) if header := b.eth.blockchain.GetHeaderByHash(hash); header != nil {
return b.eth.blockchain.GetTd(hash, header.Number.Uint64())
}
return nil
} }
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
@ -236,10 +240,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
} }
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
pending, err := b.eth.txPool.Pending(false) pending := b.eth.txPool.Pending(false)
if err != nil {
return nil, err
}
var txs types.Transactions var txs types.Transactions
for _, batch := range pending { for _, batch := range pending {
txs = append(txs, batch...) txs = append(txs, batch...)
@ -316,6 +317,10 @@ func (b *EthAPIBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap return b.eth.config.RPCGasCap
} }
func (b *EthAPIBackend) RPCEVMTimeout() time.Duration {
return b.eth.config.RPCEVMTimeout
}
func (b *EthAPIBackend) RPCTxFeeCap() float64 { func (b *EthAPIBackend) RPCTxFeeCap() float64 {
return b.eth.config.RPCTxFeeCap return b.eth.config.RPCTxFeeCap
} }

View File

@ -555,7 +555,7 @@ func (s *Ethereum) Stop() error {
s.bloomIndexer.Close() s.bloomIndexer.Close()
close(s.closeBloomHandler) close(s.closeBloomHandler)
s.txPool.Stop() s.txPool.Stop()
s.miner.Stop() s.miner.Close()
s.blockchain.Stop() s.blockchain.Stop()
s.engine.Close() s.engine.Close()
rawdb.PopUncleanShutdownMarker(s.chainDb) rawdb.PopUncleanShutdownMarker(s.chainDb)

View File

@ -39,10 +39,8 @@ import (
// Register adds catalyst APIs to the node. // Register adds catalyst APIs to the node.
func Register(stack *node.Node, backend *eth.Ethereum) error { func Register(stack *node.Node, backend *eth.Ethereum) error {
chainconfig := backend.BlockChain().Config() chainconfig := backend.BlockChain().Config()
if chainconfig.CatalystBlock == nil { if chainconfig.TerminalTotalDifficulty == nil {
return errors.New("catalystBlock is not set in genesis config") return errors.New("catalyst started without valid total difficulty")
} else if chainconfig.CatalystBlock.Sign() != 0 {
return errors.New("catalystBlock of genesis config must be zero")
} }
log.Warn("Catalyst mode enabled") log.Warn("Catalyst mode enabled")
@ -128,10 +126,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD
time.Sleep(wait) time.Sleep(wait)
} }
pending, err := pool.Pending(true) pending := pool.Pending(true)
if err != nil {
return nil, err
}
coinbase, err := api.eth.Etherbase() coinbase, err := api.eth.Etherbase()
if err != nil { if err != nil {

View File

@ -62,26 +62,28 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
return genesis, blocks return genesis, blocks
} }
// TODO (MariusVanDerWijden) reenable once engine api is updated to the latest spec
/*
func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, []*types.Block) { func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, []*types.Block) {
if fork >= n { if fork >= n {
fork = n - 1 fork = n - 1
} }
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
config := &params.ChainConfig{ config := &params.ChainConfig{
ChainID: big.NewInt(1337), ChainID: big.NewInt(1337),
HomesteadBlock: big.NewInt(0), HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0), EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0), EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0), EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0), ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0), BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0), LondonBlock: big.NewInt(0),
CatalystBlock: big.NewInt(0), TerminalTotalDifficulty: big.NewInt(0),
Ethash: new(params.EthashConfig), Ethash: new(params.EthashConfig),
} }
genesis := &core.Genesis{ genesis := &core.Genesis{
Config: config, Config: config,
@ -105,6 +107,7 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block,
forkedBlocks, _ := core.GenerateChain(config, blocks[fork], engine, db, n-fork, generateFork) forkedBlocks, _ := core.GenerateChain(config, blocks[fork], engine, db, n-fork, generateFork)
return genesis, blocks, forkedBlocks return genesis, blocks, forkedBlocks
} }
*/
func TestEth2AssembleBlock(t *testing.T) { func TestEth2AssembleBlock(t *testing.T) {
genesis, blocks := generateTestChain() genesis, blocks := generateTestChain()
@ -156,6 +159,8 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
} }
} }
// TODO (MariusVanDerWijden) reenable once engine api is updated to the latest spec
/*
func TestEth2NewBlock(t *testing.T) { func TestEth2NewBlock(t *testing.T) {
genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4) genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4)
n, ethservice := startEthService(t, genesis, blocks[1:5]) n, ethservice := startEthService(t, genesis, blocks[1:5])
@ -216,6 +221,7 @@ func TestEth2NewBlock(t *testing.T) {
t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash()) t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash())
} }
} }
*/
// startEthService creates a full node instance for testing. // startEthService creates a full node instance for testing.
func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {

View File

@ -87,10 +87,11 @@ var Defaults = Config{
GasPrice: big.NewInt(params.GWei), GasPrice: big.NewInt(params.GWei),
Recommit: 3 * time.Second, Recommit: 3 * time.Second,
}, },
TxPool: core.DefaultTxPoolConfig, TxPool: core.DefaultTxPoolConfig,
RPCGasCap: 50000000, RPCGasCap: 50000000,
GPO: FullNodeGPO, RPCEVMTimeout: 5 * time.Second,
RPCTxFeeCap: 1, // 1 ether GPO: FullNodeGPO,
RPCTxFeeCap: 1, // 1 ether
} }
func init() { func init() {
@ -188,6 +189,9 @@ type Config struct {
// RPCGasCap is the global gas cap for eth-call variants. // RPCGasCap is the global gas cap for eth-call variants.
RPCGasCap uint64 RPCGasCap uint64
// RPCEVMTimeout is the global timeout for eth-call.
RPCEVMTimeout time.Duration
// RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for
// send-transction variants. The unit is ether. // send-transction variants. The unit is ether.
RPCTxFeeCap float64 RPCTxFeeCap float64

View File

@ -55,6 +55,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
EnablePreimageRecording bool EnablePreimageRecording bool
DocRoot string `toml:"-"` DocRoot string `toml:"-"`
RPCGasCap uint64 RPCGasCap uint64
RPCEVMTimeout time.Duration
RPCTxFeeCap float64 RPCTxFeeCap float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
@ -98,6 +99,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.EnablePreimageRecording = c.EnablePreimageRecording enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.DocRoot = c.DocRoot enc.DocRoot = c.DocRoot
enc.RPCGasCap = c.RPCGasCap enc.RPCGasCap = c.RPCGasCap
enc.RPCEVMTimeout = c.RPCEVMTimeout
enc.RPCTxFeeCap = c.RPCTxFeeCap enc.RPCTxFeeCap = c.RPCTxFeeCap
enc.Checkpoint = c.Checkpoint enc.Checkpoint = c.Checkpoint
enc.CheckpointOracle = c.CheckpointOracle enc.CheckpointOracle = c.CheckpointOracle
@ -145,6 +147,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
EnablePreimageRecording *bool EnablePreimageRecording *bool
DocRoot *string `toml:"-"` DocRoot *string `toml:"-"`
RPCGasCap *uint64 RPCGasCap *uint64
RPCEVMTimeout *time.Duration
RPCTxFeeCap *float64 RPCTxFeeCap *float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
@ -265,6 +268,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.RPCGasCap != nil { if dec.RPCGasCap != nil {
c.RPCGasCap = *dec.RPCGasCap c.RPCGasCap = *dec.RPCGasCap
} }
if dec.RPCEVMTimeout != nil {
c.RPCEVMTimeout = *dec.RPCEVMTimeout
}
if dec.RPCTxFeeCap != nil { if dec.RPCTxFeeCap != nil {
c.RPCTxFeeCap = *dec.RPCTxFeeCap c.RPCTxFeeCap = *dec.RPCTxFeeCap
} }

View File

@ -506,58 +506,80 @@ func TestPendingLogsSubscription(t *testing.T) {
}, },
} }
pendingBlockNumber = big.NewInt(rpc.PendingBlockNumber.Int64())
testCases = []struct { testCases = []struct {
crit ethereum.FilterQuery crit ethereum.FilterQuery
expected []*types.Log expected []*types.Log
c chan []*types.Log c chan []*types.Log
sub *Subscription sub *Subscription
err chan error
}{ }{
// match all // match all
{ {
ethereum.FilterQuery{}, flattenLogs(allLogs), ethereum.FilterQuery{FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
nil, nil, flattenLogs(allLogs),
nil, nil, nil,
}, },
// match none due to no matching addresses // match none due to no matching addresses
{ {
ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
nil, nil,
nil, nil, nil, nil, nil,
}, },
// match logs based on addresses, ignore topics // match logs based on addresses, ignore topics
{ {
ethereum.FilterQuery{Addresses: []common.Address{firstAddr}}, ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
append(flattenLogs(allLogs[:2]), allLogs[5][3]), append(flattenLogs(allLogs[:2]), allLogs[5][3]),
nil, nil, nil, nil, nil,
}, },
// match none due to no matching topics (match with address) // match none due to no matching topics (match with address)
{ {
ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
nil,
nil, nil, nil, nil, nil, nil,
}, },
// match logs based on addresses and topics // match logs based on addresses and topics
{ {
ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
append(flattenLogs(allLogs[3:5]), allLogs[5][0]), append(flattenLogs(allLogs[3:5]), allLogs[5][0]),
nil, nil, nil, nil, nil,
}, },
// match logs based on multiple addresses and "or" topics // match logs based on multiple addresses and "or" topics
{ {
ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
append(flattenLogs(allLogs[2:5]), allLogs[5][0]), append(flattenLogs(allLogs[2:5]), allLogs[5][0]),
nil, nil, nil, nil,
nil,
},
// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes
{
ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)},
append(flattenLogs(allLogs[:2]), allLogs[5][3]),
nil, nil,
}, },
// multiple pending logs, should match only 2 topics from the logs in block 5 // multiple pending logs, should match only 2 topics from the logs in block 5
{ {
ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}}, ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
[]*types.Log{allLogs[5][0], allLogs[5][2]}, []*types.Log{allLogs[5][0], allLogs[5][2]},
nil, nil, nil, nil, nil,
},
// match none due to only matching new mined logs
{
ethereum.FilterQuery{},
nil,
nil, nil, nil,
},
// match none due to only matching mined logs within a specific block range
{
ethereum.FilterQuery{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)},
nil,
nil, nil, nil,
},
// match all due to matching mined and pending logs
{
ethereum.FilterQuery{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())},
flattenLogs(allLogs),
nil, nil, nil,
},
// match none due to matching logs from a specific block number to new mined blocks
{
ethereum.FilterQuery{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
nil,
nil, nil, nil,
}, },
} }
) )
@ -567,43 +589,69 @@ func TestPendingLogsSubscription(t *testing.T) {
// (some) events are posted. // (some) events are posted.
for i := range testCases { for i := range testCases {
testCases[i].c = make(chan []*types.Log) testCases[i].c = make(chan []*types.Log)
testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c) testCases[i].err = make(chan error)
var err error
testCases[i].sub, err = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
if err != nil {
t.Fatalf("SubscribeLogs %d failed: %v\n", i, err)
}
} }
for n, test := range testCases { for n, test := range testCases {
i := n i := n
tt := test tt := test
go func() { go func() {
defer tt.sub.Unsubscribe()
var fetched []*types.Log var fetched []*types.Log
timeout := time.After(1 * time.Second)
fetchLoop: fetchLoop:
for { for {
logs := <-tt.c select {
fetched = append(fetched, logs...) case logs := <-tt.c:
if len(fetched) >= len(tt.expected) { // Do not break early if we've fetched greater, or equal,
// to the number of logs expected. This ensures we do not
// deadlock the filter system because it will do a blocking
// send on this channel if another log arrives.
fetched = append(fetched, logs...)
case <-timeout:
break fetchLoop break fetchLoop
} }
} }
if len(fetched) != len(tt.expected) { if len(fetched) != len(tt.expected) {
panic(fmt.Sprintf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))) tt.err <- fmt.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))
return
} }
for l := range fetched { for l := range fetched {
if fetched[l].Removed { if fetched[l].Removed {
panic(fmt.Sprintf("expected log not to be removed for log %d in case %d", l, i)) tt.err <- fmt.Errorf("expected log not to be removed for log %d in case %d", l, i)
return
} }
if !reflect.DeepEqual(fetched[l], tt.expected[l]) { if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
panic(fmt.Sprintf("invalid log on index %d for case %d", l, i)) tt.err <- fmt.Errorf("invalid log on index %d for case %d\n", l, i)
return
} }
} }
tt.err <- nil
}() }()
} }
// raise events // raise events
time.Sleep(1 * time.Second)
for _, ev := range allLogs { for _, ev := range allLogs {
backend.pendingLogsFeed.Send(ev) backend.pendingLogsFeed.Send(ev)
} }
for i := range testCases {
err := <-testCases[i].err
if err != nil {
t.Fatalf("test %d failed: %v", i, err)
}
<-testCases[i].sub.Err()
}
} }
// TestPendingTxFilterDeadlock tests if the event loop hangs when pending // TestPendingTxFilterDeadlock tests if the event loop hangs when pending

View File

@ -99,18 +99,14 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
var ( var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey) addr = crypto.PubkeyToAddress(key.PublicKey)
config = *params.TestChainConfig // needs copy because it is modified below
gspec = &core.Genesis{ gspec = &core.Genesis{
Config: params.TestChainConfig, Config: &config,
Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
} }
signer = types.LatestSigner(gspec.Config) signer = types.LatestSigner(gspec.Config)
) )
if londonBlock != nil { config.LondonBlock = londonBlock
gspec.Config.LondonBlock = londonBlock
signer = types.LatestSigner(gspec.Config)
} else {
gspec.Config.LondonBlock = nil
}
engine := ethash.NewFaker() engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
genesis, _ := gspec.Commit(db) genesis, _ := gspec.Commit(db)
@ -119,9 +115,9 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) { blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1}) b.SetCoinbase(common.Address{1})
var tx *types.Transaction var txdata types.TxData
if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 { if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 {
txdata := &types.DynamicFeeTx{ txdata = &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID, ChainID: gspec.Config.ChainID,
Nonce: b.TxNonce(addr), Nonce: b.TxNonce(addr),
To: &common.Address{}, To: &common.Address{},
@ -130,9 +126,8 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
GasTipCap: big.NewInt(int64(i+1) * params.GWei), GasTipCap: big.NewInt(int64(i+1) * params.GWei),
Data: []byte{}, Data: []byte{},
} }
tx = types.NewTx(txdata)
} else { } else {
txdata := &types.LegacyTx{ txdata = &types.LegacyTx{
Nonce: b.TxNonce(addr), Nonce: b.TxNonce(addr),
To: &common.Address{}, To: &common.Address{},
Gas: 21000, Gas: 21000,
@ -140,18 +135,13 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
Value: big.NewInt(100), Value: big.NewInt(100),
Data: []byte{}, Data: []byte{},
} }
tx = types.NewTx(txdata)
} }
tx, err := types.SignTx(tx, signer, key) b.AddTx(types.MustSignNewTx(key, signer, txdata))
if err != nil {
t.Fatalf("failed to create tx: %v", err)
}
b.AddTx(tx)
}) })
// Construct testing chain // Construct testing chain
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
gspec.Commit(diskdb) gspec.Commit(diskdb)
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, &config, engine, vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create local chain, %v", err) t.Fatalf("Failed to create local chain, %v", err)
} }

View File

@ -66,7 +66,7 @@ type txPool interface {
// Pending should return pending transactions. // Pending should return pending transactions.
// The slice should be modifiable by the caller. // The slice should be modifiable by the caller.
Pending(enforceTips bool) (map[common.Address]types.Transactions, error) Pending(enforceTips bool) map[common.Address]types.Transactions
// SubscribeNewTxsEvent should return an event subscription of // SubscribeNewTxsEvent should return an event subscription of
// NewTxsEvent and send events to the given channel. // NewTxsEvent and send events to the given channel.

View File

@ -486,7 +486,6 @@ func TestCheckpointChallenge(t *testing.T) {
} }
func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) { func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
t.Parallel()
// Reduce the checkpoint handshake challenge timeout // Reduce the checkpoint handshake challenge timeout
defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout) defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)

View File

@ -91,7 +91,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
} }
// Pending returns all the transactions known to the pool // Pending returns all the transactions known to the pool
func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) { func (p *testTxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
p.lock.RLock() p.lock.RLock()
defer p.lock.RUnlock() defer p.lock.RUnlock()
@ -103,7 +103,7 @@ func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transac
for _, batch := range batches { for _, batch := range batches {
sort.Sort(types.TxByNonce(batch)) sort.Sort(types.TxByNonce(batch))
} }
return batches, nil return batches
} }
// SubscribeNewTxsEvent should return an event subscription of NewTxsEvent and // SubscribeNewTxsEvent should return an event subscription of NewTxsEvent and

View File

@ -75,18 +75,18 @@ func (p *Peer) broadcastTransactions() {
if done == nil && len(queue) > 0 { if done == nil && len(queue) > 0 {
// Pile transaction until we reach our allowed network limit // Pile transaction until we reach our allowed network limit
var ( var (
hashes []common.Hash hashesCount uint64
txs []*types.Transaction txs []*types.Transaction
size common.StorageSize size common.StorageSize
) )
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 { if tx := p.txpool.Get(queue[i]); tx != nil {
txs = append(txs, tx) txs = append(txs, tx)
size += tx.Size() size += tx.Size()
} }
hashes = append(hashes, queue[i]) hashesCount++
} }
queue = queue[:copy(queue, queue[len(hashes):])] queue = queue[:copy(queue, queue[hashesCount:])]
// If there's anything available to transfer, fire up an async writer // If there's anything available to transfer, fire up an async writer
if len(txs) > 0 { if len(txs) > 0 {

View File

@ -126,6 +126,12 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
for i := range unknown { for i := range unknown {
unknown[i] = byte(i) unknown[i] = byte(i)
} }
getHashes := func(from, limit uint64) (hashes []common.Hash) {
for i := uint64(0); i < limit; i++ {
hashes = append(hashes, backend.chain.GetCanonicalHash(from-1-i))
}
return hashes
}
// Create a batch of tests for various scenarios // Create a batch of tests for various scenarios
limit := uint64(maxHeadersServe) limit := uint64(maxHeadersServe)
tests := []struct { tests := []struct {
@ -183,7 +189,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
// Ensure protocol limits are honored // Ensure protocol limits are honored
{ {
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
backend.chain.GetBlockHashesFromHash(backend.chain.CurrentBlock().Hash(), limit), getHashes(backend.chain.CurrentBlock().NumberU64(), limit),
}, },
// Check that requesting more than available is handled gracefully // Check that requesting more than available is handled gracefully
{ {
@ -379,7 +385,7 @@ func testGetNodeData(t *testing.T, protocol uint) {
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
signer := types.HomesteadSigner{} signer := types.HomesteadSigner{}
// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makers_test)
generator := func(i int, block *core.BlockGen) { generator := func(i int, block *core.BlockGen) {
switch i { switch i {
case 0: case 0:
@ -414,9 +420,8 @@ func testGetNodeData(t *testing.T, protocol uint) {
peer, _ := newTestPeer("peer", protocol, backend) peer, _ := newTestPeer("peer", protocol, backend)
defer peer.close() defer peer.close()
// Fetch for now the entire chain db // Collect all state tree hashes.
var hashes []common.Hash var hashes []common.Hash
it := backend.db.NewIterator(nil, nil) it := backend.db.NewIterator(nil, nil)
for it.Next() { for it.Next() {
if key := it.Key(); len(key) == common.HashLength { if key := it.Key(); len(key) == common.HashLength {
@ -425,6 +430,7 @@ func testGetNodeData(t *testing.T, protocol uint) {
} }
it.Release() it.Release()
// Request all hashes.
p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{ p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{
RequestId: 123, RequestId: 123,
GetNodeDataPacket: hashes, GetNodeDataPacket: hashes,
@ -436,38 +442,40 @@ func testGetNodeData(t *testing.T, protocol uint) {
if msg.Code != NodeDataMsg { if msg.Code != NodeDataMsg {
t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg) t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg)
} }
var ( var res NodeDataPacket66
data [][]byte
res NodeDataPacket66
)
if err := msg.Decode(&res); err != nil { if err := msg.Decode(&res); err != nil {
t.Fatalf("failed to decode response node data: %v", err) t.Fatalf("failed to decode response node data: %v", err)
} }
data = res.NodeDataPacket
// Verify that all hashes correspond to the requested data, and reconstruct a state tree // Verify that all hashes correspond to the requested data.
data := res.NodeDataPacket
for i, want := range hashes { for i, want := range hashes {
if hash := crypto.Keccak256Hash(data[i]); hash != want { if hash := crypto.Keccak256Hash(data[i]); hash != want {
t.Errorf("data hash mismatch: have %x, want %x", hash, want) t.Errorf("data hash mismatch: have %x, want %x", hash, want)
} }
} }
statedb := rawdb.NewMemoryDatabase()
// Reconstruct state tree from the received data.
reconstructDB := rawdb.NewMemoryDatabase()
for i := 0; i < len(data); i++ { for i := 0; i < len(data); i++ {
statedb.Put(hashes[i].Bytes(), data[i]) rawdb.WriteTrieNode(reconstructDB, hashes[i], data[i])
} }
// Sanity check whether all state matches.
accounts := []common.Address{testAddr, acc1Addr, acc2Addr} accounts := []common.Address{testAddr, acc1Addr, acc2Addr}
for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ { for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ {
trie, _ := state.New(backend.chain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb), nil) root := backend.chain.GetBlockByNumber(i).Root()
reconstructed, _ := state.New(root, state.NewDatabase(reconstructDB), nil)
for j, acc := range accounts { for j, acc := range accounts {
state, _ := backend.chain.State() state, _ := backend.chain.StateAt(root)
bw := state.GetBalance(acc) bw := state.GetBalance(acc)
bh := trie.GetBalance(acc) bh := reconstructed.GetBalance(acc)
if (bw != nil && bh == nil) || (bw == nil && bh != nil) { if (bw == nil) != (bh == nil) {
t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) t.Errorf("block %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
} }
if bw != nil && bh != nil && bw.Cmp(bw) != 0 { if bw != nil && bh != nil && bw.Cmp(bh) != 0 {
t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) t.Errorf("block %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
} }
} }
} }

View File

@ -43,7 +43,7 @@ func (h *handler) syncTransactions(p *eth.Peer) {
// //
// TODO(karalabe): Figure out if we could get away with random order somehow // TODO(karalabe): Figure out if we could get away with random order somehow
var txs types.Transactions var txs types.Transactions
pending, _ := h.txpool.Pending(false) pending := h.txpool.Pending(false)
for _, batch := range pending { for _, batch := range pending {
txs = append(txs, batch...) txs = append(txs, batch...)
} }
@ -182,7 +182,7 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
// If we're in fast sync mode, return that directly // If we're in fast sync mode, return that directly
if atomic.LoadUint32(&cs.handler.fastSync) == 1 { if atomic.LoadUint32(&cs.handler.fastSync) == 1 {
block := cs.handler.chain.CurrentFastBlock() block := cs.handler.chain.CurrentFastBlock()
td := cs.handler.chain.GetTdByHash(block.Hash()) td := cs.handler.chain.GetTd(block.Hash(), block.NumberU64())
return downloader.FastSync, td return downloader.FastSync, td
} }
// We are probably in full sync, but we might have rewound to before the // We are probably in full sync, but we might have rewound to before the
@ -190,7 +190,7 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil { if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil {
if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot { if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot {
block := cs.handler.chain.CurrentFastBlock() block := cs.handler.chain.CurrentFastBlock()
td := cs.handler.chain.GetTdByHash(block.Hash()) td := cs.handler.chain.GetTd(block.Hash(), block.NumberU64())
return downloader.FastSync, td return downloader.FastSync, td
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -18,77 +18,68 @@
// callFrameTracer uses the new call frame tracing methods to report useful information // callFrameTracer uses the new call frame tracing methods to report useful information
// about internal messages of a transaction. // about internal messages of a transaction.
{ {
callstack: [{}], callstack: [{}],
fault: function(log, db) { fault: function(log, db) {},
var len = this.callstack.length result: function(ctx, db) {
if (len > 1) { // Prepare outer message info
var call = this.callstack.pop() var result = {
if (this.callstack[len-1].calls === undefined) { type: ctx.type,
this.callstack[len-1].calls = [] from: toHex(ctx.from),
} to: toHex(ctx.to),
this.callstack[len-1].calls.push(call) value: '0x' + ctx.value.toString(16),
} gas: '0x' + bigInt(ctx.gas).toString(16),
}, gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
result: function(ctx, db) { input: toHex(ctx.input),
// Prepare outer message info output: toHex(ctx.output),
var result = { }
type: ctx.type, if (this.callstack[0].calls !== undefined) {
from: toHex(ctx.from), result.calls = this.callstack[0].calls
to: toHex(ctx.to), }
value: '0x' + ctx.value.toString(16), if (this.callstack[0].error !== undefined) {
gas: '0x' + bigInt(ctx.gas).toString(16), result.error = this.callstack[0].error
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16), } else if (ctx.error !== undefined) {
input: toHex(ctx.input), result.error = ctx.error
output: toHex(ctx.output), }
} if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
if (this.callstack[0].calls !== undefined) { delete result.output
result.calls = this.callstack[0].calls }
}
if (this.callstack[0].error !== undefined) {
result.error = this.callstack[0].error
} else if (ctx.error !== undefined) {
result.error = ctx.error
}
if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
delete result.output
}
return this.finalize(result) return this.finalize(result)
}, },
enter: function(frame) { enter: function(frame) {
var call = { var call = {
type: frame.getType(), type: frame.getType(),
from: toHex(frame.getFrom()), from: toHex(frame.getFrom()),
to: toHex(frame.getTo()), to: toHex(frame.getTo()),
input: toHex(frame.getInput()), input: toHex(frame.getInput()),
gas: '0x' + bigInt(frame.getGas()).toString('16'), gas: '0x' + bigInt(frame.getGas()).toString('16'),
} }
if (frame.getValue() !== undefined){ if (frame.getValue() !== undefined){
call.value='0x' + bigInt(frame.getValue()).toString(16) call.value='0x' + bigInt(frame.getValue()).toString(16)
} }
this.callstack.push(call) this.callstack.push(call)
}, },
exit: function(frameResult) { exit: function(frameResult) {
var len = this.callstack.length var len = this.callstack.length
if (len > 1) { if (len > 1) {
var call = this.callstack.pop() var call = this.callstack.pop()
call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16') call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16')
var error = frameResult.getError() var error = frameResult.getError()
if (error === undefined) { if (error === undefined) {
call.output = toHex(frameResult.getOutput()) call.output = toHex(frameResult.getOutput())
} else { } else {
call.error = error call.error = error
if (call.type === 'CREATE' || call.type === 'CREATE2') { if (call.type === 'CREATE' || call.type === 'CREATE2') {
delete call.to delete call.to
} }
} }
len -= 1 len -= 1
if (this.callstack[len-1].calls === undefined) { if (this.callstack[len-1].calls === undefined) {
this.callstack[len-1].calls = [] this.callstack[len-1].calls = []
} }
this.callstack[len-1].calls.push(call) this.callstack[len-1].calls.push(call)
} }
}, },
// finalize recreates a call object using the final desired field oder for json // finalize recreates a call object using the final desired field oder for json
// serialization. This is a nicety feature to pass meaningfully ordered results // serialization. This is a nicety feature to pass meaningfully ordered results
// to users who don't interpret it, just display it. // to users who don't interpret it, just display it.

View File

@ -795,9 +795,6 @@ func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) {
if !jst.traceCallFrames { if !jst.traceCallFrames {
return return
} }
if jst.err != nil {
return
}
// If tracing was interrupted, set the error and stop // If tracing was interrupted, set the error and stop
if atomic.LoadUint32(&jst.interrupt) > 0 { if atomic.LoadUint32(&jst.interrupt) > 0 {
jst.err = jst.reason jst.err = jst.reason

View File

@ -207,7 +207,7 @@ func TestNoStepExec(t *testing.T) {
} }
func TestIsPrecompile(t *testing.T) { func TestIsPrecompile(t *testing.T) {
chaincfg := &params.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), CatalystBlock: nil, Ethash: new(params.EthashConfig), Clique: nil} chaincfg := &params.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), TerminalTotalDifficulty: nil, Ethash: new(params.EthashConfig), Clique: nil}
chaincfg.ByzantiumBlock = big.NewInt(100) chaincfg.ByzantiumBlock = big.NewInt(100)
chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.IstanbulBlock = big.NewInt(200)
chaincfg.BerlinBlock = big.NewInt(300) chaincfg.BerlinBlock = big.NewInt(300)

7
go.mod
View File

@ -20,15 +20,13 @@ require (
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48
github.com/edsrzf/mmap-go v1.0.0 github.com/edsrzf/mmap-go v1.0.0
github.com/fatih/color v1.7.0 github.com/fatih/color v1.7.0
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
github.com/go-stack/stack v1.8.0 github.com/go-stack/stack v1.8.0
github.com/golang/protobuf v1.4.3 github.com/golang/protobuf v1.4.3
github.com/golang/snappy v0.0.4 github.com/golang/snappy v0.0.4
@ -47,7 +45,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/julienschmidt/httprouter v1.2.0 github.com/julienschmidt/httprouter v1.2.0
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-isatty v0.0.12
@ -75,6 +73,5 @@ require (
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/urfave/cli.v1 v1.20.0 gopkg.in/urfave/cli.v1 v1.20.0
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools v2.2.0+incompatible // indirect gotest.tools v2.2.0+incompatible // indirect
) )

27
go.sum
View File

@ -106,6 +106,7 @@ github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -121,12 +122,13 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ=
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
@ -159,8 +161,8 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@ -267,8 +269,8 @@ github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559 h1:0VWDXPNE0brOek1Q8bLfzKkvOzwbQE/snjGojlCr8CY=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
@ -279,11 +281,13 @@ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH6
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
@ -637,8 +641,9 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=

View File

@ -23,7 +23,6 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"strconv" "strconv"
"time"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -954,7 +953,7 @@ func (b *Block) Call(ctx context.Context, args struct {
return nil, err return nil, err
} }
} }
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, 5*time.Second, b.backend.RPCGasCap()) result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1024,7 +1023,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
Data ethapi.TransactionArgs Data ethapi.TransactionArgs
}) (*CallResult, error) { }) (*CallResult, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, 5*time.Second, p.backend.RPCGasCap()) result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -972,7 +972,7 @@ func (e *revertError) ErrorData() interface{} {
// Note, this function doesn't make and changes in the state/blockchain and is // Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values. // useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) { func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, 5*time.Second, s.b.RPCGasCap()) result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1206,7 +1206,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes. // transaction hashes.
func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) (map[string]interface{}, error) {
fields := RPCMarshalHeader(block.Header()) fields := RPCMarshalHeader(block.Header())
fields["size"] = hexutil.Uint64(block.Size()) fields["size"] = hexutil.Uint64(block.Size())
@ -1216,7 +1216,7 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]i
} }
if fullTx { if fullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) { formatTx = func(tx *types.Transaction) (interface{}, error) {
return newRPCTransactionFromBlockHash(block, tx.Hash()), nil return newRPCTransactionFromBlockHash(block, tx.Hash(), config), nil
} }
} }
txs := block.Transactions() txs := block.Transactions()
@ -1250,7 +1250,7 @@ func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *type
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires // rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires
// a `PublicBlockchainAPI`. // a `PublicBlockchainAPI`.
func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
fields, err := RPCMarshalBlock(b, inclTx, fullTx) fields, err := RPCMarshalBlock(b, inclTx, fullTx, s.b.ChainConfig())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1285,17 +1285,8 @@ type RPCTransaction struct {
// newRPCTransaction returns a transaction that will serialize to the RPC // newRPCTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available). // representation, with the given location metadata set (if available).
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction { func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction {
// Determine the signer. For replay-protected transactions, use the most permissive signer := types.MakeSigner(config, big.NewInt(0).SetUint64(blockNumber))
// signer, because we assume that signers are backwards-compatible with old
// transactions. For non-protected transactions, the homestead signer signer is used
// because the return value of ChainId is zero for those transactions.
var signer types.Signer
if tx.Protected() {
signer = types.LatestSignerForChainID(tx.ChainId())
} else {
signer = types.HomesteadSigner{}
}
from, _ := types.Sender(signer, tx) from, _ := types.Sender(signer, tx)
v, r, s := tx.RawSignatureValues() v, r, s := tx.RawSignatureValues()
result := &RPCTransaction{ result := &RPCTransaction{
@ -1346,16 +1337,16 @@ func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, conf
if current != nil { if current != nil {
baseFee = misc.CalcBaseFee(config, current) baseFee = misc.CalcBaseFee(config, current)
} }
return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee) return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee, config)
} }
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. // newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig) *RPCTransaction {
txs := b.Transactions() txs := b.Transactions()
if index >= uint64(len(txs)) { if index >= uint64(len(txs)) {
return nil return nil
} }
return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee()) return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee(), config)
} }
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
@ -1369,10 +1360,10 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.By
} }
// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. // newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction { func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash, config *params.ChainConfig) *RPCTransaction {
for idx, tx := range b.Transactions() { for idx, tx := range b.Transactions() {
if tx.Hash() == hash { if tx.Hash() == hash {
return newRPCTransactionFromBlockIndex(b, uint64(idx)) return newRPCTransactionFromBlockIndex(b, uint64(idx), config)
} }
} }
return nil return nil
@ -1513,7 +1504,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Co
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil {
return newRPCTransactionFromBlockIndex(block, uint64(index)) return newRPCTransactionFromBlockIndex(block, uint64(index), s.b.ChainConfig())
} }
return nil return nil
} }
@ -1521,7 +1512,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx conte
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction {
if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
return newRPCTransactionFromBlockIndex(block, uint64(index)) return newRPCTransactionFromBlockIndex(block, uint64(index), s.b.ChainConfig())
} }
return nil return nil
} }
@ -1573,7 +1564,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee, s.b.ChainConfig()), nil
} }
// No finalized transaction, try to retrieve it from the pool // No finalized transaction, try to retrieve it from the pool
if tx := s.b.GetPoolTransaction(hash); tx != nil { if tx := s.b.GetPoolTransaction(hash); tx != nil {
@ -1915,17 +1906,22 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI {
return &PublicDebugAPI{b: b} return &PublicDebugAPI{b: b}
} }
// GetHeaderRlp retrieves the RLP encoded for of a single header.
func (api *PublicDebugAPI) GetHeaderRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) {
header, _ := api.b.HeaderByNumber(ctx, rpc.BlockNumber(number))
if header == nil {
return nil, fmt.Errorf("header #%d not found", number)
}
return rlp.EncodeToBytes(header)
}
// GetBlockRlp retrieves the RLP encoded for of a single block. // GetBlockRlp retrieves the RLP encoded for of a single block.
func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number))
if block == nil { if block == nil {
return "", fmt.Errorf("block #%d not found", number) return nil, fmt.Errorf("block #%d not found", number)
} }
encoded, err := rlp.EncodeToBytes(block) return rlp.EncodeToBytes(block)
if err != nil {
return "", err
}
return fmt.Sprintf("%x", encoded), nil
} }
// TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the // TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the

View File

@ -20,6 +20,7 @@ package ethapi
import ( import (
"context" "context"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -47,9 +48,10 @@ type Backend interface {
ChainDb() ethdb.Database ChainDb() ethdb.Database
AccountManager() *accounts.Manager AccountManager() *accounts.Manager
ExtRPCEnabled() bool ExtRPCEnabled() bool
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
UnprotectedAllowed() bool // allows only for EIP155 transactions. RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
UnprotectedAllowed() bool // allows only for EIP155 transactions.
// Blockchain API // Blockchain API
SetHead(number uint64) SetHead(number uint64)

View File

@ -44,7 +44,7 @@ func getCompletions(vm *goja.Runtime, line string) (results []string) {
obj := vm.GlobalObject() obj := vm.GlobalObject()
for i := 0; i < len(parts)-1; i++ { for i := 0; i < len(parts)-1; i++ {
v := obj.Get(parts[i]) v := obj.Get(parts[i])
if v == nil { if v == nil || goja.IsNull(v) || goja.IsUndefined(v) {
return nil // No object was found return nil // No object was found
} }
obj = v.ToObject(vm) obj = v.ToObject(vm)

View File

@ -72,6 +72,7 @@ func TestCompleteKeywords(t *testing.T) {
{ {
input: "x.gazonk.", input: "x.gazonk.",
want: []string{ want: []string{
"x.gazonk.__proto__",
"x.gazonk.constructor", "x.gazonk.constructor",
"x.gazonk.hasOwnProperty", "x.gazonk.hasOwnProperty",
"x.gazonk.isPrototypeOf", "x.gazonk.isPrototypeOf",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13635,4 +13635,4 @@ if (typeof window !== 'undefined' && typeof window.Web3 === 'undefined') {
module.exports = Web3; module.exports = Web3;
},{"./lib/web3":22}]},{},["web3"]) },{"./lib/web3":22}]},{},["web3"])
//# sourceMappingURL=web3-light.js.map

64
internal/syncx/mutex.go Normal file
View File

@ -0,0 +1,64 @@
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package syncx contains exotic synchronization primitives.
package syncx
// ClosableMutex is a mutex that can also be closed.
// Once closed, it can never be taken again.
type ClosableMutex struct {
ch chan struct{}
}
func NewClosableMutex() *ClosableMutex {
ch := make(chan struct{}, 1)
ch <- struct{}{}
return &ClosableMutex{ch}
}
// TryLock attempts to lock cm.
// If the mutex is closed, TryLock returns false.
func (cm *ClosableMutex) TryLock() bool {
_, ok := <-cm.ch
return ok
}
// MustLock locks cm.
// If the mutex is closed, MustLock panics.
func (cm *ClosableMutex) MustLock() {
_, ok := <-cm.ch
if !ok {
panic("mutex closed")
}
}
// Unlock unlocks cm.
func (cm *ClosableMutex) Unlock() {
select {
case cm.ch <- struct{}{}:
default:
panic("Unlock of already-unlocked ClosableMutex")
}
}
// Close locks the mutex, then closes it.
func (cm *ClosableMutex) Close() {
_, ok := <-cm.ch
if !ok {
panic("Close of already-closed ClosableMutex")
}
close(cm.ch)
}

View File

@ -223,6 +223,11 @@ web3._extend({
params: 1, params: 1,
outputFormatter: console.log outputFormatter: console.log
}), }),
new web3._extend.Method({
name: 'getHeaderRlp',
call: 'debug_getHeaderRlp',
params: 1
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'getBlockRlp', name: 'getBlockRlp',
call: 'debug_getBlockRlp', call: 'debug_getBlockRlp',
@ -460,6 +465,12 @@ web3._extend({
call: 'debug_freezeClient', call: 'debug_freezeClient',
params: 1, params: 1,
}), }),
new web3._extend.Method({
name: 'getAccessibleState',
call: 'debug_getAccessibleState',
params: 2,
inputFormatter:[web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter],
}),
], ],
properties: [] properties: []
}); });

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"errors" "errors"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -293,6 +294,10 @@ func (b *LesApiBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap return b.eth.config.RPCGasCap
} }
func (b *LesApiBackend) RPCEVMTimeout() time.Duration {
return b.eth.config.RPCEVMTimeout
}
func (b *LesApiBackend) RPCTxFeeCap() float64 { func (b *LesApiBackend) RPCTxFeeCap() float64 {
return b.eth.config.RPCTxFeeCap return b.eth.config.RPCTxFeeCap
} }

View File

@ -143,8 +143,10 @@ func NewClientPool(balanceDb ethdb.KeyValueStore, minCap uint64, connectedBias t
if oldState.HasAll(cp.setup.activeFlag) && oldState.HasNone(cp.setup.activeFlag) { if oldState.HasAll(cp.setup.activeFlag) && oldState.HasNone(cp.setup.activeFlag) {
clientDeactivatedMeter.Mark(1) clientDeactivatedMeter.Mark(1)
} }
_, connected := cp.Active() activeCount, activeCap := cp.Active()
totalConnectedGauge.Update(int64(connected)) totalActiveCountGauge.Update(int64(activeCount))
totalActiveCapacityGauge.Update(int64(activeCap))
totalInactiveCountGauge.Update(int64(cp.Inactive()))
}) })
return cp return cp
} }

View File

@ -21,7 +21,9 @@ import (
) )
var ( var (
totalConnectedGauge = metrics.NewRegisteredGauge("vflux/server/totalConnected", nil) totalActiveCapacityGauge = metrics.NewRegisteredGauge("vflux/server/active/capacity", nil)
totalActiveCountGauge = metrics.NewRegisteredGauge("vflux/server/active/count", nil)
totalInactiveCountGauge = metrics.NewRegisteredGauge("vflux/server/inactive/count", nil)
clientConnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/connected", nil) clientConnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/connected", nil)
clientActivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/activated", nil) clientActivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/activated", nil)

View File

@ -128,7 +128,7 @@ func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock m
} else { } else {
ns.SetStateSub(node, nodestate.Flags{}, pp.setup.activeFlag.Or(pp.setup.inactiveFlag), 0) ns.SetStateSub(node, nodestate.Flags{}, pp.setup.activeFlag.Or(pp.setup.inactiveFlag), 0)
if n, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); n != nil { if n, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); n != nil {
pp.disconnectedNode(n) pp.disconnectNode(n)
} }
ns.SetFieldSub(node, pp.setup.capacityField, nil) ns.SetFieldSub(node, pp.setup.capacityField, nil)
ns.SetFieldSub(node, pp.setup.queueField, nil) ns.SetFieldSub(node, pp.setup.queueField, nil)
@ -137,10 +137,10 @@ func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock m
ns.SubscribeState(pp.setup.activeFlag.Or(pp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { ns.SubscribeState(pp.setup.activeFlag.Or(pp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) {
if c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); c != nil { if c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); c != nil {
if oldState.IsEmpty() { if oldState.IsEmpty() {
pp.connectedNode(c) pp.connectNode(c)
} }
if newState.IsEmpty() { if newState.IsEmpty() {
pp.disconnectedNode(c) pp.disconnectNode(c)
} }
} }
}) })
@ -233,6 +233,14 @@ func (pp *priorityPool) Active() (uint64, uint64) {
return pp.activeCount, pp.activeCap return pp.activeCount, pp.activeCap
} }
// Inactive returns the number of currently inactive nodes
func (pp *priorityPool) Inactive() int {
pp.lock.Lock()
defer pp.lock.Unlock()
return pp.inactiveQueue.Size()
}
// Limits returns the maximum allowed number and total capacity of active nodes // Limits returns the maximum allowed number and total capacity of active nodes
func (pp *priorityPool) Limits() (uint64, uint64) { func (pp *priorityPool) Limits() (uint64, uint64) {
pp.lock.Lock() pp.lock.Lock()
@ -285,9 +293,9 @@ func (pp *priorityPool) inactivePriority(p *ppNodeInfo) int64 {
return p.nodePriority.priority(pp.minCap) return p.nodePriority.priority(pp.minCap)
} }
// connectedNode is called when a new node has been added to the pool (inactiveFlag set) // connectNode is called when a new node has been added to the pool (inactiveFlag set)
// Note: this function should run inside a NodeStateMachine operation // Note: this function should run inside a NodeStateMachine operation
func (pp *priorityPool) connectedNode(c *ppNodeInfo) { func (pp *priorityPool) connectNode(c *ppNodeInfo) {
pp.lock.Lock() pp.lock.Lock()
pp.activeQueue.Refresh() pp.activeQueue.Refresh()
if c.connected { if c.connected {
@ -301,10 +309,10 @@ func (pp *priorityPool) connectedNode(c *ppNodeInfo) {
pp.updateFlags(updates) pp.updateFlags(updates)
} }
// disconnectedNode is called when a node has been removed from the pool (both inactiveFlag // disconnectNode is called when a node has been removed from the pool (both inactiveFlag
// and activeFlag reset) // and activeFlag reset)
// Note: this function should run inside a NodeStateMachine operation // Note: this function should run inside a NodeStateMachine operation
func (pp *priorityPool) disconnectedNode(c *ppNodeInfo) { func (pp *priorityPool) disconnectNode(c *ppNodeInfo) {
pp.lock.Lock() pp.lock.Lock()
pp.activeQueue.Refresh() pp.activeQueue.Refresh()
if !c.connected { if !c.connected {

View File

@ -430,12 +430,6 @@ func (lc *LightChain) GetTd(hash common.Hash, number uint64) *big.Int {
return lc.hc.GetTd(hash, number) return lc.hc.GetTd(hash, number)
} }
// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
func (lc *LightChain) GetTdByHash(hash common.Hash) *big.Int {
return lc.hc.GetTdByHash(hash)
}
// GetHeaderByNumberOdr retrieves the total difficult from the database or // GetHeaderByNumberOdr retrieves the total difficult from the database or
// network by hash and number, caching it (associated with its hash) if found. // network by hash and number, caching it (associated with its hash) if found.
func (lc *LightChain) GetTdOdr(ctx context.Context, hash common.Hash, number uint64) *big.Int { func (lc *LightChain) GetTdOdr(ctx context.Context, hash common.Hash, number uint64) *big.Int {
@ -470,12 +464,6 @@ func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash {
return bc.hc.GetCanonicalHash(number) return bc.hc.GetCanonicalHash(number)
} }
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
return lc.hc.GetBlockHashesFromHash(hash, max)
}
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or // GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the // a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
// number of blocks to be individually checked before we reach the canonical chain. // number of blocks to be individually checked before we reach the canonical chain.

View File

@ -104,12 +104,13 @@ func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td
} }
// Sanity check that the forked chain can be imported into the original // Sanity check that the forked chain can be imported into the original
var tdPre, tdPost *big.Int var tdPre, tdPost *big.Int
cur := LightChain.CurrentHeader()
tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash()) tdPre = LightChain.GetTd(cur.Hash(), cur.Number.Uint64())
if err := testHeaderChainImport(headerChainB, LightChain); err != nil { if err := testHeaderChainImport(headerChainB, LightChain); err != nil {
t.Fatalf("failed to import forked header chain: %v", err) t.Fatalf("failed to import forked header chain: %v", err)
} }
tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) last := headerChainB[len(headerChainB)-1]
tdPost = LightChain.GetTd(last.Hash(), last.Number.Uint64())
// Compare the total difficulties of the chains // Compare the total difficulties of the chains
comparator(tdPre, tdPost) comparator(tdPre, tdPost)
} }
@ -124,7 +125,8 @@ func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error
} }
// Manually insert the header into the database, but don't reorganize (allows subsequent testing) // Manually insert the header into the database, but don't reorganize (allows subsequent testing)
lightchain.chainmu.Lock() lightchain.chainmu.Lock()
rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(),
new(big.Int).Add(header.Difficulty, lightchain.GetTd(header.ParentHash, header.Number.Uint64()-1)))
rawdb.WriteHeader(lightchain.chainDb, header) rawdb.WriteHeader(lightchain.chainDb, header)
lightchain.chainmu.Unlock() lightchain.chainmu.Unlock()
} }
@ -309,7 +311,7 @@ func testReorg(t *testing.T, first, second []int, td int64) {
} }
// Make sure the chain total difficulty is the correct one // Make sure the chain total difficulty is the correct one
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { if have := bc.GetTd(bc.CurrentHeader().Hash(), bc.CurrentHeader().Number.Uint64()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want) t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
} }
} }

View File

@ -20,6 +20,7 @@ package miner
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"sync"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -63,6 +64,8 @@ type Miner struct {
exitCh chan struct{} exitCh chan struct{}
startCh chan common.Address startCh chan common.Address
stopCh chan struct{} stopCh chan struct{}
wg sync.WaitGroup
} }
func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner { func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner {
@ -75,8 +78,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
stopCh: make(chan struct{}), stopCh: make(chan struct{}),
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true), worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true),
} }
miner.wg.Add(1)
go miner.update() go miner.update()
return miner return miner
} }
@ -85,6 +88,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
// 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
// and halt your mining operation for as long as the DOS continues. // and halt your mining operation for as long as the DOS continues.
func (miner *Miner) update() { func (miner *Miner) update() {
defer miner.wg.Done()
events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
defer func() { defer func() {
if !events.Closed() { if !events.Closed() {
@ -154,6 +159,7 @@ func (miner *Miner) Stop() {
func (miner *Miner) Close() { func (miner *Miner) Close() {
close(miner.exitCh) close(miner.exitCh)
miner.wg.Wait()
} }
func (miner *Miner) Mining() bool { func (miner *Miner) Mining() bool {

View File

@ -23,10 +23,9 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"os" "os"
"path/filepath" "os/signal"
"time" "time"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
@ -58,12 +57,17 @@ func main() {
faucets[i], _ = crypto.GenerateKey() faucets[i], _ = crypto.GenerateKey()
} }
// Pre-generate the ethash mining DAG so we don't race // Pre-generate the ethash mining DAG so we don't race
ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash")) ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir)
// Create an Ethash network based off of the Ropsten config // Create an Ethash network based off of the Ropsten config
genesis := makeGenesis(faucets) genesis := makeGenesis(faucets)
// Handle interrupts.
interruptCh := make(chan os.Signal, 5)
signal.Notify(interruptCh, os.Interrupt)
var ( var (
stacks []*node.Node
nodes []*eth.Ethereum nodes []*eth.Ethereum
enodes []*enode.Node enodes []*enode.Node
) )
@ -85,12 +89,6 @@ func main() {
// Start tracking the node and its enode // Start tracking the node and its enode
nodes = append(nodes, ethBackend) nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self()) enodes = append(enodes, stack.Server().Self())
// Inject the signer key and start sealing with it
store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
if _, err := store.NewAccount(""); err != nil {
panic(err)
}
} }
// Iterate over all the nodes and start mining // Iterate over all the nodes and start mining
@ -111,6 +109,16 @@ func main() {
signer = types.LatestSignerForChainID(genesis.Config.ChainID) signer = types.LatestSignerForChainID(genesis.Config.ChainID)
) )
for { for {
// Stop when interrupted.
select {
case <-interruptCh:
for _, node := range stacks {
node.Close()
}
return
default:
}
// Pick a random mining node // Pick a random mining node
index := rand.Intn(len(faucets)) index := rand.Intn(len(faucets))
backend := nodes[index%len(nodes)] backend := nodes[index%len(nodes)]
@ -242,9 +250,10 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
GPO: ethconfig.Defaults.GPO, GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash, Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{ Miner: miner.Config{
GasCeil: genesis.GasLimit * 11 / 10, Etherbase: common.Address{1},
GasPrice: big.NewInt(1), GasCeil: genesis.GasLimit * 11 / 10,
Recommit: time.Second, GasPrice: big.NewInt(1),
Recommit: time.Second,
}, },
}) })
if err != nil { if err != nil {

View File

@ -24,6 +24,7 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"os" "os"
"os/signal"
"time" "time"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
@ -59,11 +60,15 @@ func main() {
// Create a Clique network based off of the Rinkeby config // Create a Clique network based off of the Rinkeby config
genesis := makeGenesis(faucets, sealers) genesis := makeGenesis(faucets, sealers)
// Handle interrupts.
interruptCh := make(chan os.Signal, 5)
signal.Notify(interruptCh, os.Interrupt)
var ( var (
stacks []*node.Node
nodes []*eth.Ethereum nodes []*eth.Ethereum
enodes []*enode.Node enodes []*enode.Node
) )
for _, sealer := range sealers { for _, sealer := range sealers {
// Start the node and wait until it's up // Start the node and wait until it's up
stack, ethBackend, err := makeSealer(genesis) stack, ethBackend, err := makeSealer(genesis)
@ -80,18 +85,20 @@ func main() {
stack.Server().AddPeer(n) stack.Server().AddPeer(n)
} }
// Start tracking the node and its enode // Start tracking the node and its enode
stacks = append(stacks, stack)
nodes = append(nodes, ethBackend) nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self()) enodes = append(enodes, stack.Server().Self())
// Inject the signer key and start sealing with it // Inject the signer key and start sealing with it
store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) ks := keystore.NewKeyStore(stack.KeyStoreDir(), keystore.LightScryptN, keystore.LightScryptP)
signer, err := store.ImportECDSA(sealer, "") signer, err := ks.ImportECDSA(sealer, "")
if err != nil { if err != nil {
panic(err) panic(err)
} }
if err := store.Unlock(signer, ""); err != nil { if err := ks.Unlock(signer, ""); err != nil {
panic(err) panic(err)
} }
stack.AccountManager().AddBackend(ks)
} }
// Iterate over all the nodes and start signing on them // Iterate over all the nodes and start signing on them
@ -106,6 +113,16 @@ func main() {
// Start injecting transactions from the faucet like crazy // Start injecting transactions from the faucet like crazy
nonces := make([]uint64, len(faucets)) nonces := make([]uint64, len(faucets))
for { for {
// Stop when interrupted.
select {
case <-interruptCh:
for _, node := range stacks {
node.Close()
}
return
default:
}
// Pick a random signer node // Pick a random signer node
index := rand.Intn(len(faucets)) index := rand.Intn(len(faucets))
backend := nodes[index%len(nodes)] backend := nodes[index%len(nodes)]

View File

@ -23,10 +23,9 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"os" "os"
"path/filepath" "os/signal"
"time" "time"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
@ -54,12 +53,17 @@ func main() {
faucets[i], _ = crypto.GenerateKey() faucets[i], _ = crypto.GenerateKey()
} }
// Pre-generate the ethash mining DAG so we don't race // Pre-generate the ethash mining DAG so we don't race
ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash")) ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir)
// Create an Ethash network based off of the Ropsten config // Create an Ethash network based off of the Ropsten config
genesis := makeGenesis(faucets) genesis := makeGenesis(faucets)
// Handle interrupts.
interruptCh := make(chan os.Signal, 5)
signal.Notify(interruptCh, os.Interrupt)
var ( var (
stacks []*node.Node
nodes []*eth.Ethereum nodes []*eth.Ethereum
enodes []*enode.Node enodes []*enode.Node
) )
@ -79,14 +83,9 @@ func main() {
stack.Server().AddPeer(n) stack.Server().AddPeer(n)
} }
// Start tracking the node and its enode // Start tracking the node and its enode
stacks = append(stacks, stack)
nodes = append(nodes, ethBackend) nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self()) enodes = append(enodes, stack.Server().Self())
// Inject the signer key and start sealing with it
store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
if _, err := store.NewAccount(""); err != nil {
panic(err)
}
} }
// Iterate over all the nodes and start mining // Iterate over all the nodes and start mining
@ -101,6 +100,16 @@ func main() {
// Start injecting transactions from the faucets like crazy // Start injecting transactions from the faucets like crazy
nonces := make([]uint64, len(faucets)) nonces := make([]uint64, len(faucets))
for { for {
// Stop when interrupted.
select {
case <-interruptCh:
for _, node := range stacks {
node.Close()
}
return
default:
}
// Pick a random mining node // Pick a random mining node
index := rand.Intn(len(faucets)) index := rand.Intn(len(faucets))
backend := nodes[index%len(nodes)] backend := nodes[index%len(nodes)]
@ -171,9 +180,10 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
GPO: ethconfig.Defaults.GPO, GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash, Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{ Miner: miner.Config{
GasCeil: genesis.GasLimit * 11 / 10, Etherbase: common.Address{1},
GasPrice: big.NewInt(1), GasCeil: genesis.GasLimit * 11 / 10,
Recommit: time.Second, GasPrice: big.NewInt(1),
Recommit: time.Second,
}, },
}) })
if err != nil { if err != nil {

View File

@ -150,6 +150,8 @@ type worker struct {
resubmitIntervalCh chan time.Duration resubmitIntervalCh chan time.Duration
resubmitAdjustCh chan *intervalAdjust resubmitAdjustCh chan *intervalAdjust
wg sync.WaitGroup
current *environment // An environment for current running cycle. current *environment // An environment for current running cycle.
localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks.
remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks.
@ -225,6 +227,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
recommit = minRecommitInterval recommit = minRecommitInterval
} }
worker.wg.Add(4)
go worker.mainLoop() go worker.mainLoop()
go worker.newWorkLoop(recommit) go worker.newWorkLoop(recommit)
go worker.resultLoop() go worker.resultLoop()
@ -318,11 +321,9 @@ func (w *worker) isRunning() bool {
// close terminates all background threads maintained by the worker. // close terminates all background threads maintained by the worker.
// Note the worker does not support being closed multiple times. // Note the worker does not support being closed multiple times.
func (w *worker) close() { func (w *worker) close() {
if w.current != nil && w.current.state != nil {
w.current.state.StopPrefetcher()
}
atomic.StoreInt32(&w.running, 0) atomic.StoreInt32(&w.running, 0)
close(w.exitCh) close(w.exitCh)
w.wg.Wait()
} }
// recalcRecommit recalculates the resubmitting interval upon feedback. // recalcRecommit recalculates the resubmitting interval upon feedback.
@ -349,6 +350,7 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
// newWorkLoop is a standalone goroutine to submit new mining work upon received events. // newWorkLoop is a standalone goroutine to submit new mining work upon received events.
func (w *worker) newWorkLoop(recommit time.Duration) { func (w *worker) newWorkLoop(recommit time.Duration) {
defer w.wg.Done()
var ( var (
interrupt *int32 interrupt *int32
minRecommit = recommit // minimal resubmit interval specified by user. minRecommit = recommit // minimal resubmit interval specified by user.
@ -446,9 +448,15 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
// mainLoop is a standalone goroutine to regenerate the sealing task based on the received event. // mainLoop is a standalone goroutine to regenerate the sealing task based on the received event.
func (w *worker) mainLoop() { func (w *worker) mainLoop() {
defer w.wg.Done()
defer w.txsSub.Unsubscribe() defer w.txsSub.Unsubscribe()
defer w.chainHeadSub.Unsubscribe() defer w.chainHeadSub.Unsubscribe()
defer w.chainSideSub.Unsubscribe() defer w.chainSideSub.Unsubscribe()
defer func() {
if w.current != nil && w.current.state != nil {
w.current.state.StopPrefetcher()
}
}()
for { for {
select { select {
@ -548,6 +556,7 @@ func (w *worker) mainLoop() {
// taskLoop is a standalone goroutine to fetch sealing task from the generator and // taskLoop is a standalone goroutine to fetch sealing task from the generator and
// push them to consensus engine. // push them to consensus engine.
func (w *worker) taskLoop() { func (w *worker) taskLoop() {
defer w.wg.Done()
var ( var (
stopCh chan struct{} stopCh chan struct{}
prev common.Hash prev common.Hash
@ -595,6 +604,7 @@ func (w *worker) taskLoop() {
// resultLoop is a standalone goroutine to handle sealing result submitting // resultLoop is a standalone goroutine to handle sealing result submitting
// and flush relative data to the database. // and flush relative data to the database.
func (w *worker) resultLoop() { func (w *worker) resultLoop() {
defer w.wg.Done()
for { for {
select { select {
case block := <-w.resultCh: case block := <-w.resultCh:
@ -977,11 +987,7 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
} }
// Fill the block with all available pending transactions. // Fill the block with all available pending transactions.
pending, err := w.eth.TxPool().Pending(true) pending := w.eth.TxPool().Pending(true)
if err != nil {
log.Error("Failed to fetch pending transactions", "err", err)
return
}
// Short circuit if there is no available pending transactions. // Short circuit if there is no available pending transactions.
// But if we disable empty precommit already, ignore it. Since // But if we disable empty precommit already, ignore it. Since
// empty block is necessary to keep the liveness of the network. // empty block is necessary to keep the liveness of the network.

View File

@ -218,7 +218,7 @@ func (api *privateAdminAPI) StartHTTP(host *string, port *int, cors *string, api
} }
// StartRPC starts the HTTP RPC API server. // StartRPC starts the HTTP RPC API server.
// This method is deprecated. Use StartHTTP instead. // Deprecated: use StartHTTP instead.
func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
log.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP") log.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP")
return api.StartHTTP(host, port, cors, apis, vhosts) return api.StartHTTP(host, port, cors, apis, vhosts)
@ -231,7 +231,7 @@ func (api *privateAdminAPI) StopHTTP() (bool, error) {
} }
// StopRPC shuts down the HTTP server. // StopRPC shuts down the HTTP server.
// This method is deprecated. Use StopHTTP instead. // Deprecated: use StopHTTP instead.
func (api *privateAdminAPI) StopRPC() (bool, error) { func (api *privateAdminAPI) StopRPC() (bool, error) {
log.Warn("Deprecation warning", "method", "admin.StopRPC", "use-instead", "admin.StopHTTP") log.Warn("Deprecation warning", "method", "admin.StopRPC", "use-instead", "admin.StopHTTP")
return api.StopHTTP() return api.StopHTTP()

View File

@ -314,7 +314,9 @@ type ChainConfig struct {
BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin)
LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london)
CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst) // TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade.
TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"`
// Various consensus engines // Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"` Ethash *EthashConfig `json:"ethash,omitempty"`
@ -432,9 +434,12 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool {
return isForked(c.LondonBlock, num) return isForked(c.LondonBlock, num)
} }
// IsCatalyst returns whether num is either equal to the Merge fork block or greater. // IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
func (c *ChainConfig) IsCatalyst(num *big.Int) bool { func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool {
return isForked(c.CatalystBlock, num) if c.TerminalTotalDifficulty == nil {
return false
}
return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0
} }
// CheckCompatible checks whether scheduled fork transitions have been imported // CheckCompatible checks whether scheduled fork transitions have been imported
@ -613,7 +618,7 @@ type Rules struct {
ChainID *big.Int ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon, IsCatalyst bool IsBerlin, IsLondon bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -634,6 +639,5 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
IsIstanbul: c.IsIstanbul(num), IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num), IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num), IsLondon: c.IsLondon(num),
IsCatalyst: c.IsCatalyst(num),
} }
} }

View File

@ -23,7 +23,7 @@ import (
const ( const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 10 // Minor version component of the current release VersionMinor = 10 // Minor version component of the current release
VersionPatch = 9 // Patch version component of the current release VersionPatch = 10 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string VersionMeta = "stable" // Version metadata to append to the version string
) )

View File

@ -29,6 +29,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -100,7 +101,8 @@ func (t *Type) isReferenceType() bool {
return false return false
} }
// Reference types must have a leading uppercase character // Reference types must have a leading uppercase character
return unicode.IsUpper([]rune(t.Type)[0]) r, _ := utf8.DecodeRuneInString(t.Type)
return unicode.IsUpper(r)
} }
type Types map[string][]Type type Types map[string][]Type

View File

@ -21,7 +21,7 @@ import (
"regexp" "regexp"
) )
var printable7BitAscii = regexp.MustCompile("^[A-Za-z0-9!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ]+$") var printable7BitAscii = regexp.MustCompile("^[A-Za-z0-9!\"#$%&'()*+,\\-./:;<=>?@[\\]^_`{|}~ ]+$")
// ValidatePasswordFormat returns an error if the password is too short, or consists of characters // ValidatePasswordFormat returns an error if the password is too short, or consists of characters
// outside the range of the printable 7bit ascii set // outside the range of the printable 7bit ascii set

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,21 +0,0 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package deps contains the console JavaScript dependencies Go embedded.
package deps
//go:generate go-bindata -nometadata -pkg deps -o bindata.go bignumber.js
//go:generate gofmt -w -s bindata.go

View File

@ -24,9 +24,9 @@ import (
"github.com/dop251/goja" "github.com/dop251/goja"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/jsre/deps"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/signer/core" "github.com/ethereum/go-ethereum/signer/core"
"github.com/ethereum/go-ethereum/signer/rules/deps"
"github.com/ethereum/go-ethereum/signer/storage" "github.com/ethereum/go-ethereum/signer/storage"
) )