mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
Merge branch 'ethereum:master' into santa
This commit is contained in:
commit
b25a6ba82c
83 changed files with 965 additions and 302 deletions
15
accounts/external/backend.go
vendored
15
accounts/external/backend.go
vendored
|
|
@ -205,7 +205,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
to = &t
|
to = &t
|
||||||
}
|
}
|
||||||
args := &apitypes.SendTxArgs{
|
args := &apitypes.SendTxArgs{
|
||||||
Data: &data,
|
Input: &data,
|
||||||
Nonce: hexutil.Uint64(tx.Nonce()),
|
Nonce: hexutil.Uint64(tx.Nonce()),
|
||||||
Value: hexutil.Big(*tx.Value()),
|
Value: hexutil.Big(*tx.Value()),
|
||||||
Gas: hexutil.Uint64(tx.Gas()),
|
Gas: hexutil.Uint64(tx.Gas()),
|
||||||
|
|
@ -215,7 +215,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case types.LegacyTxType, types.AccessListTxType:
|
case types.LegacyTxType, types.AccessListTxType:
|
||||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
||||||
case types.DynamicFeeTxType:
|
case types.DynamicFeeTxType, types.BlobTxType:
|
||||||
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
||||||
default:
|
default:
|
||||||
|
|
@ -235,6 +235,17 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
accessList := tx.AccessList()
|
accessList := tx.AccessList()
|
||||||
args.AccessList = &accessList
|
args.AccessList = &accessList
|
||||||
}
|
}
|
||||||
|
if tx.Type() == types.BlobTxType {
|
||||||
|
args.BlobHashes = tx.BlobHashes()
|
||||||
|
sidecar := tx.BlobTxSidecar()
|
||||||
|
if sidecar == nil {
|
||||||
|
return nil, errors.New("blobs must be present for signing")
|
||||||
|
}
|
||||||
|
args.Blobs = sidecar.Blobs
|
||||||
|
args.Commitments = sidecar.Commitments
|
||||||
|
args.Proofs = sidecar.Proofs
|
||||||
|
}
|
||||||
|
|
||||||
var res signTransactionResult
|
var res signTransactionResult
|
||||||
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package engine
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"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"
|
||||||
|
|
@ -132,12 +133,7 @@ func (b PayloadID) Version() PayloadVersion {
|
||||||
|
|
||||||
// Is returns whether the identifier matches any of provided payload versions.
|
// Is returns whether the identifier matches any of provided payload versions.
|
||||||
func (b PayloadID) Is(versions ...PayloadVersion) bool {
|
func (b PayloadID) Is(versions ...PayloadVersion) bool {
|
||||||
for _, v := range versions {
|
return slices.Contains(versions, b.Version())
|
||||||
if v == b.Version() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b PayloadID) String() string {
|
func (b PayloadID) String() string {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@ package types
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -27,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/beacon/merkle"
|
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||||
"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/log"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -34,6 +37,8 @@ import (
|
||||||
// across signing different data structures.
|
// across signing different data structures.
|
||||||
const syncCommitteeDomain = 7
|
const syncCommitteeDomain = 7
|
||||||
|
|
||||||
|
var knownForks = []string{"GENESIS", "ALTAIR", "BELLATRIX", "CAPELLA", "DENEB"}
|
||||||
|
|
||||||
// Fork describes a single beacon chain fork and also stores the calculated
|
// Fork describes a single beacon chain fork and also stores the calculated
|
||||||
// signature domain used after this fork.
|
// signature domain used after this fork.
|
||||||
type Fork struct {
|
type Fork struct {
|
||||||
|
|
@ -46,6 +51,9 @@ type Fork struct {
|
||||||
// Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
|
// Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
|
||||||
Version []byte
|
Version []byte
|
||||||
|
|
||||||
|
// index in list of known forks or MaxInt if unknown
|
||||||
|
knownIndex int
|
||||||
|
|
||||||
// calculated by computeDomain, based on fork version and genesis validators root
|
// calculated by computeDomain, based on fork version and genesis validators root
|
||||||
domain merkle.Value
|
domain merkle.Value
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +109,12 @@ func (f Forks) SigningRoot(header Header) (common.Hash, error) {
|
||||||
|
|
||||||
func (f Forks) Len() int { return len(f) }
|
func (f Forks) Len() int { return len(f) }
|
||||||
func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||||
func (f Forks) Less(i, j int) bool { return f[i].Epoch < f[j].Epoch }
|
func (f Forks) Less(i, j int) bool {
|
||||||
|
if f[i].Epoch != f[j].Epoch {
|
||||||
|
return f[i].Epoch < f[j].Epoch
|
||||||
|
}
|
||||||
|
return f[i].knownIndex < f[j].knownIndex
|
||||||
|
}
|
||||||
|
|
||||||
// ChainConfig contains the beacon chain configuration.
|
// ChainConfig contains the beacon chain configuration.
|
||||||
type ChainConfig struct {
|
type ChainConfig struct {
|
||||||
|
|
@ -122,16 +135,22 @@ func (c *ChainConfig) ForkAtEpoch(epoch uint64) Fork {
|
||||||
|
|
||||||
// AddFork adds a new item to the list of forks.
|
// AddFork adds a new item to the list of forks.
|
||||||
func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
|
func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
|
||||||
|
knownIndex := slices.Index(knownForks, name)
|
||||||
|
if knownIndex == -1 {
|
||||||
|
knownIndex = math.MaxInt // assume that the unknown fork happens after the known ones
|
||||||
|
if epoch != math.MaxUint64 {
|
||||||
|
log.Warn("Unknown fork in config.yaml", "fork name", name, "known forks", knownForks)
|
||||||
|
}
|
||||||
|
}
|
||||||
fork := &Fork{
|
fork := &Fork{
|
||||||
Name: name,
|
Name: name,
|
||||||
Epoch: epoch,
|
Epoch: epoch,
|
||||||
Version: version,
|
Version: version,
|
||||||
|
knownIndex: knownIndex,
|
||||||
}
|
}
|
||||||
fork.computeDomain(c.GenesisValidatorsRoot)
|
fork.computeDomain(c.GenesisValidatorsRoot)
|
||||||
|
|
||||||
c.Forks = append(c.Forks, fork)
|
c.Forks = append(c.Forks, fork)
|
||||||
sort.Sort(c.Forks)
|
sort.Sort(c.Forks)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,6 +200,5 @@ func (c *ChainConfig) LoadForks(path string) error {
|
||||||
for name := range versions {
|
for name := range versions {
|
||||||
return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name)
|
return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name)
|
||||||
}
|
}
|
||||||
sort.Sort(c.Forks)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,22 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.22.1
|
# version:golang 1.22.2
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
79c9b91d7f109515a25fc3ecdaad125d67e6bdb54f6d4d98580f46799caea321 go1.22.1.src.tar.gz
|
374ea82b289ec738e968267cac59c7d5ff180f9492250254784b2044e90df5a9 go1.22.2.src.tar.gz
|
||||||
3bc971772f4712fec0364f4bc3de06af22a00a12daab10b6f717fdcd13156cc0 go1.22.1.darwin-amd64.tar.gz
|
33e7f63077b1c5bce4f1ecadd4d990cf229667c40bfb00686990c950911b7ab7 go1.22.2.darwin-amd64.tar.gz
|
||||||
f6a9cec6b8a002fcc9c0ee24ec04d67f430a52abc3cfd613836986bcc00d8383 go1.22.1.darwin-arm64.tar.gz
|
660298be38648723e783ba0398e90431de1cb288c637880cdb124f39bd977f0d go1.22.2.darwin-arm64.tar.gz
|
||||||
99f81c10d5a3f8a886faf8fa86aaa2aaf929fbed54a972ae5eec3c5e0bdb961a go1.22.1.freebsd-386.tar.gz
|
efc7162b0cad2f918ac566a923d4701feb29dc9c0ab625157d49b1cbcbba39da go1.22.2.freebsd-386.tar.gz
|
||||||
51c614ddd92ee4a9913a14c39bf80508d9cfba08561f24d2f075fd00f3cfb067 go1.22.1.freebsd-amd64.tar.gz
|
d753428296e6709527e291fd204700a587ffef2c0a472b21aebea11618245929 go1.22.2.freebsd-amd64.tar.gz
|
||||||
8484df36d3d40139eaf0fe5e647b006435d826cc12f9ae72973bf7ec265e0ae4 go1.22.1.linux-386.tar.gz
|
586d9eb7fe0489ab297ad80dd06414997df487c5cf536c490ffeaa8d8f1807a7 go1.22.2.linux-386.tar.gz
|
||||||
aab8e15785c997ae20f9c88422ee35d962c4562212bb0f879d052a35c8307c7f go1.22.1.linux-amd64.tar.gz
|
5901c52b7a78002aeff14a21f93e0f064f74ce1360fce51c6ee68cd471216a17 go1.22.2.linux-amd64.tar.gz
|
||||||
e56685a245b6a0c592fc4a55f0b7803af5b3f827aaa29feab1f40e491acf35b8 go1.22.1.linux-arm64.tar.gz
|
36e720b2d564980c162a48c7e97da2e407dfcc4239e1e58d98082dfa2486a0c1 go1.22.2.linux-arm64.tar.gz
|
||||||
8cb7a90e48c20daed39a6ac8b8a40760030ba5e93c12274c42191d868687c281 go1.22.1.linux-armv6l.tar.gz
|
9243dfafde06e1efe24d59df6701818e6786b4adfdf1191098050d6d023c5369 go1.22.2.linux-armv6l.tar.gz
|
||||||
ac775e19d93cc1668999b77cfe8c8964abfbc658718feccfe6e0eb87663cd668 go1.22.1.linux-ppc64le.tar.gz
|
251a8886c5113be6490bdbb955ddee98763b49c9b1bf4c8364c02d3b482dab00 go1.22.2.linux-ppc64le.tar.gz
|
||||||
7bb7dd8e10f95c9a4cc4f6bef44c816a6e7c9e03f56ac6af6efbb082b19b379f go1.22.1.linux-s390x.tar.gz
|
2b39019481c28c560d65e9811a478ae10e3ef765e0f59af362031d386a71bfef go1.22.2.linux-s390x.tar.gz
|
||||||
0c5ebb7eb39b7884ec99f92b425d4c03a96a72443562aafbf6e7d15c42a3108a go1.22.1.windows-386.zip
|
651753c06df037020ef4d162c5b273452e9ba976ed17ae39e66ef7ee89d8147e go1.22.2.windows-386.zip
|
||||||
cf9c66a208a106402a527f5b956269ca506cfe535fc388e828d249ea88ed28ba go1.22.1.windows-amd64.zip
|
8e581cf330f49d3266e936521a2d8263679ef7e2fc2cbbceb85659122d883596 go1.22.2.windows-amd64.zip
|
||||||
85b8511b298c9f4199ecae26afafcc3d46155bac934d43f2357b9224bcaa310f go1.22.1.windows-arm64.zip
|
ddfca5beb9a0c62254266c3090c2555d899bf3e7aa26243e7de3621108f06875 go1.22.2.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 1.55.2
|
# version:golangci 1.55.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ It enables usecases like the following:
|
||||||
|
|
||||||
The two main features that are required for this to work well are;
|
The two main features that are required for this to work well are;
|
||||||
|
|
||||||
1. Rule Implementation: how to create, manage and interpret rules in a flexible but secure manner
|
1. Rule Implementation: how to create, manage, and interpret rules in a flexible but secure manner
|
||||||
2. Credential managements and credentials; how to provide auto-unlock without exposing keys unnecessarily.
|
2. Credential management and credentials; how to provide auto-unlock without exposing keys unnecessarily.
|
||||||
|
|
||||||
The section below deals with both of them
|
The section below deals with both of them
|
||||||
|
|
||||||
## Rule Implementation
|
## Rule Implementation
|
||||||
|
|
||||||
A ruleset file is implemented as a `js` file. Under the hood, the ruleset-engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods
|
A ruleset file is implemented as a `js` file. Under the hood, the ruleset engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods
|
||||||
defined in the UI protocol. Example:
|
defined in the UI protocol. Example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
@ -27,7 +27,7 @@ function asBig(str) {
|
||||||
return new BigNumber(str)
|
return new BigNumber(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approve transactions to a certain contract if value is below a certain limit
|
// Approve transactions to a certain contract if the value is below a certain limit
|
||||||
function ApproveTx(req) {
|
function ApproveTx(req) {
|
||||||
var limit = big.Newint("0xb1a2bc2ec50000")
|
var limit = big.Newint("0xb1a2bc2ec50000")
|
||||||
var value = asBig(req.transaction.value);
|
var value = asBig(req.transaction.value);
|
||||||
|
|
@ -70,7 +70,7 @@ The Otto vm has a few [caveats](https://github.com/robertkrimen/otto):
|
||||||
Additionally, a few more have been added
|
Additionally, a few more have been added
|
||||||
|
|
||||||
* The rule execution cannot load external javascript files.
|
* The rule execution cannot load external javascript files.
|
||||||
* The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository.
|
* The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the GitHub repository.
|
||||||
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
|
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
|
||||||
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
|
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
|
||||||
* The JS engine has access to `storage` and `console`.
|
* The JS engine has access to `storage` and `console`.
|
||||||
|
|
@ -88,8 +88,8 @@ Some security precautions can be made, such as:
|
||||||
|
|
||||||
##### Security of implementation
|
##### Security of implementation
|
||||||
|
|
||||||
The drawbacks of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement, since it's already
|
The drawback of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement since it's already
|
||||||
implemented for `geth`. There are no known security vulnerabilities in, nor have we had any security-problems with it so far.
|
implemented for `geth`. There are no known security vulnerabilities in it, nor have we had any security problems with it so far.
|
||||||
|
|
||||||
The javascript engine would be an added attack surface; but if the validation of `rulesets` is made good (with hash-based attestation), the actual javascript cannot be considered
|
The javascript engine would be an added attack surface; but if the validation of `rulesets` is made good (with hash-based attestation), the actual javascript cannot be considered
|
||||||
an attack surface -- if an attacker can control the ruleset, a much simpler attack would be to implement an "always-approve" rule instead of exploiting the js vm. The only benefit
|
an attack surface -- if an attacker can control the ruleset, a much simpler attack would be to implement an "always-approve" rule instead of exploiting the js vm. The only benefit
|
||||||
|
|
@ -105,7 +105,7 @@ It's unclear whether any other DSL could be more secure; since there's always th
|
||||||
|
|
||||||
## Credential management
|
## Credential management
|
||||||
|
|
||||||
The ability to auto-approve transaction means that the signer needs to have necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass).
|
The ability to auto-approve transactions means that the signer needs to have the necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass).
|
||||||
|
|
||||||
### Example implementation
|
### Example implementation
|
||||||
|
|
||||||
|
|
@ -127,8 +127,8 @@ The `vault.dat` would be an encrypted container storing the following informatio
|
||||||
|
|
||||||
### Security considerations
|
### Security considerations
|
||||||
|
|
||||||
This would leave it up to the user to ensure that the `path/to/masterseed` is handled in a secure way. It's difficult to get around this, although one could
|
This would leave it up to the user to ensure that the `path/to/masterseed` is handled securely. It's difficult to get around this, although one could
|
||||||
imagine leveraging OS-level keychains where supported. The setup is however in general similar to how ssh-keys are stored in `.ssh/`.
|
imagine leveraging OS-level keychains where supported. The setup is however, in general, similar to how ssh-keys are stored in `.ssh/`.
|
||||||
|
|
||||||
|
|
||||||
# Implementation status
|
# Implementation status
|
||||||
|
|
@ -149,7 +149,7 @@ function big(str) {
|
||||||
// Time window: 1 week
|
// Time window: 1 week
|
||||||
var window = 1000* 3600*24*7;
|
var window = 1000* 3600*24*7;
|
||||||
|
|
||||||
// Limit : 1 ether
|
// Limit: 1 ether
|
||||||
var limit = new BigNumber("1e18");
|
var limit = new BigNumber("1e18");
|
||||||
|
|
||||||
function isLimitOk(transaction) {
|
function isLimitOk(transaction) {
|
||||||
|
|
@ -163,7 +163,7 @@ function isLimitOk(transaction) {
|
||||||
if (stored != "") {
|
if (stored != "") {
|
||||||
txs = JSON.parse(stored)
|
txs = JSON.parse(stored)
|
||||||
}
|
}
|
||||||
// First, remove all that have passed out of the time-window
|
// First, remove all that has passed out of the time window
|
||||||
var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
|
var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
|
||||||
console.log(txs, newtxs.length);
|
console.log(txs, newtxs.length);
|
||||||
|
|
||||||
|
|
@ -174,7 +174,7 @@ function isLimitOk(transaction) {
|
||||||
console.log("ApproveTx > Sum so far", sum);
|
console.log("ApproveTx > Sum so far", sum);
|
||||||
console.log("ApproveTx > Requested", value.toNumber());
|
console.log("ApproveTx > Requested", value.toNumber());
|
||||||
|
|
||||||
// Would we exceed weekly limit ?
|
// Would we exceed the weekly limit ?
|
||||||
return sum.plus(value).lt(limit)
|
return sum.plus(value).lt(limit)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("timed out waiting for txs")
|
return errors.New("timed out waiting for txs")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
|
func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package v5test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -266,7 +267,7 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
|
||||||
n := bn.conn.localNode.Node()
|
n := bn.conn.localNode.Node()
|
||||||
expect[n.ID()] = n
|
expect[n.ID()] = n
|
||||||
d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
|
d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
|
||||||
if !containsUint(dists, d) {
|
if !slices.Contains(dists, d) {
|
||||||
dists = append(dists, d)
|
dists = append(dists, d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,12 +252,3 @@ func checkRecords(records []*enr.Record) ([]*enode.Node, error) {
|
||||||
}
|
}
|
||||||
return nodes, nil
|
return nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func containsUint(ints []uint, x uint) bool {
|
|
||||||
for i := range ints {
|
|
||||||
if ints[i] == x {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -50,4 +50,4 @@ contains the password.
|
||||||
|
|
||||||
## JSON
|
## JSON
|
||||||
|
|
||||||
In case you need to output the result in a JSON format, you shall by using the `--json` flag.
|
In case you need to output the result in a JSON format, you shall use the `--json` flag.
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Priority queue data structure.
|
// Prque is a priority queue data structure.
|
||||||
type Prque[P cmp.Ordered, V any] struct {
|
type Prque[P cmp.Ordered, V any] struct {
|
||||||
cont *sstack[P, V]
|
cont *sstack[P, V]
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ func New[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] {
|
||||||
return &Prque[P, V]{newSstack[P, V](setIndex)}
|
return &Prque[P, V]{newSstack[P, V](setIndex)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a value with a given priority into the queue, expanding if necessary.
|
// Push a value with a given priority into the queue, expanding if necessary.
|
||||||
func (p *Prque[P, V]) Push(data V, priority P) {
|
func (p *Prque[P, V]) Push(data V, priority P) {
|
||||||
heap.Push(p.cont, &item[P, V]{data, priority})
|
heap.Push(p.cont, &item[P, V]{data, priority})
|
||||||
}
|
}
|
||||||
|
|
@ -43,14 +43,14 @@ func (p *Prque[P, V]) Peek() (V, P) {
|
||||||
return item.value, item.priority
|
return item.value, item.priority
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops the value with the greatest priority off the stack and returns it.
|
// Pop the value with the greatest priority off the stack and returns it.
|
||||||
// Currently no shrinking is done.
|
// Currently no shrinking is done.
|
||||||
func (p *Prque[P, V]) Pop() (V, P) {
|
func (p *Prque[P, V]) Pop() (V, P) {
|
||||||
item := heap.Pop(p.cont).(*item[P, V])
|
item := heap.Pop(p.cont).(*item[P, V])
|
||||||
return item.value, item.priority
|
return item.value, item.priority
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops only the item from the queue, dropping the associated priority value.
|
// PopItem pops only the item from the queue, dropping the associated priority value.
|
||||||
func (p *Prque[P, V]) PopItem() V {
|
func (p *Prque[P, V]) PopItem() V {
|
||||||
return heap.Pop(p.cont).(*item[P, V]).value
|
return heap.Pop(p.cont).(*item[P, V]).value
|
||||||
}
|
}
|
||||||
|
|
@ -60,17 +60,17 @@ func (p *Prque[P, V]) Remove(i int) V {
|
||||||
return heap.Remove(p.cont, i).(*item[P, V]).value
|
return heap.Remove(p.cont, i).(*item[P, V]).value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether the priority queue is empty.
|
// Empty checks whether the priority queue is empty.
|
||||||
func (p *Prque[P, V]) Empty() bool {
|
func (p *Prque[P, V]) Empty() bool {
|
||||||
return p.cont.Len() == 0
|
return p.cont.Len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of element in the priority queue.
|
// Size returns the number of element in the priority queue.
|
||||||
func (p *Prque[P, V]) Size() int {
|
func (p *Prque[P, V]) Size() int {
|
||||||
return p.cont.Len()
|
return p.cont.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears the contents of the priority queue.
|
// Reset clears the contents of the priority queue.
|
||||||
func (p *Prque[P, V]) Reset() {
|
func (p *Prque[P, V]) Reset() {
|
||||||
*p = *New[P, V](p.cont.setIndex)
|
*p = *New[P, V](p.cont.setIndex)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ func newSstack[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V]
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a value onto the stack, expanding it if necessary. Required by
|
// Push a value onto the stack, expanding it if necessary. Required by
|
||||||
// heap.Interface.
|
// heap.Interface.
|
||||||
func (s *sstack[P, V]) Push(data any) {
|
func (s *sstack[P, V]) Push(data any) {
|
||||||
if s.size == s.capacity {
|
if s.size == s.capacity {
|
||||||
|
|
@ -69,7 +69,7 @@ func (s *sstack[P, V]) Push(data any) {
|
||||||
s.size++
|
s.size++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops a value off the stack and returns it. Currently no shrinking is done.
|
// Pop a value off the stack and returns it. Currently no shrinking is done.
|
||||||
// Required by heap.Interface.
|
// Required by heap.Interface.
|
||||||
func (s *sstack[P, V]) Pop() (res any) {
|
func (s *sstack[P, V]) Pop() (res any) {
|
||||||
s.size--
|
s.size--
|
||||||
|
|
@ -85,18 +85,18 @@ func (s *sstack[P, V]) Pop() (res any) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the length of the stack. Required by sort.Interface.
|
// Len returns the length of the stack. Required by sort.Interface.
|
||||||
func (s *sstack[P, V]) Len() int {
|
func (s *sstack[P, V]) Len() int {
|
||||||
return s.size
|
return s.size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compares the priority of two elements of the stack (higher is first).
|
// Less compares the priority of two elements of the stack (higher is first).
|
||||||
// Required by sort.Interface.
|
// Required by sort.Interface.
|
||||||
func (s *sstack[P, V]) Less(i, j int) bool {
|
func (s *sstack[P, V]) Less(i, j int) bool {
|
||||||
return s.blocks[i/blockSize][i%blockSize].priority > s.blocks[j/blockSize][j%blockSize].priority
|
return s.blocks[i/blockSize][i%blockSize].priority > s.blocks[j/blockSize][j%blockSize].priority
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swaps two elements in the stack. Required by sort.Interface.
|
// Swap two elements in the stack. Required by sort.Interface.
|
||||||
func (s *sstack[P, V]) Swap(i, j int) {
|
func (s *sstack[P, V]) Swap(i, j int) {
|
||||||
ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize
|
ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize
|
||||||
a, b := s.blocks[jb][jo], s.blocks[ib][io]
|
a, b := s.blocks[jb][jo], s.blocks[ib][io]
|
||||||
|
|
@ -107,7 +107,7 @@ func (s *sstack[P, V]) Swap(i, j int) {
|
||||||
s.blocks[ib][io], s.blocks[jb][jo] = a, b
|
s.blocks[ib][io], s.blocks[jb][jo] = a, b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets the stack, effectively clearing its contents.
|
// Reset the stack, effectively clearing its contents.
|
||||||
func (s *sstack[P, V]) Reset() {
|
func (s *sstack[P, V]) Reset() {
|
||||||
*s = *newSstack[P, V](s.setIndex)
|
*s = *newSstack[P, V](s.setIndex)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||||
if rbloom != header.Bloom {
|
if rbloom != header.Bloom {
|
||||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
||||||
}
|
}
|
||||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
||||||
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
||||||
if receiptSha != header.ReceiptHash {
|
if receiptSha != header.ReceiptHash {
|
||||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,8 @@ type BlockChain struct {
|
||||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
||||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
||||||
blockCache *lru.Cache[common.Hash, *types.Block]
|
blockCache *lru.Cache[common.Hash, *types.Block]
|
||||||
|
|
||||||
|
txLookupLock sync.RWMutex
|
||||||
txLookupCache *lru.Cache[common.Hash, txLookup]
|
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||||
|
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
@ -439,7 +441,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
}
|
}
|
||||||
|
|
||||||
if alloc == nil {
|
if alloc == nil {
|
||||||
return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set")
|
return nil, errors.New("live blockchain tracer requires genesis alloc to be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
||||||
|
|
@ -2290,14 +2292,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
||||||
// rewind the canonical chain to a lower point.
|
// rewind the canonical chain to a lower point.
|
||||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
|
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
|
||||||
}
|
}
|
||||||
// Reset the tx lookup cache in case to clear stale txlookups.
|
// Acquire the tx-lookup lock before mutation. This step is essential
|
||||||
// This is done before writing any new chain data to avoid the
|
// as the txlookups should be changed atomically, and all subsequent
|
||||||
// weird scenario that canonical chain is changed while the
|
// reads should be blocked until the mutation is complete.
|
||||||
// stale lookups are still cached.
|
bc.txLookupLock.Lock()
|
||||||
bc.txLookupCache.Purge()
|
|
||||||
|
|
||||||
// Insert the new chain(except the head block(reverse order)),
|
// Insert the new chain segment in incremental order, from the old
|
||||||
// taking care of the proper incremental order.
|
// to the new. The new chain head (newChain[0]) is not inserted here,
|
||||||
|
// as it will be handled separately outside of this function
|
||||||
for i := len(newChain) - 1; i >= 1; i-- {
|
for i := len(newChain) - 1; i >= 1; i-- {
|
||||||
// Insert the block in the canonical way, re-writing history
|
// Insert the block in the canonical way, re-writing history
|
||||||
bc.writeHeadBlock(newChain[i])
|
bc.writeHeadBlock(newChain[i])
|
||||||
|
|
@ -2334,6 +2336,11 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
||||||
if err := indexesBatch.Write(); err != nil {
|
if err := indexesBatch.Write(); err != nil {
|
||||||
log.Crit("Failed to delete useless indexes", "err", err)
|
log.Crit("Failed to delete useless indexes", "err", err)
|
||||||
}
|
}
|
||||||
|
// Reset the tx lookup cache to clear stale txlookup cache.
|
||||||
|
bc.txLookupCache.Purge()
|
||||||
|
|
||||||
|
// Release the tx-lookup lock after mutation.
|
||||||
|
bc.txLookupLock.Unlock()
|
||||||
|
|
||||||
// Send out events for logs from the old canon chain, and 'reborn'
|
// Send out events for logs from the old canon chain, and 'reborn'
|
||||||
// logs from the new canon chain. The number of logs can be very
|
// logs from the new canon chain. The number of logs can be very
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,9 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
||||||
// transaction indexing is already finished. The transaction is not existent
|
// transaction indexing is already finished. The transaction is not existent
|
||||||
// from the node's perspective.
|
// from the node's perspective.
|
||||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
||||||
|
bc.txLookupLock.RLock()
|
||||||
|
defer bc.txLookupLock.RUnlock()
|
||||||
|
|
||||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||||
return item.lookup, item.transaction, nil
|
return item.lookup, item.transaction, nil
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ package core
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -1966,7 +1966,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
|
|
||||||
// Create a temporary persistent database
|
// Create a temporary persistent database
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := path.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
db, err := rawdb.Open(rawdb.OpenOptions{
|
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||||
Directory: datadir,
|
Directory: datadir,
|
||||||
|
|
|
||||||
|
|
@ -596,6 +596,9 @@ func (s *MatcherSession) deliverSections(bit uint, sections []uint64, bitsets []
|
||||||
// of the session, any request in-flight need to be responded to! Empty responses
|
// of the session, any request in-flight need to be responded to! Empty responses
|
||||||
// are fine though in that case.
|
// are fine though in that case.
|
||||||
func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan *Retrieval) {
|
func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan *Retrieval) {
|
||||||
|
waitTimer := time.NewTimer(wait)
|
||||||
|
defer waitTimer.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Allocate a new bloom bit index to retrieve data for, stopping when done
|
// Allocate a new bloom bit index to retrieve data for, stopping when done
|
||||||
bit, ok := s.allocateRetrieval()
|
bit, ok := s.allocateRetrieval()
|
||||||
|
|
@ -604,6 +607,7 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
|
||||||
}
|
}
|
||||||
// Bit allocated, throttle a bit if we're below our batch limit
|
// Bit allocated, throttle a bit if we're below our batch limit
|
||||||
if s.pendingSections(bit) < batch {
|
if s.pendingSections(bit) < batch {
|
||||||
|
waitTimer.Reset(wait)
|
||||||
select {
|
select {
|
||||||
case <-s.quit:
|
case <-s.quit:
|
||||||
// Session terminating, we can't meaningfully service, abort
|
// Session terminating, we can't meaningfully service, abort
|
||||||
|
|
@ -611,7 +615,7 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
|
||||||
s.deliverSections(bit, []uint64{}, [][]byte{})
|
s.deliverSections(bit, []uint64{}, [][]byte{})
|
||||||
return
|
return
|
||||||
|
|
||||||
case <-time.After(wait):
|
case <-waitTimer.C:
|
||||||
// Throttling up, fetch whatever is available
|
// Throttling up, fetch whatever is available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
||||||
if header.ExcessBlobGas != nil {
|
if header.ExcessBlobGas != nil {
|
||||||
blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas)
|
blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas)
|
||||||
}
|
}
|
||||||
if header.Difficulty.Cmp(common.Big0) == 0 {
|
if header.Difficulty.Sign() == 0 {
|
||||||
random = &header.MixDigest
|
random = &header.MixDigest
|
||||||
}
|
}
|
||||||
return vm.BlockContext{
|
return vm.BlockContext{
|
||||||
|
|
|
||||||
|
|
@ -582,7 +582,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||||
Config: &config,
|
Config: &config,
|
||||||
GasLimit: gasLimit,
|
GasLimit: gasLimit,
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Difficulty: big.NewInt(1),
|
Difficulty: big.NewInt(0),
|
||||||
Alloc: map[common.Address]types.Account{
|
Alloc: map[common.Address]types.Account{
|
||||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -172,7 +171,7 @@ func resolveChainFreezerDir(ancient string) string {
|
||||||
// sub folder, if not then two possibilities:
|
// sub folder, if not then two possibilities:
|
||||||
// - chain freezer is not initialized
|
// - chain freezer is not initialized
|
||||||
// - chain freezer exists in legacy location (root ancient folder)
|
// - chain freezer exists in legacy location (root ancient folder)
|
||||||
freezer := path.Join(ancient, ChainFreezerName)
|
freezer := filepath.Join(ancient, ChainFreezerName)
|
||||||
if !common.FileExist(freezer) {
|
if !common.FileExist(freezer) {
|
||||||
if !common.FileExist(ancient) {
|
if !common.FileExist(ancient) {
|
||||||
// The entire ancient store is not initialized, still use the sub
|
// The entire ancient store is not initialized, still use the sub
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -398,11 +397,11 @@ func TestRenameWindows(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
f2, err := os.Create(path.Join(dir1, fname2))
|
f2, err := os.Create(filepath.Join(dir1, fname2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
f3, err := os.Create(path.Join(dir2, fname2))
|
f3, err := os.Create(filepath.Join(dir2, fname2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -424,15 +423,15 @@ func TestRenameWindows(t *testing.T) {
|
||||||
if err := f3.Close(); err != nil {
|
if err := f3.Close(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := os.Rename(f.Name(), path.Join(dir2, fname)); err != nil {
|
if err := os.Rename(f.Name(), filepath.Join(dir2, fname)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := os.Rename(f2.Name(), path.Join(dir2, fname2)); err != nil {
|
if err := os.Rename(f2.Name(), filepath.Join(dir2, fname2)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check file contents
|
// Check file contents
|
||||||
f, err = os.Open(path.Join(dir2, fname))
|
f, err = os.Open(filepath.Join(dir2, fname))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -446,7 +445,7 @@ func TestRenameWindows(t *testing.T) {
|
||||||
t.Errorf("unexpected file contents. Got %v\n", buf)
|
t.Errorf("unexpected file contents. Got %v\n", buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err = os.Open(path.Join(dir2, fname2))
|
f, err = os.Open(filepath.Join(dir2, fname2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
core/tracing/CHANGELOG.md
Normal file
69
core/tracing/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to the tracing interface will be documented in this file.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
There has been a major breaking change in the tracing interface for custom native tracers. JS and built-in tracers are not affected by this change and tracing API methods may be used as before. This overhaul has been done as part of the new live tracing feature ([#29189](https://github.com/ethereum/go-ethereum/pull/29189)). To learn more about live tracing please refer to the [docs](https://geth.ethereum.org/docs/developers/evm-tracing/live-tracing).
|
||||||
|
|
||||||
|
**The `EVMLogger` interface which the tracers implemented has been removed.** It has been replaced by a new struct `tracing.Hooks`. `Hooks` keeps pointers to event listening functions. Internally the EVM will use these function pointers to emit events and can skip an event if the tracer has opted not to implement it. In fact this is the main reason for this change of approach. Another benefit is the ease of adding new hooks in future, and dynamically assigning event receivers.
|
||||||
|
|
||||||
|
The consequence of this change can be seen in the constructor of a tracer. Let's take the 4byte tracer as an example. Previously the constructor return an instance which satisfied the interface. Now it should return a pointer to `tracers.Tracer` (which is now also a struct as opposed to an interface) and explicitly assign the event listeners. As a side-benefit the tracers will not have to provide empty implementation of methods just to satisfy the interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
||||||
|
t := &fourByteTracer{
|
||||||
|
ids: make(map[string]int),
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And now:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) {
|
||||||
|
t := &fourByteTracer{
|
||||||
|
ids: make(map[string]int),
|
||||||
|
}
|
||||||
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.onTxStart,
|
||||||
|
OnEnter: t.onEnter,
|
||||||
|
},
|
||||||
|
GetResult: t.getResult,
|
||||||
|
Stop: t.stop,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event listeners
|
||||||
|
|
||||||
|
If you have sharp eyes you might have noticed the new names for `OnTxStart` and `OnEnter`, previously called `CaptureTxStart` and `CaptureEnter`. Indeed there have been various modifications to the signatures of the event listeners. All method names now follow the `On*` pattern instead of `Capture*`. However the modifications are not limited to the names.
|
||||||
|
|
||||||
|
#### New methods
|
||||||
|
|
||||||
|
The live tracing feature was half about adding more observability into the state of the blockchain. As such there have been a host of method additions. Please consult the [Hooks](./hooks.go) struct for the full list of methods. Custom tracers which are invoked through the API (as opposed to "live" tracers) can benefit from the following new methods:
|
||||||
|
|
||||||
|
- `OnGasChange(old, new uint64, reason GasChangeReason)`: This hook tracks the lifetime of gas within a transaction and its subcalls. It will first track the initial purchase of gas with ether, then the following consumptions and refunds of gas until at the end the rest is returned.
|
||||||
|
- `OnBalanceChange(addr common.Address, prev, new *big.Int, reason BalanceChangeReason)`: This hook tracks the balance changes of accounts. Where possible a reason is provided for the change (e.g. a transfer, gas purchase, withdrawal deposit etc).
|
||||||
|
- `OnNonceChange(addr common.Address, prev, new uint64)`: This hook tracks the nonce changes of accounts.
|
||||||
|
- `OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)`: This hook tracks the code changes of accounts.
|
||||||
|
- `OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash)`: This hook tracks the storage changes of accounts.
|
||||||
|
- `OnLogChange(log *types.Log)`: This hook tracks the logs emitted by the EVM.
|
||||||
|
|
||||||
|
#### Removed methods
|
||||||
|
|
||||||
|
The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signaled the top-level call frame of a transaction. The relevant info will be now emitted by `OnEnter` and `OnExit` which are emitted for every call frame. They now contain a `depth` parameter which can be used to distinguish the top-level call frame when necessary. The `create bool` parameter to `CaptureStart` can now be inferred from `typ byte` in `OnEnter`, i.e. `vm.OpCode(typ) == vm.CREATE`.
|
||||||
|
|
||||||
|
#### Modified methods
|
||||||
|
|
||||||
|
- `CaptureTxStart` -> `OnTxStart(vm *VMContext, tx *types.Transaction, from common.Address)`. It now emits the full transaction object as well as `from` which should be used to get the sender address. The `*VMContext` is a replacement for the `*vm.EVM` object previously passed to `CaptureStart`.
|
||||||
|
- `CaptureTxEnd` -> `OnTxEnd(receipt *types.Receipt, err error)`. It now returns the full receipt object.
|
||||||
|
- `CaptureEnter` -> `OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)`. The new `depth int` parameter indicates the call stack depth. It is 0 for the top-level call. Furthermore, the location where `OnEnter` is called in the EVM is now made a soon as a call is started. This means some specific error cases that were not before calling `OnEnter/OnExit` will now do so, leading some transaction to have an extra call traced.
|
||||||
|
- `CaptureExit` -> `OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool)`. It has the new `depth` parameter, same as `OnEnter`. The new `reverted` parameter indicates whether the call frame was reverted.
|
||||||
|
- `CaptureState` -> `OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error)`. `op` is of type `byte` which can be cast to `vm.OpCode` when necessary. A `*vm.ScopeContext` is not passed anymore. It is replaced by `tracing.OpContext` which offers access to the memory, stack and current contract.
|
||||||
|
- `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above.
|
||||||
|
|
||||||
|
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.13.14...master
|
||||||
|
|
@ -60,7 +60,7 @@ func newLimbo(datadir string) (*limbo, error) {
|
||||||
fails = append(fails, id)
|
fails = append(fails, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
store, err := billy.Open(billy.Options{Path: datadir}, newSlotter(), index)
|
store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, newSlotter(), index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,8 +197,8 @@ type Block struct {
|
||||||
withdrawals Withdrawals
|
withdrawals Withdrawals
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Value
|
hash atomic.Pointer[common.Hash]
|
||||||
size atomic.Value
|
size atomic.Uint64
|
||||||
|
|
||||||
// These fields are used by package eth to track
|
// These fields are used by package eth to track
|
||||||
// inter-peer block relay.
|
// inter-peer block relay.
|
||||||
|
|
@ -406,8 +406,8 @@ func (b *Block) BlobGasUsed() *uint64 {
|
||||||
// Size returns the true RLP encoded storage size of the block, either by encoding
|
// Size returns the true RLP encoded storage size of the block, either by encoding
|
||||||
// and returning it, or returning a previously cached value.
|
// and returning it, or returning a previously cached value.
|
||||||
func (b *Block) Size() uint64 {
|
func (b *Block) Size() uint64 {
|
||||||
if size := b.size.Load(); size != nil {
|
if size := b.size.Load(); size > 0 {
|
||||||
return size.(uint64)
|
return size
|
||||||
}
|
}
|
||||||
c := writeCounter(0)
|
c := writeCounter(0)
|
||||||
rlp.Encode(&c, b)
|
rlp.Encode(&c, b)
|
||||||
|
|
@ -486,11 +486,11 @@ func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block {
|
||||||
// The hash is computed on the first call and cached thereafter.
|
// The hash is computed on the first call and cached thereafter.
|
||||||
func (b *Block) Hash() common.Hash {
|
func (b *Block) Hash() common.Hash {
|
||||||
if hash := b.hash.Load(); hash != nil {
|
if hash := b.hash.Load(); hash != nil {
|
||||||
return hash.(common.Hash)
|
return *hash
|
||||||
}
|
}
|
||||||
v := b.header.Hash()
|
h := b.header.Hash()
|
||||||
b.hash.Store(v)
|
b.hash.Store(&h)
|
||||||
return v
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
type Blocks []*Block
|
type Blocks []*Block
|
||||||
|
|
|
||||||
|
|
@ -57,9 +57,9 @@ type Transaction struct {
|
||||||
time time.Time // Time first seen locally (spam avoidance)
|
time time.Time // Time first seen locally (spam avoidance)
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Value
|
hash atomic.Pointer[common.Hash]
|
||||||
size atomic.Value
|
size atomic.Uint64
|
||||||
from atomic.Value
|
from atomic.Pointer[sigCache]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTx creates a new transaction.
|
// NewTx creates a new transaction.
|
||||||
|
|
@ -446,6 +446,26 @@ func (tx *Transaction) WithoutBlobTxSidecar() *Transaction {
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithBlobTxSidecar returns a copy of tx with the blob sidecar added.
|
||||||
|
func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction {
|
||||||
|
blobtx, ok := tx.inner.(*BlobTx)
|
||||||
|
if !ok {
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
cpy := &Transaction{
|
||||||
|
inner: blobtx.withSidecar(sideCar),
|
||||||
|
time: tx.time,
|
||||||
|
}
|
||||||
|
// Note: tx.size cache not carried over because the sidecar is included in size!
|
||||||
|
if h := tx.hash.Load(); h != nil {
|
||||||
|
cpy.hash.Store(h)
|
||||||
|
}
|
||||||
|
if f := tx.from.Load(); f != nil {
|
||||||
|
cpy.from.Store(f)
|
||||||
|
}
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
|
||||||
// SetTime sets the decoding time of a transaction. This is used by tests to set
|
// SetTime sets the decoding time of a transaction. This is used by tests to set
|
||||||
// arbitrary times and by persistent transaction pools when loading old txs from
|
// arbitrary times and by persistent transaction pools when loading old txs from
|
||||||
// disk.
|
// disk.
|
||||||
|
|
@ -462,7 +482,7 @@ func (tx *Transaction) Time() time.Time {
|
||||||
// Hash returns the transaction hash.
|
// Hash returns the transaction hash.
|
||||||
func (tx *Transaction) Hash() common.Hash {
|
func (tx *Transaction) Hash() common.Hash {
|
||||||
if hash := tx.hash.Load(); hash != nil {
|
if hash := tx.hash.Load(); hash != nil {
|
||||||
return hash.(common.Hash)
|
return *hash
|
||||||
}
|
}
|
||||||
|
|
||||||
var h common.Hash
|
var h common.Hash
|
||||||
|
|
@ -471,15 +491,15 @@ func (tx *Transaction) Hash() common.Hash {
|
||||||
} else {
|
} else {
|
||||||
h = prefixedRlpHash(tx.Type(), tx.inner)
|
h = prefixedRlpHash(tx.Type(), tx.inner)
|
||||||
}
|
}
|
||||||
tx.hash.Store(h)
|
tx.hash.Store(&h)
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the true encoded storage size of the transaction, either by encoding
|
// Size returns the true encoded storage size of the transaction, either by encoding
|
||||||
// and returning it, or returning a previously cached value.
|
// and returning it, or returning a previously cached value.
|
||||||
func (tx *Transaction) Size() uint64 {
|
func (tx *Transaction) Size() uint64 {
|
||||||
if size := tx.size.Load(); size != nil {
|
if size := tx.size.Load(); size > 0 {
|
||||||
return size.(uint64)
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache miss, encode and cache.
|
// Cache miss, encode and cache.
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,7 @@ func MustSignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) *Transaction
|
||||||
// signing method. The cache is invalidated if the cached signer does
|
// signing method. The cache is invalidated if the cached signer does
|
||||||
// not match the signer used in the current call.
|
// not match the signer used in the current call.
|
||||||
func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
||||||
if sc := tx.from.Load(); sc != nil {
|
if sigCache := tx.from.Load(); sigCache != nil {
|
||||||
sigCache := sc.(sigCache)
|
|
||||||
// If the signer used to derive from in a previous
|
// If the signer used to derive from in a previous
|
||||||
// call is not the same as used current, invalidate
|
// call is not the same as used current, invalidate
|
||||||
// the cache.
|
// the cache.
|
||||||
|
|
@ -142,7 +141,7 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
tx.from.Store(sigCache{signer: signer, from: addr})
|
tx.from.Store(&sigCache{signer: signer, from: addr})
|
||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,12 @@ func (tx *BlobTx) withoutSidecar() *BlobTx {
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *BlobTx) withSidecar(sideCar *BlobTxSidecar) *BlobTx {
|
||||||
|
cpy := *tx
|
||||||
|
cpy.Sidecar = sideCar
|
||||||
|
return &cpy
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *BlobTx) encode(b *bytes.Buffer) error {
|
func (tx *BlobTx) encode(b *bytes.Buffer) error {
|
||||||
if tx.Sidecar == nil {
|
if tx.Sidecar == nil {
|
||||||
return rlp.Encode(b, tx)
|
return rlp.Encode(b, tx)
|
||||||
|
|
|
||||||
|
|
@ -111,15 +111,15 @@ var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{
|
||||||
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
|
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
|
||||||
// contracts specified in EIP-2537. These are exported for testing purposes.
|
// contracts specified in EIP-2537. These are exported for testing purposes.
|
||||||
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
|
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
|
||||||
common.BytesToAddress([]byte{10}): &bls12381G1Add{},
|
common.BytesToAddress([]byte{11}): &bls12381G1Add{},
|
||||||
common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
|
common.BytesToAddress([]byte{12}): &bls12381G1Mul{},
|
||||||
common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
|
common.BytesToAddress([]byte{13}): &bls12381G1MultiExp{},
|
||||||
common.BytesToAddress([]byte{13}): &bls12381G2Add{},
|
common.BytesToAddress([]byte{14}): &bls12381G2Add{},
|
||||||
common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
|
common.BytesToAddress([]byte{15}): &bls12381G2Mul{},
|
||||||
common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
|
common.BytesToAddress([]byte{16}): &bls12381G2MultiExp{},
|
||||||
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
|
common.BytesToAddress([]byte{17}): &bls12381Pairing{},
|
||||||
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
|
common.BytesToAddress([]byte{18}): &bls12381MapG1{},
|
||||||
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
|
common.BytesToAddress([]byte{19}): &bls12381MapG2{},
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ var (
|
||||||
ErrContractAddressCollision = errors.New("contract address collision")
|
ErrContractAddressCollision = errors.New("contract address collision")
|
||||||
ErrExecutionReverted = errors.New("execution reverted")
|
ErrExecutionReverted = errors.New("execution reverted")
|
||||||
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
||||||
|
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||||
ErrInvalidJump = errors.New("invalid jump destination")
|
ErrInvalidJump = errors.New("invalid jump destination")
|
||||||
ErrWriteProtection = errors.New("write protection")
|
ErrWriteProtection = errors.New("write protection")
|
||||||
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
|
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
|
||||||
|
|
|
||||||
|
|
@ -439,13 +439,19 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
if evm.chainRules.IsBerlin {
|
if evm.chainRules.IsBerlin {
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
}
|
}
|
||||||
// Ensure there's no existing contract already at the designated address
|
// Ensure there's no existing contract already at the designated address.
|
||||||
|
// Account is regarded as existent if any of these three conditions is met:
|
||||||
|
// - the nonce is nonzero
|
||||||
|
// - the code is non-empty
|
||||||
|
// - the storage is non-empty
|
||||||
contractHash := evm.StateDB.GetCodeHash(address)
|
contractHash := evm.StateDB.GetCodeHash(address)
|
||||||
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
|
storageRoot := evm.StateDB.GetStorageRoot(address)
|
||||||
|
if evm.StateDB.GetNonce(address) != 0 ||
|
||||||
|
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||||
|
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||||
}
|
}
|
||||||
// Create a new account on the state
|
// Create a new account on the state
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -310,9 +311,12 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||||
if overflow || size > params.MaxInitCodeSize {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if size > params.MaxInitCodeSize {
|
||||||
|
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
|
||||||
|
}
|
||||||
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||||
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
||||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||||
|
|
@ -326,9 +330,12 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||||
if overflow || size > params.MaxInitCodeSize {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if size > params.MaxInitCodeSize {
|
||||||
|
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
|
||||||
|
}
|
||||||
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||||
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
||||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -98,7 +99,7 @@ func TestEIP2200(t *testing.T) {
|
||||||
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
||||||
|
|
||||||
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
|
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
|
||||||
if err != tt.failure {
|
if !errors.Is(err, tt.failure) {
|
||||||
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
|
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
|
||||||
}
|
}
|
||||||
if used := tt.gaspool - gas; used != tt.used {
|
if used := tt.gaspool - gas; used != tt.used {
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ type StateDB interface {
|
||||||
GetCommittedState(common.Address, common.Hash) common.Hash
|
GetCommittedState(common.Address, common.Hash) common.Hash
|
||||||
GetState(common.Address, common.Hash) common.Hash
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
SetState(common.Address, common.Hash, common.Hash)
|
SetState(common.Address, common.Hash, common.Hash)
|
||||||
|
GetStorageRoot(addr common.Address) common.Hash
|
||||||
|
|
||||||
GetTransientState(addr common.Address, key common.Hash) common.Hash
|
GetTransientState(addr common.Address, key common.Hash) common.Hash
|
||||||
SetTransientState(addr common.Address, key, value common.Hash)
|
SetTransientState(addr common.Address, key, value common.Hash)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -255,7 +257,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
var dynamicCost uint64
|
var dynamicCost uint64
|
||||||
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
||||||
cost += dynamicCost // for tracing
|
cost += dynamicCost // for tracing
|
||||||
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, tracing.GasChangeIgnored) {
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
|
||||||
|
}
|
||||||
|
if !contract.UseGas(dynamicCost, in.evm.Config.Tracer, tracing.GasChangeIgnored) {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ func createKeyPair() (string, string) {
|
||||||
defer os.Remove(tmpKey.Name())
|
defer os.Remove(tmpKey.Name())
|
||||||
defer os.Remove(tmpKey.Name() + ".pub")
|
defer os.Remove(tmpKey.Name() + ".pub")
|
||||||
defer os.Remove(tmpKey.Name() + ".sec")
|
defer os.Remove(tmpKey.Name() + ".sec")
|
||||||
|
defer tmpKey.Close()
|
||||||
cmd := exec.Command("signify", "-G", "-n", "-p", tmpKey.Name()+".pub", "-s", tmpKey.Name()+".sec")
|
cmd := exec.Command("signify", "-G", "-n", "-p", tmpKey.Name()+".pub", "-s", tmpKey.Name()+".sec")
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
panic(fmt.Sprintf("could not verify the file: %v, output: \n%s", err, output))
|
panic(fmt.Sprintf("could not verify the file: %v, output: \n%s", err, output))
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
if !config.SyncMode.IsValid() {
|
if !config.SyncMode.IsValid() {
|
||||||
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
|
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
|
||||||
}
|
}
|
||||||
if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 {
|
if config.Miner.GasPrice == nil || config.Miner.GasPrice.Sign() <= 0 {
|
||||||
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
|
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
|
||||||
config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
|
config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis) (*node.
|
||||||
// send enough transactions to fill multiple blocks
|
// send enough transactions to fill multiple blocks
|
||||||
func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
|
func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
|
||||||
var withdrawals []types.Withdrawal
|
var withdrawals []types.Withdrawal
|
||||||
txs := make(map[common.Hash]types.Transaction)
|
txs := make(map[common.Hash]*types.Transaction)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// testKey is a private key to use for funding a tester account.
|
// testKey is a private key to use for funding a tester account.
|
||||||
|
|
@ -110,7 +110,7 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error signing transaction, err=%v", err)
|
t.Fatalf("error signing transaction, err=%v", err)
|
||||||
}
|
}
|
||||||
txs[tx.Hash()] = *tx
|
txs[tx.Hash()] = tx
|
||||||
|
|
||||||
if err := ethService.APIBackend.SendTx(context.Background(), tx); err != nil {
|
if err := ethService.APIBackend.SendTx(context.Background(), tx); err != nil {
|
||||||
t.Fatal("SendTx failed", err)
|
t.Fatal("SendTx failed", err)
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,9 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
|
||||||
localHeaders = d.readHeaderRange(tail, int(count))
|
localHeaders = d.readHeaderRange(tail, int(count))
|
||||||
log.Warn("Retrieved beacon headers from local", "from", from, "count", count)
|
log.Warn("Retrieved beacon headers from local", "from", from, "count", count)
|
||||||
}
|
}
|
||||||
|
fsHeaderContCheckTimer := time.NewTimer(fsHeaderContCheck)
|
||||||
|
defer fsHeaderContCheckTimer.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Some beacon headers might have appeared since the last cycle, make
|
// Some beacon headers might have appeared since the last cycle, make
|
||||||
// sure we're always syncing to all available ones
|
// sure we're always syncing to all available ones
|
||||||
|
|
@ -381,8 +384,9 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
|
||||||
}
|
}
|
||||||
// State sync still going, wait a bit for new headers and retry
|
// State sync still going, wait a bit for new headers and retry
|
||||||
log.Trace("Pivot not yet committed, waiting...")
|
log.Trace("Pivot not yet committed, waiting...")
|
||||||
|
fsHeaderContCheckTimer.Reset(fsHeaderContCheck)
|
||||||
select {
|
select {
|
||||||
case <-time.After(fsHeaderContCheck):
|
case <-fsHeaderContCheckTimer.C:
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
return errCanceled
|
return errCanceled
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1276,7 +1276,10 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
||||||
var (
|
var (
|
||||||
mode = d.getMode()
|
mode = d.getMode()
|
||||||
gotHeaders = false // Wait for batches of headers to process
|
gotHeaders = false // Wait for batches of headers to process
|
||||||
|
timer = time.NewTimer(time.Second)
|
||||||
)
|
)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
|
|
@ -1397,10 +1400,11 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode
|
||||||
if mode == FullSync || mode == SnapSync {
|
if mode == FullSync || mode == SnapSync {
|
||||||
// If we've reached the allowed number of pending headers, stall a bit
|
// If we've reached the allowed number of pending headers, stall a bit
|
||||||
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
|
for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
|
||||||
|
timer.Reset(time.Second)
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
return errCanceled
|
return errCanceled
|
||||||
case <-time.After(time.Second):
|
case <-timer.C:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise insert the headers for content retrieval
|
// Otherwise insert the headers for content retrieval
|
||||||
|
|
@ -1567,7 +1571,10 @@ func (d *Downloader) processSnapSyncContent() error {
|
||||||
var (
|
var (
|
||||||
oldPivot *fetchResult // Locked in pivot block, might change eventually
|
oldPivot *fetchResult // Locked in pivot block, might change eventually
|
||||||
oldTail []*fetchResult // Downloaded content after the pivot
|
oldTail []*fetchResult // Downloaded content after the pivot
|
||||||
|
timer = time.NewTimer(time.Second)
|
||||||
)
|
)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Wait for the next batch of downloaded data to be available. If we have
|
// Wait for the next batch of downloaded data to be available. If we have
|
||||||
// not yet reached the pivot point, wait blockingly as there's no need to
|
// not yet reached the pivot point, wait blockingly as there's no need to
|
||||||
|
|
@ -1650,6 +1657,7 @@ func (d *Downloader) processSnapSyncContent() error {
|
||||||
oldPivot = P
|
oldPivot = P
|
||||||
}
|
}
|
||||||
// Wait for completion, occasionally checking for pivot staleness
|
// Wait for completion, occasionally checking for pivot staleness
|
||||||
|
timer.Reset(time.Second)
|
||||||
select {
|
select {
|
||||||
case <-sync.done:
|
case <-sync.done:
|
||||||
if sync.err != nil {
|
if sync.err != nil {
|
||||||
|
|
@ -1660,7 +1668,7 @@ func (d *Downloader) processSnapSyncContent() error {
|
||||||
}
|
}
|
||||||
oldPivot = nil
|
oldPivot = nil
|
||||||
|
|
||||||
case <-time.After(time.Second):
|
case <-timer.C:
|
||||||
oldTail = afterP
|
oldTail = afterP
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
|
|
@ -347,16 +348,6 @@ func (f *Filter) pendingLogs() []*types.Log {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// includes returns true if the element is present in the list.
|
|
||||||
func includes[T comparable](things []T, element T) bool {
|
|
||||||
for _, thing := range things {
|
|
||||||
if thing == element {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// filterLogs creates a slice of logs matching the given criteria.
|
// filterLogs creates a slice of logs matching the given criteria.
|
||||||
func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
|
func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
|
||||||
var check = func(log *types.Log) bool {
|
var check = func(log *types.Log) bool {
|
||||||
|
|
@ -366,7 +357,7 @@ func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []comm
|
||||||
if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
|
if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(addresses) > 0 && !includes(addresses, log.Address) {
|
if len(addresses) > 0 && !slices.Contains(addresses, log.Address) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// If the to filtered topics is greater than the amount of topics in logs, skip.
|
// If the to filtered topics is greater than the amount of topics in logs, skip.
|
||||||
|
|
@ -377,7 +368,7 @@ func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []comm
|
||||||
if len(sub) == 0 {
|
if len(sub) == 0 {
|
||||||
continue // empty rule set == wildcard
|
continue // empty rule set == wildcard
|
||||||
}
|
}
|
||||||
if !includes(sub, log.Topics[i]) {
|
if !slices.Contains(sub, log.Topics[i]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,4 +54,9 @@ var (
|
||||||
// skipStorageHealingGauge is the metric to track how many storages are retrieved
|
// skipStorageHealingGauge is the metric to track how many storages are retrieved
|
||||||
// in multiple requests but healing is not necessary.
|
// in multiple requests but healing is not necessary.
|
||||||
skipStorageHealingGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/noheal", nil)
|
skipStorageHealingGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/noheal", nil)
|
||||||
|
|
||||||
|
// largeStorageDiscardGauge is the metric to track how many chunked storages are
|
||||||
|
// discarded during the snap sync.
|
||||||
|
largeStorageDiscardGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/chunk/discard", nil)
|
||||||
|
largeStorageResumedGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/chunk/resume", nil)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
154
eth/protocols/snap/progress_test.go
Normal file
154
eth/protocols/snap/progress_test.go
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
// Copyright 2024 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 snap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Legacy sync progress definitions
|
||||||
|
type legacyStorageTask struct {
|
||||||
|
Next common.Hash // Next account to sync in this interval
|
||||||
|
Last common.Hash // Last account to sync in this interval
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyAccountTask struct {
|
||||||
|
Next common.Hash // Next account to sync in this interval
|
||||||
|
Last common.Hash // Last account to sync in this interval
|
||||||
|
SubTasks map[common.Hash][]*legacyStorageTask // Storage intervals needing fetching for large contracts
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyProgress struct {
|
||||||
|
Tasks []*legacyAccountTask // The suspended account tasks (contract tasks within)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareProgress(a legacyProgress, b SyncProgress) bool {
|
||||||
|
if len(a.Tasks) != len(b.Tasks) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(a.Tasks); i++ {
|
||||||
|
if a.Tasks[i].Next != b.Tasks[i].Next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Tasks[i].Last != b.Tasks[i].Last {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// new fields are not checked here
|
||||||
|
|
||||||
|
if len(a.Tasks[i].SubTasks) != len(b.Tasks[i].SubTasks) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for addrHash, subTasksA := range a.Tasks[i].SubTasks {
|
||||||
|
subTasksB, ok := b.Tasks[i].SubTasks[addrHash]
|
||||||
|
if !ok || len(subTasksB) != len(subTasksA) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for j := 0; j < len(subTasksA); j++ {
|
||||||
|
if subTasksA[j].Next != subTasksB[j].Next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if subTasksA[j].Last != subTasksB[j].Last {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLegacyProgress() legacyProgress {
|
||||||
|
return legacyProgress{
|
||||||
|
Tasks: []*legacyAccountTask{
|
||||||
|
{
|
||||||
|
Next: common.Hash{},
|
||||||
|
Last: common.Hash{0x77},
|
||||||
|
SubTasks: map[common.Hash][]*legacyStorageTask{
|
||||||
|
common.Hash{0x1}: {
|
||||||
|
{
|
||||||
|
Next: common.Hash{},
|
||||||
|
Last: common.Hash{0xff},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Next: common.Hash{0x88},
|
||||||
|
Last: common.Hash{0xff},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertLegacy(legacy legacyProgress) SyncProgress {
|
||||||
|
var progress SyncProgress
|
||||||
|
for i, task := range legacy.Tasks {
|
||||||
|
subTasks := make(map[common.Hash][]*storageTask)
|
||||||
|
for owner, list := range task.SubTasks {
|
||||||
|
var cpy []*storageTask
|
||||||
|
for i := 0; i < len(list); i++ {
|
||||||
|
cpy = append(cpy, &storageTask{
|
||||||
|
Next: list[i].Next,
|
||||||
|
Last: list[i].Last,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
subTasks[owner] = cpy
|
||||||
|
}
|
||||||
|
accountTask := &accountTask{
|
||||||
|
Next: task.Next,
|
||||||
|
Last: task.Last,
|
||||||
|
SubTasks: subTasks,
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
accountTask.StorageCompleted = []common.Hash{{0xaa}, {0xbb}} // fulfill new fields
|
||||||
|
}
|
||||||
|
progress.Tasks = append(progress.Tasks, accountTask)
|
||||||
|
}
|
||||||
|
return progress
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncProgressCompatibility(t *testing.T) {
|
||||||
|
// Decode serialized bytes of legacy progress, backward compatibility
|
||||||
|
legacy := makeLegacyProgress()
|
||||||
|
blob, err := json.Marshal(legacy)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to marshal progress %v", err)
|
||||||
|
}
|
||||||
|
var dec SyncProgress
|
||||||
|
if err := json.Unmarshal(blob, &dec); err != nil {
|
||||||
|
t.Fatalf("Failed to unmarshal progress %v", err)
|
||||||
|
}
|
||||||
|
if !compareProgress(legacy, dec) {
|
||||||
|
t.Fatal("sync progress is not backward compatible")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode serialized bytes of new format progress
|
||||||
|
progress := convertLegacy(legacy)
|
||||||
|
blob, err = json.Marshal(progress)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to marshal progress %v", err)
|
||||||
|
}
|
||||||
|
var legacyDec legacyProgress
|
||||||
|
if err := json.Unmarshal(blob, &legacyDec); err != nil {
|
||||||
|
t.Fatalf("Failed to unmarshal progress %v", err)
|
||||||
|
}
|
||||||
|
if !compareProgress(legacyDec, progress) {
|
||||||
|
t.Fatal("sync progress is not forward compatible")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -295,11 +295,19 @@ type bytecodeHealResponse struct {
|
||||||
|
|
||||||
// accountTask represents the sync task for a chunk of the account snapshot.
|
// accountTask represents the sync task for a chunk of the account snapshot.
|
||||||
type accountTask struct {
|
type accountTask struct {
|
||||||
// These fields get serialized to leveldb on shutdown
|
// These fields get serialized to key-value store on shutdown
|
||||||
Next common.Hash // Next account to sync in this interval
|
Next common.Hash // Next account to sync in this interval
|
||||||
Last common.Hash // Last account to sync in this interval
|
Last common.Hash // Last account to sync in this interval
|
||||||
SubTasks map[common.Hash][]*storageTask // Storage intervals needing fetching for large contracts
|
SubTasks map[common.Hash][]*storageTask // Storage intervals needing fetching for large contracts
|
||||||
|
|
||||||
|
// This is a list of account hashes whose storage are already completed
|
||||||
|
// in this cycle. This field is newly introduced in v1.14 and will be
|
||||||
|
// empty if the task is resolved from legacy progress data. Furthermore,
|
||||||
|
// this additional field will be ignored by legacy Geth. The only side
|
||||||
|
// effect is that these contracts might be resynced in the new cycle,
|
||||||
|
// retaining the legacy behavior.
|
||||||
|
StorageCompleted []common.Hash `json:",omitempty"`
|
||||||
|
|
||||||
// These fields are internals used during runtime
|
// These fields are internals used during runtime
|
||||||
req *accountRequest // Pending request to fill this task
|
req *accountRequest // Pending request to fill this task
|
||||||
res *accountResponse // Validate response filling this task
|
res *accountResponse // Validate response filling this task
|
||||||
|
|
@ -311,6 +319,7 @@ type accountTask struct {
|
||||||
|
|
||||||
codeTasks map[common.Hash]struct{} // Code hashes that need retrieval
|
codeTasks map[common.Hash]struct{} // Code hashes that need retrieval
|
||||||
stateTasks map[common.Hash]common.Hash // Account hashes->roots that need full state retrieval
|
stateTasks map[common.Hash]common.Hash // Account hashes->roots that need full state retrieval
|
||||||
|
stateCompleted map[common.Hash]struct{} // Account hashes whose storage have been completed
|
||||||
|
|
||||||
genBatch ethdb.Batch // Batch used by the node generator
|
genBatch ethdb.Batch // Batch used by the node generator
|
||||||
genTrie *trie.StackTrie // Node generator from storage slots
|
genTrie *trie.StackTrie // Node generator from storage slots
|
||||||
|
|
@ -318,6 +327,30 @@ type accountTask struct {
|
||||||
done bool // Flag whether the task can be removed
|
done bool // Flag whether the task can be removed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// activeSubTasks returns the set of storage tasks covered by the current account
|
||||||
|
// range. Normally this would be the entire subTask set, but on a sync interrupt
|
||||||
|
// and later resume it can happen that a shorter account range is retrieved. This
|
||||||
|
// method ensures that we only start up the subtasks covered by the latest account
|
||||||
|
// response.
|
||||||
|
//
|
||||||
|
// Nil is returned if the account range is empty.
|
||||||
|
func (task *accountTask) activeSubTasks() map[common.Hash][]*storageTask {
|
||||||
|
if len(task.res.hashes) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
tasks = make(map[common.Hash][]*storageTask)
|
||||||
|
last = task.res.hashes[len(task.res.hashes)-1]
|
||||||
|
)
|
||||||
|
for hash, subTasks := range task.SubTasks {
|
||||||
|
subTasks := subTasks // closure
|
||||||
|
if hash.Cmp(last) <= 0 {
|
||||||
|
tasks[hash] = subTasks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tasks
|
||||||
|
}
|
||||||
|
|
||||||
// storageTask represents the sync task for a chunk of the storage snapshot.
|
// storageTask represents the sync task for a chunk of the storage snapshot.
|
||||||
type storageTask struct {
|
type storageTask struct {
|
||||||
Next common.Hash // Next account to sync in this interval
|
Next common.Hash // Next account to sync in this interval
|
||||||
|
|
@ -745,6 +778,14 @@ func (s *Syncer) loadSyncStatus() {
|
||||||
for _, task := range s.tasks {
|
for _, task := range s.tasks {
|
||||||
task := task // closure for task.genBatch in the stacktrie writer callback
|
task := task // closure for task.genBatch in the stacktrie writer callback
|
||||||
|
|
||||||
|
// Restore the completed storages
|
||||||
|
task.stateCompleted = make(map[common.Hash]struct{})
|
||||||
|
for _, hash := range task.StorageCompleted {
|
||||||
|
task.stateCompleted[hash] = struct{}{}
|
||||||
|
}
|
||||||
|
task.StorageCompleted = nil
|
||||||
|
|
||||||
|
// Allocate batch for account trie generation
|
||||||
task.genBatch = ethdb.HookedBatch{
|
task.genBatch = ethdb.HookedBatch{
|
||||||
Batch: s.db.NewBatch(),
|
Batch: s.db.NewBatch(),
|
||||||
OnPut: func(key []byte, value []byte) {
|
OnPut: func(key []byte, value []byte) {
|
||||||
|
|
@ -767,6 +808,8 @@ func (s *Syncer) loadSyncStatus() {
|
||||||
options = options.WithSkipBoundary(task.Next != (common.Hash{}), task.Last != common.MaxHash, boundaryAccountNodesGauge)
|
options = options.WithSkipBoundary(task.Next != (common.Hash{}), task.Last != common.MaxHash, boundaryAccountNodesGauge)
|
||||||
}
|
}
|
||||||
task.genTrie = trie.NewStackTrie(options)
|
task.genTrie = trie.NewStackTrie(options)
|
||||||
|
|
||||||
|
// Restore leftover storage tasks
|
||||||
for accountHash, subtasks := range task.SubTasks {
|
for accountHash, subtasks := range task.SubTasks {
|
||||||
for _, subtask := range subtasks {
|
for _, subtask := range subtasks {
|
||||||
subtask := subtask // closure for subtask.genBatch in the stacktrie writer callback
|
subtask := subtask // closure for subtask.genBatch in the stacktrie writer callback
|
||||||
|
|
@ -865,6 +908,7 @@ func (s *Syncer) loadSyncStatus() {
|
||||||
Last: last,
|
Last: last,
|
||||||
SubTasks: make(map[common.Hash][]*storageTask),
|
SubTasks: make(map[common.Hash][]*storageTask),
|
||||||
genBatch: batch,
|
genBatch: batch,
|
||||||
|
stateCompleted: make(map[common.Hash]struct{}),
|
||||||
genTrie: trie.NewStackTrie(options),
|
genTrie: trie.NewStackTrie(options),
|
||||||
})
|
})
|
||||||
log.Debug("Created account sync task", "from", next, "last", last)
|
log.Debug("Created account sync task", "from", next, "last", last)
|
||||||
|
|
@ -886,6 +930,14 @@ func (s *Syncer) saveSyncStatus() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Save the account hashes of completed storage.
|
||||||
|
task.StorageCompleted = make([]common.Hash, 0, len(task.stateCompleted))
|
||||||
|
for hash := range task.stateCompleted {
|
||||||
|
task.StorageCompleted = append(task.StorageCompleted, hash)
|
||||||
|
}
|
||||||
|
if len(task.StorageCompleted) > 0 {
|
||||||
|
log.Debug("Leftover completed storages", "number", len(task.StorageCompleted), "next", task.Next, "last", task.Last)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Store the actual progress markers
|
// Store the actual progress markers
|
||||||
progress := &SyncProgress{
|
progress := &SyncProgress{
|
||||||
|
|
@ -970,6 +1022,10 @@ func (s *Syncer) cleanStorageTasks() {
|
||||||
delete(task.SubTasks, account)
|
delete(task.SubTasks, account)
|
||||||
task.pend--
|
task.pend--
|
||||||
|
|
||||||
|
// Mark the state as complete to prevent resyncing, regardless
|
||||||
|
// if state healing is necessary.
|
||||||
|
task.stateCompleted[account] = struct{}{}
|
||||||
|
|
||||||
// If this was the last pending task, forward the account task
|
// If this was the last pending task, forward the account task
|
||||||
if task.pend == 0 {
|
if task.pend == 0 {
|
||||||
s.forwardAccountTask(task)
|
s.forwardAccountTask(task)
|
||||||
|
|
@ -1209,7 +1265,8 @@ func (s *Syncer) assignStorageTasks(success chan *storageResponse, fail chan *st
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Skip tasks that are already retrieving (or done with) all small states
|
// Skip tasks that are already retrieving (or done with) all small states
|
||||||
if len(task.SubTasks) == 0 && len(task.stateTasks) == 0 {
|
storageTasks := task.activeSubTasks()
|
||||||
|
if len(storageTasks) == 0 && len(task.stateTasks) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Task pending retrieval, try to find an idle peer. If no such peer
|
// Task pending retrieval, try to find an idle peer. If no such peer
|
||||||
|
|
@ -1253,7 +1310,7 @@ func (s *Syncer) assignStorageTasks(success chan *storageResponse, fail chan *st
|
||||||
roots = make([]common.Hash, 0, storageSets)
|
roots = make([]common.Hash, 0, storageSets)
|
||||||
subtask *storageTask
|
subtask *storageTask
|
||||||
)
|
)
|
||||||
for account, subtasks := range task.SubTasks {
|
for account, subtasks := range storageTasks {
|
||||||
for _, st := range subtasks {
|
for _, st := range subtasks {
|
||||||
// Skip any subtasks already filling
|
// Skip any subtasks already filling
|
||||||
if st.req != nil {
|
if st.req != nil {
|
||||||
|
|
@ -1850,11 +1907,11 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
|
||||||
res.task.res = res
|
res.task.res = res
|
||||||
|
|
||||||
// Ensure that the response doesn't overflow into the subsequent task
|
// Ensure that the response doesn't overflow into the subsequent task
|
||||||
last := res.task.Last.Big()
|
lastBig := res.task.Last.Big()
|
||||||
for i, hash := range res.hashes {
|
for i, hash := range res.hashes {
|
||||||
// Mark the range complete if the last is already included.
|
// Mark the range complete if the last is already included.
|
||||||
// Keep iteration to delete the extra states if exists.
|
// Keep iteration to delete the extra states if exists.
|
||||||
cmp := hash.Big().Cmp(last)
|
cmp := hash.Big().Cmp(lastBig)
|
||||||
if cmp == 0 {
|
if cmp == 0 {
|
||||||
res.cont = false
|
res.cont = false
|
||||||
continue
|
continue
|
||||||
|
|
@ -1890,7 +1947,21 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
|
||||||
}
|
}
|
||||||
// Check if the account is a contract with an unknown storage trie
|
// Check if the account is a contract with an unknown storage trie
|
||||||
if account.Root != types.EmptyRootHash {
|
if account.Root != types.EmptyRootHash {
|
||||||
|
// If the storage was already retrieved in the last cycle, there's no need
|
||||||
|
// to resync it again, regardless of whether the storage root is consistent
|
||||||
|
// or not.
|
||||||
|
if _, exist := res.task.stateCompleted[res.hashes[i]]; exist {
|
||||||
|
// The leftover storage tasks are not expected, unless system is
|
||||||
|
// very wrong.
|
||||||
|
if _, ok := res.task.SubTasks[res.hashes[i]]; ok {
|
||||||
|
panic(fmt.Errorf("unexpected leftover storage tasks, owner: %x", res.hashes[i]))
|
||||||
|
}
|
||||||
|
// Mark the healing tag if storage root node is inconsistent, or
|
||||||
|
// it's non-existent due to storage chunking.
|
||||||
if !rawdb.HasTrieNode(s.db, res.hashes[i], nil, account.Root, s.scheme) {
|
if !rawdb.HasTrieNode(s.db, res.hashes[i], nil, account.Root, s.scheme) {
|
||||||
|
res.task.needHeal[i] = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// If there was a previous large state retrieval in progress,
|
// If there was a previous large state retrieval in progress,
|
||||||
// don't restart it from scratch. This happens if a sync cycle
|
// don't restart it from scratch. This happens if a sync cycle
|
||||||
// is interrupted and resumed later. However, *do* update the
|
// is interrupted and resumed later. However, *do* update the
|
||||||
|
|
@ -1902,7 +1973,12 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
|
||||||
}
|
}
|
||||||
res.task.needHeal[i] = true
|
res.task.needHeal[i] = true
|
||||||
resumed[res.hashes[i]] = struct{}{}
|
resumed[res.hashes[i]] = struct{}{}
|
||||||
|
largeStorageResumedGauge.Inc(1)
|
||||||
} else {
|
} else {
|
||||||
|
// It's possible that in the hash scheme, the storage, along
|
||||||
|
// with the trie nodes of the given root, is already present
|
||||||
|
// in the database. Schedule the storage task anyway to simplify
|
||||||
|
// the logic here.
|
||||||
res.task.stateTasks[res.hashes[i]] = account.Root
|
res.task.stateTasks[res.hashes[i]] = account.Root
|
||||||
}
|
}
|
||||||
res.task.needState[i] = true
|
res.task.needState[i] = true
|
||||||
|
|
@ -1910,13 +1986,29 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delete any subtasks that have been aborted but not resumed. This may undo
|
// Delete any subtasks that have been aborted but not resumed. It's essential
|
||||||
// some progress if a new peer gives us less accounts than an old one, but for
|
// as the corresponding contract might be self-destructed in this cycle(it's
|
||||||
// now we have to live with that.
|
// no longer possible in ethereum as self-destruction is disabled in Cancun
|
||||||
|
// Fork, but the condition is still necessary for other networks).
|
||||||
|
//
|
||||||
|
// Keep the leftover storage tasks if they are not covered by the responded
|
||||||
|
// account range which should be picked up in next account wave.
|
||||||
|
if len(res.hashes) > 0 {
|
||||||
|
// The hash of last delivered account in the response
|
||||||
|
last := res.hashes[len(res.hashes)-1]
|
||||||
for hash := range res.task.SubTasks {
|
for hash := range res.task.SubTasks {
|
||||||
|
// TODO(rjl493456442) degrade the log level before merging.
|
||||||
|
if hash.Cmp(last) > 0 {
|
||||||
|
log.Info("Keeping suspended storage retrieval", "account", hash)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO(rjl493456442) degrade the log level before merging.
|
||||||
|
// It should never happen in ethereum.
|
||||||
if _, ok := resumed[hash]; !ok {
|
if _, ok := resumed[hash]; !ok {
|
||||||
log.Debug("Aborting suspended storage retrieval", "account", hash)
|
log.Error("Aborting suspended storage retrieval", "account", hash)
|
||||||
delete(res.task.SubTasks, hash)
|
delete(res.task.SubTasks, hash)
|
||||||
|
largeStorageDiscardGauge.Inc(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the account range contained no contracts, or all have been fully filled
|
// If the account range contained no contracts, or all have been fully filled
|
||||||
|
|
@ -2014,6 +2106,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
if res.subTask == nil && res.mainTask.needState[j] && (i < len(res.hashes)-1 || !res.cont) {
|
if res.subTask == nil && res.mainTask.needState[j] && (i < len(res.hashes)-1 || !res.cont) {
|
||||||
res.mainTask.needState[j] = false
|
res.mainTask.needState[j] = false
|
||||||
res.mainTask.pend--
|
res.mainTask.pend--
|
||||||
|
res.mainTask.stateCompleted[account] = struct{}{} // mark it as completed
|
||||||
smallStorageGauge.Inc(1)
|
smallStorageGauge.Inc(1)
|
||||||
}
|
}
|
||||||
// If the last contract was chunked, mark it as needing healing
|
// If the last contract was chunked, mark it as needing healing
|
||||||
|
|
@ -2409,10 +2502,19 @@ func (s *Syncer) forwardAccountTask(task *accountTask) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
task.Next = incHash(hash)
|
task.Next = incHash(hash)
|
||||||
|
|
||||||
|
// Remove the completion flag once the account range is pushed
|
||||||
|
// forward. The leftover accounts will be skipped in the next
|
||||||
|
// cycle.
|
||||||
|
delete(task.stateCompleted, hash)
|
||||||
}
|
}
|
||||||
// All accounts marked as complete, track if the entire task is done
|
// All accounts marked as complete, track if the entire task is done
|
||||||
task.done = !res.cont
|
task.done = !res.cont
|
||||||
|
|
||||||
|
// Error out if there is any leftover completion flag.
|
||||||
|
if task.done && len(task.stateCompleted) != 0 {
|
||||||
|
panic(fmt.Errorf("storage completion flags should be emptied, %d left", len(task.stateCompleted)))
|
||||||
|
}
|
||||||
// Stack trie could have generated trie nodes, push them to disk (we need to
|
// Stack trie could have generated trie nodes, push them to disk (we need to
|
||||||
// flush after finalizing task.done. It's fine even if we crash and lose this
|
// flush after finalizing task.done. It's fine even if we crash and lose this
|
||||||
// write as it will only cause more data to be downloaded during heal.
|
// write as it will only cause more data to be downloaded during heal.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
"gas": "0x1a034",
|
"gas": "0x1a034",
|
||||||
"init": "0x36600060003760406103e8366000600060095af26001556103e8516002556104085160035500"
|
"init": "0x36600060003760406103e8366000600060095af26001556103e8516002556104085160035500"
|
||||||
},
|
},
|
||||||
"error": "out of gas",
|
"error": "out of gas: not enough gas for reentrancy sentry",
|
||||||
"traceAddress": [],
|
"traceAddress": [],
|
||||||
"subtraces": 1,
|
"subtraces": 1,
|
||||||
"transactionPosition": 117,
|
"transactionPosition": 117,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
"gas": "0x19ee4",
|
"gas": "0x19ee4",
|
||||||
"init": "0x5a600055600060006000f0505a60015500"
|
"init": "0x5a600055600060006000f0505a60015500"
|
||||||
},
|
},
|
||||||
"error": "out of gas",
|
"error": "out of gas: not enough gas for reentrancy sentry",
|
||||||
"traceAddress": [],
|
"traceAddress": [],
|
||||||
"subtraces": 1,
|
"subtraces": 1,
|
||||||
"transactionPosition": 63,
|
"transactionPosition": 63,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -529,13 +530,7 @@ func (t *jsTracer) setBuiltinFunctions() {
|
||||||
vm.Interrupt(err)
|
vm.Interrupt(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
addr := common.BytesToAddress(a)
|
return slices.Contains(t.activePrecompiles, common.BytesToAddress(a))
|
||||||
for _, p := range t.activePrecompiles {
|
|
||||||
if p == addr {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
})
|
||||||
vm.Set("slice", func(slice goja.Value, start, end int64) goja.Value {
|
vm.Set("slice", func(slice goja.Value, start, end int64) goja.Value {
|
||||||
b, err := t.fromBuf(vm, slice, false)
|
b, err := t.fromBuf(vm, slice, false)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -228,12 +229,7 @@ func (t *flatCallTracer) Stop(err error) {
|
||||||
|
|
||||||
// isPrecompiled returns whether the addr is a precompile.
|
// isPrecompiled returns whether the addr is a precompile.
|
||||||
func (t *flatCallTracer) isPrecompiled(addr common.Address) bool {
|
func (t *flatCallTracer) isPrecompiled(addr common.Address) bool {
|
||||||
for _, p := range t.activePrecompiles {
|
return slices.Contains(t.activePrecompiles, addr)
|
||||||
if p == addr {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) {
|
func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) {
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ func testGetProofNonExistent(t *testing.T, client *rpc.Client) {
|
||||||
t.Fatalf("invalid nonce, want: %v got: %v", 0, result.Nonce)
|
t.Fatalf("invalid nonce, want: %v got: %v", 0, result.Nonce)
|
||||||
}
|
}
|
||||||
// test balance
|
// test balance
|
||||||
if result.Balance.Cmp(big.NewInt(0)) != 0 {
|
if result.Balance.Sign() != 0 {
|
||||||
t.Fatalf("invalid balance, want: %v got: %v", 0, result.Balance)
|
t.Fatalf("invalid balance, want: %v got: %v", 0, result.Balance)
|
||||||
}
|
}
|
||||||
// test storage
|
// test storage
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func WithCallGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethc
|
||||||
// 0 is not possible as a live Geth node would reject that due to DoS protection,
|
// 0 is not possible as a live Geth node would reject that due to DoS protection,
|
||||||
// so the simulated backend will replicate that behavior for consistency.
|
// so the simulated backend will replicate that behavior for consistency.
|
||||||
func WithMinerMinTip(tip *big.Int) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
|
func WithMinerMinTip(tip *big.Int) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
|
||||||
if tip == nil || tip.Cmp(new(big.Int)) <= 0 {
|
if tip == nil || tip.Sign() <= 0 {
|
||||||
panic("invalid miner minimum tip")
|
panic("invalid miner minimum tip")
|
||||||
}
|
}
|
||||||
return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
|
return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
|
||||||
|
|
|
||||||
|
|
@ -544,10 +544,13 @@ func (s *Service) reportLatency(conn *connWrapper) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Wait for the pong request to arrive back
|
// Wait for the pong request to arrive back
|
||||||
|
timer := time.NewTimer(5 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.pongCh:
|
case <-s.pongCh:
|
||||||
// Pong delivered, report the latency
|
// Pong delivered, report the latency
|
||||||
case <-time.After(5 * time.Second):
|
case <-timer.C:
|
||||||
// Ping timeout, abort
|
// Ping timeout, abort
|
||||||
return errors.New("ping timed out")
|
return errors.New("ping timed out")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -112,7 +111,7 @@ func RunGit(args ...string) string {
|
||||||
|
|
||||||
// readGitFile returns content of file in .git directory.
|
// readGitFile returns content of file in .git directory.
|
||||||
func readGitFile(file string) string {
|
func readGitFile(file string) string {
|
||||||
content, err := os.ReadFile(path.Join(".git", file))
|
content, err := os.ReadFile(filepath.Join(".git", file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +174,7 @@ func UploadSFTP(identityFile, host, dir string, files []string) error {
|
||||||
}
|
}
|
||||||
in := io.MultiWriter(stdin, os.Stdout)
|
in := io.MultiWriter(stdin, os.Stdout)
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
fmt.Fprintln(in, "put", f, path.Join(dir, filepath.Base(f)))
|
fmt.Fprintln(in, "put", f, filepath.Join(dir, filepath.Base(f)))
|
||||||
}
|
}
|
||||||
fmt.Fprintln(in, "exit")
|
fmt.Fprintln(in, "exit")
|
||||||
// Some issue with the PPA sftp server makes it so the server does not
|
// Some issue with the PPA sftp server makes it so the server does not
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -57,7 +56,7 @@ type HandlerT struct {
|
||||||
// Verbosity sets the log verbosity ceiling. The verbosity of individual packages
|
// Verbosity sets the log verbosity ceiling. The verbosity of individual packages
|
||||||
// and source files can be raised using Vmodule.
|
// and source files can be raised using Vmodule.
|
||||||
func (*HandlerT) Verbosity(level int) {
|
func (*HandlerT) Verbosity(level int) {
|
||||||
glogger.Verbosity(slog.Level(level))
|
glogger.Verbosity(log.FromLegacyLevel(level))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vmodule sets the log verbosity pattern. See package log for details on the
|
// Vmodule sets the log verbosity pattern. See package log for details on the
|
||||||
|
|
|
||||||
|
|
@ -231,9 +231,9 @@ func Setup(ctx *cli.Context) error {
|
||||||
case ctx.Bool(logjsonFlag.Name):
|
case ctx.Bool(logjsonFlag.Name):
|
||||||
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
|
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
|
||||||
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
|
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
|
||||||
handler = log.JSONHandler(output)
|
handler = log.JSONHandlerWithLevel(output, log.LevelInfo)
|
||||||
case logFmtFlag == "json":
|
case logFmtFlag == "json":
|
||||||
handler = log.JSONHandler(output)
|
handler = log.JSONHandlerWithLevel(output, log.LevelInfo)
|
||||||
case logFmtFlag == "logfmt":
|
case logFmtFlag == "logfmt":
|
||||||
handler = log.LogfmtHandler(output)
|
handler = log.LogfmtHandler(output)
|
||||||
case logFmtFlag == "", logFmtFlag == "terminal":
|
case logFmtFlag == "", logFmtFlag == "terminal":
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package era
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -80,7 +79,7 @@ func (it *Iterator) Block() (*types.Block, error) {
|
||||||
// Receipts returns the receipts for the iterator's current position.
|
// Receipts returns the receipts for the iterator's current position.
|
||||||
func (it *Iterator) Receipts() (types.Receipts, error) {
|
func (it *Iterator) Receipts() (types.Receipts, error) {
|
||||||
if it.inner.Receipts == nil {
|
if it.inner.Receipts == nil {
|
||||||
return nil, fmt.Errorf("receipts must be non-nil")
|
return nil, errors.New("receipts must be non-nil")
|
||||||
}
|
}
|
||||||
var receipts types.Receipts
|
var receipts types.Receipts
|
||||||
err := rlp.Decode(it.inner.Receipts, &receipts)
|
err := rlp.Decode(it.inner.Receipts, &receipts)
|
||||||
|
|
|
||||||
|
|
@ -1497,7 +1497,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
} else {
|
} else {
|
||||||
to = crypto.CreateAddress(args.from(), uint64(*args.Nonce))
|
to = crypto.CreateAddress(args.from(), uint64(*args.Nonce))
|
||||||
}
|
}
|
||||||
isPostMerge := header.Difficulty.Cmp(common.Big0) == 0
|
isPostMerge := header.Difficulty.Sign() == 0
|
||||||
// Retrieve the precompiles since they don't need to be added to the access list
|
// Retrieve the precompiles since they don't need to be added to the access list
|
||||||
precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge, header.Time))
|
precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge, header.Time))
|
||||||
|
|
||||||
|
|
@ -1865,15 +1865,14 @@ type SignTransactionResult struct {
|
||||||
// The node needs to have the private key of the account corresponding with
|
// The node needs to have the private key of the account corresponding with
|
||||||
// the given from address and it needs to be unlocked.
|
// the given from address and it needs to be unlocked.
|
||||||
func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
|
func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
|
||||||
|
args.blobSidecarAllowed = true
|
||||||
|
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
return nil, errors.New("gas not specified")
|
return nil, errors.New("gas not specified")
|
||||||
}
|
}
|
||||||
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
|
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
|
||||||
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
||||||
}
|
}
|
||||||
if args.IsEIP4844() {
|
|
||||||
return nil, errBlobTxNotSupported
|
|
||||||
}
|
|
||||||
if args.Nonce == nil {
|
if args.Nonce == nil {
|
||||||
return nil, errors.New("nonce not specified")
|
return nil, errors.New("nonce not specified")
|
||||||
}
|
}
|
||||||
|
|
@ -1889,6 +1888,16 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// If the transaction-to-sign was a blob transaction, then the signed one
|
||||||
|
// no longer retains the blobs, only the blob hashes. In this step, we need
|
||||||
|
// to put back the blob(s).
|
||||||
|
if args.IsEIP4844() {
|
||||||
|
signed = signed.WithBlobTxSidecar(&types.BlobTxSidecar{
|
||||||
|
Blobs: args.Blobs,
|
||||||
|
Commitments: args.Commitments,
|
||||||
|
Proofs: args.Proofs,
|
||||||
|
})
|
||||||
|
}
|
||||||
data, err := signed.MarshalBinary()
|
data, err := signed.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -1037,11 +1037,8 @@ func TestSignBlobTransaction(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
|
_, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
|
||||||
if err == nil {
|
if err != nil {
|
||||||
t.Fatalf("should fail on blob transaction")
|
t.Fatalf("should not fail on blob transaction")
|
||||||
}
|
|
||||||
if !errors.Is(err, errBlobTxNotSupported) {
|
|
||||||
t.Errorf("error mismatch. Have: %v, want: %v", err, errBlobTxNotSupported)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ func (args *TransactionArgs) data() []byte {
|
||||||
|
|
||||||
// setDefaults fills in default values for unspecified tx fields.
|
// setDefaults fills in default values for unspecified tx fields.
|
||||||
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error {
|
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error {
|
||||||
if err := args.setBlobTxSidecar(ctx, b); err != nil {
|
if err := args.setBlobTxSidecar(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := args.setFeeDefaults(ctx, b); err != nil {
|
if err := args.setFeeDefaults(ctx, b); err != nil {
|
||||||
|
|
@ -290,7 +290,7 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ
|
||||||
}
|
}
|
||||||
|
|
||||||
// setBlobTxSidecar adds the blob tx
|
// setBlobTxSidecar adds the blob tx
|
||||||
func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) error {
|
func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context) error {
|
||||||
// No blobs, we're done.
|
// No blobs, we're done.
|
||||||
if args.Blobs == nil {
|
if args.Blobs == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package jsre
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -42,7 +42,7 @@ func (no *testNativeObjectBinding) TestMethod(call goja.FunctionCall) goja.Value
|
||||||
func newWithTestJS(t *testing.T, testjs string) *JSRE {
|
func newWithTestJS(t *testing.T, testjs string) *JSRE {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
if testjs != "" {
|
if testjs != "" {
|
||||||
if err := os.WriteFile(path.Join(dir, "test.js"), []byte(testjs), os.ModePerm); err != nil {
|
if err := os.WriteFile(filepath.Join(dir, "test.js"), []byte(testjs), os.ModePerm); err != nil {
|
||||||
t.Fatal("cannot create test.js:", err)
|
t.Fatal("cannot create test.js:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,15 @@ func (l *leveler) Level() slog.Level {
|
||||||
|
|
||||||
// JSONHandler returns a handler which prints records in JSON format.
|
// JSONHandler returns a handler which prints records in JSON format.
|
||||||
func JSONHandler(wr io.Writer) slog.Handler {
|
func JSONHandler(wr io.Writer) slog.Handler {
|
||||||
|
return JSONHandlerWithLevel(wr, levelMaxVerbosity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONHandlerWithLevel returns a handler which prints records in JSON format that are less than or equal to
|
||||||
|
// the specified verbosity level.
|
||||||
|
func JSONHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler {
|
||||||
return slog.NewJSONHandler(wr, &slog.HandlerOptions{
|
return slog.NewJSONHandler(wr, &slog.HandlerOptions{
|
||||||
ReplaceAttr: builtinReplaceJSON,
|
ReplaceAttr: builtinReplaceJSON,
|
||||||
|
Level: &leveler{level},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"maps"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -145,10 +146,7 @@ func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool {
|
||||||
|
|
||||||
func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||||
h.lock.RLock()
|
h.lock.RLock()
|
||||||
siteCache := make(map[uintptr]slog.Level)
|
siteCache := maps.Clone(h.siteCache)
|
||||||
for k, v := range h.siteCache {
|
|
||||||
siteCache[k] = v
|
|
||||||
}
|
|
||||||
h.lock.RUnlock()
|
h.lock.RUnlock()
|
||||||
|
|
||||||
patterns := []pattern{}
|
patterns := []pattern{}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,25 @@ func TestTerminalHandlerWithAttrs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the default json handler outputs debug log lines
|
||||||
|
func TestJSONHandler(t *testing.T) {
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
handler := JSONHandler(out)
|
||||||
|
logger := slog.New(handler)
|
||||||
|
logger.Debug("hi there")
|
||||||
|
if len(out.String()) == 0 {
|
||||||
|
t.Error("expected non-empty debug log output from default JSON Handler")
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Reset()
|
||||||
|
handler = JSONHandlerWithLevel(out, slog.LevelInfo)
|
||||||
|
logger = slog.New(handler)
|
||||||
|
logger.Debug("hi there")
|
||||||
|
if len(out.String()) != 0 {
|
||||||
|
t.Errorf("expected empty debug log output, but got: %v", out.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkTraceLogging(b *testing.B) {
|
func BenchmarkTraceLogging(b *testing.B) {
|
||||||
SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true)))
|
SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ type newPayloadResult struct {
|
||||||
receipts []*types.Receipt // Receipts collected during construction
|
receipts []*types.Receipt // Receipts collected during construction
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateParams wraps various of settings for generating sealing task.
|
// generateParams wraps various settings for generating sealing task.
|
||||||
type generateParams struct {
|
type generateParams struct {
|
||||||
timestamp uint64 // The timestamp for sealing task
|
timestamp uint64 // The timestamp for sealing task
|
||||||
forceTime bool // Flag whether the given timestamp is immutable or not
|
forceTime bool // Flag whether the given timestamp is immutable or not
|
||||||
|
|
@ -131,7 +131,7 @@ func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error)
|
||||||
if genParams.parentHash != (common.Hash{}) {
|
if genParams.parentHash != (common.Hash{}) {
|
||||||
block := miner.chain.GetBlockByHash(genParams.parentHash)
|
block := miner.chain.GetBlockByHash(genParams.parentHash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, fmt.Errorf("missing parent")
|
return nil, errors.New("missing parent")
|
||||||
}
|
}
|
||||||
parent = block.Header()
|
parent = block.Header()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
node/node.go
13
node/node.go
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
|
@ -278,16 +279,6 @@ func (n *Node) openEndpoints() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// containsLifecycle checks if 'lfs' contains 'l'.
|
|
||||||
func containsLifecycle(lfs []Lifecycle, l Lifecycle) bool {
|
|
||||||
for _, obj := range lfs {
|
|
||||||
if obj == l {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopServices terminates running services, RPC and p2p networking.
|
// stopServices terminates running services, RPC and p2p networking.
|
||||||
// It is the inverse of Start.
|
// It is the inverse of Start.
|
||||||
func (n *Node) stopServices(running []Lifecycle) error {
|
func (n *Node) stopServices(running []Lifecycle) error {
|
||||||
|
|
@ -571,7 +562,7 @@ func (n *Node) RegisterLifecycle(lifecycle Lifecycle) {
|
||||||
if n.state != initializingState {
|
if n.state != initializingState {
|
||||||
panic("can't register lifecycle on running/stopped node")
|
panic("can't register lifecycle on running/stopped node")
|
||||||
}
|
}
|
||||||
if containsLifecycle(n.lifecycles, lifecycle) {
|
if slices.Contains(n.lifecycles, lifecycle) {
|
||||||
panic(fmt.Sprintf("attempt to register lifecycle %T more than once", lifecycle))
|
panic(fmt.Sprintf("attempt to register lifecycle %T more than once", lifecycle))
|
||||||
}
|
}
|
||||||
n.lifecycles = append(n.lifecycles, lifecycle)
|
n.lifecycles = append(n.lifecycles, lifecycle)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ func TestAuthEndpoints(t *testing.T) {
|
||||||
t.Fatalf("failed to create jwt secret: %v", err)
|
t.Fatalf("failed to create jwt secret: %v", err)
|
||||||
}
|
}
|
||||||
// Geth must read it from a file, and does not support in-memory JWT secrets, so we create a temporary file.
|
// Geth must read it from a file, and does not support in-memory JWT secrets, so we create a temporary file.
|
||||||
jwtPath := path.Join(t.TempDir(), "jwt_secret")
|
jwtPath := filepath.Join(t.TempDir(), "jwt_secret")
|
||||||
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
|
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
|
||||||
t.Fatalf("failed to prepare jwt secret file: %v", err)
|
t.Fatalf("failed to prepare jwt secret file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -116,7 +117,7 @@ func TestLifecycleRegistry_Successful(t *testing.T) {
|
||||||
noop := NewNoop()
|
noop := NewNoop()
|
||||||
stack.RegisterLifecycle(noop)
|
stack.RegisterLifecycle(noop)
|
||||||
|
|
||||||
if !containsLifecycle(stack.lifecycles, noop) {
|
if !slices.Contains(stack.lifecycles, Lifecycle(noop)) {
|
||||||
t.Fatalf("lifecycle was not properly registered on the node, %v", err)
|
t.Fatalf("lifecycle was not properly registered on the node, %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ func (tn *preminedTestnet) neighborsAtDistances(base *enode.Node, distances []ui
|
||||||
for i := range lookupTestnet.dists[d] {
|
for i := range lookupTestnet.dists[d] {
|
||||||
n := lookupTestnet.node(d, i)
|
n := lookupTestnet.node(d, i)
|
||||||
d := enode.LogDist(base.ID(), n.ID())
|
d := enode.LogDist(base.ID(), n.ID())
|
||||||
if containsUint(uint(d), distances) {
|
if slices.Contains(distances, uint(d)) {
|
||||||
result = append(result, n)
|
result = append(result, n)
|
||||||
if len(result) >= elems {
|
if len(result) >= elems {
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -437,7 +438,7 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s
|
||||||
}
|
}
|
||||||
if distances != nil {
|
if distances != nil {
|
||||||
nd := enode.LogDist(c.id, node.ID())
|
nd := enode.LogDist(c.id, node.ID())
|
||||||
if !containsUint(uint(nd), distances) {
|
if !slices.Contains(distances, uint(nd)) {
|
||||||
return nil, errors.New("does not match any requested distance")
|
return nil, errors.New("does not match any requested distance")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -448,15 +449,6 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func containsUint(x uint, xs []uint) bool {
|
|
||||||
for _, v := range xs {
|
|
||||||
if x == v {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// callToNode sends the given call and sets up a handler for response packets (of message
|
// callToNode sends the given call and sets up a handler for response packets (of message
|
||||||
// type responseType). Responses are dispatched to the call's response channel.
|
// type responseType). Responses are dispatched to the call's response channel.
|
||||||
func (t *UDPv5) callToNode(n *enode.Node, responseType byte, req v5wire.Packet) *callV5 {
|
func (t *UDPv5) callToNode(n *enode.Node, responseType byte, req v5wire.Packet) *callV5 {
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func TestIteratorNodeUpdates(t *testing.T) {
|
||||||
// Ensure RandomNode returns the new nodes after the tree is updated.
|
// Ensure RandomNode returns the new nodes after the tree is updated.
|
||||||
updateSomeNodes(keys, nodes)
|
updateSomeNodes(keys, nodes)
|
||||||
tree2, _ := makeTestTree("n", nodes, nil)
|
tree2, _ := makeTestTree("n", nodes, nil)
|
||||||
resolver.clear()
|
clear(resolver)
|
||||||
resolver.add(tree2.ToTXT("n"))
|
resolver.add(tree2.ToTXT("n"))
|
||||||
t.Log("tree updated")
|
t.Log("tree updated")
|
||||||
|
|
||||||
|
|
@ -256,7 +256,7 @@ func TestIteratorRootRecheckOnFail(t *testing.T) {
|
||||||
// Ensure RandomNode returns the new nodes after the tree is updated.
|
// Ensure RandomNode returns the new nodes after the tree is updated.
|
||||||
updateSomeNodes(keys, nodes)
|
updateSomeNodes(keys, nodes)
|
||||||
tree2, _ := makeTestTree("n", nodes, nil)
|
tree2, _ := makeTestTree("n", nodes, nil)
|
||||||
resolver.clear()
|
clear(resolver)
|
||||||
resolver.add(tree2.ToTXT("n"))
|
resolver.add(tree2.ToTXT("n"))
|
||||||
t.Log("tree updated")
|
t.Log("tree updated")
|
||||||
|
|
||||||
|
|
@ -447,12 +447,6 @@ func newMapResolver(maps ...map[string]string) mapResolver {
|
||||||
return mr
|
return mr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr mapResolver) clear() {
|
|
||||||
for k := range mr {
|
|
||||||
delete(mr, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mr mapResolver) add(m map[string]string) {
|
func (mr mapResolver) add(m map[string]string) {
|
||||||
maps.Copy(mr, m)
|
maps.Copy(mr, m)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -303,10 +303,13 @@ func (n *ExecNode) Stop() error {
|
||||||
go func() {
|
go func() {
|
||||||
waitErr <- n.Cmd.Wait()
|
waitErr <- n.Cmd.Wait()
|
||||||
}()
|
}()
|
||||||
|
timer := time.NewTimer(5 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case err := <-waitErr:
|
case err := <-waitErr:
|
||||||
return err
|
return err
|
||||||
case <-time.After(5 * time.Second):
|
case <-timer.C:
|
||||||
return n.Cmd.Process.Kill()
|
return n.Cmd.Process.Kill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,13 @@ func startStop(net *Network, quit chan struct{}, nodeCount int) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Could not startup node network for mocker")
|
panic("Could not startup node network for mocker")
|
||||||
}
|
}
|
||||||
tick := time.NewTicker(10 * time.Second)
|
var (
|
||||||
|
tick = time.NewTicker(10 * time.Second)
|
||||||
|
timer = time.NewTimer(3 * time.Second)
|
||||||
|
)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-quit:
|
case <-quit:
|
||||||
|
|
@ -80,11 +85,12 @@ func startStop(net *Network, quit chan struct{}, nodeCount int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timer.Reset(3 * time.Second)
|
||||||
select {
|
select {
|
||||||
case <-quit:
|
case <-quit:
|
||||||
log.Info("Terminating simulation loop")
|
log.Info("Terminating simulation loop")
|
||||||
return
|
return
|
||||||
case <-time.After(3 * time.Second):
|
case <-timer.C:
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("starting node", "id", id)
|
log.Debug("starting node", "id", id)
|
||||||
|
|
|
||||||
|
|
@ -1028,11 +1028,14 @@ func (net *Network) Load(snap *Snapshot) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeout := time.NewTimer(snapshotLoadTimeout)
|
||||||
|
defer timeout.Stop()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
// Wait until all connections from the snapshot are established.
|
// Wait until all connections from the snapshot are established.
|
||||||
case <-allConnected:
|
case <-allConnected:
|
||||||
// Make sure that we do not wait forever.
|
// Make sure that we do not wait forever.
|
||||||
case <-time.After(snapshotLoadTimeout):
|
case <-timeout.C:
|
||||||
return errors.New("snapshot connections not established")
|
return errors.New("snapshot connections not established")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -158,17 +158,17 @@ func makeDecoder(typ reflect.Type, tags rlpstruct.Tags) (dec decoder, err error)
|
||||||
switch {
|
switch {
|
||||||
case typ == rawValueType:
|
case typ == rawValueType:
|
||||||
return decodeRawValue, nil
|
return decodeRawValue, nil
|
||||||
case typ.AssignableTo(reflect.PtrTo(bigInt)):
|
case typ.AssignableTo(reflect.PointerTo(bigInt)):
|
||||||
return decodeBigInt, nil
|
return decodeBigInt, nil
|
||||||
case typ.AssignableTo(bigInt):
|
case typ.AssignableTo(bigInt):
|
||||||
return decodeBigIntNoPtr, nil
|
return decodeBigIntNoPtr, nil
|
||||||
case typ == reflect.PtrTo(u256Int):
|
case typ == reflect.PointerTo(u256Int):
|
||||||
return decodeU256, nil
|
return decodeU256, nil
|
||||||
case typ == u256Int:
|
case typ == u256Int:
|
||||||
return decodeU256NoPtr, nil
|
return decodeU256NoPtr, nil
|
||||||
case kind == reflect.Ptr:
|
case kind == reflect.Ptr:
|
||||||
return makePtrDecoder(typ, tags)
|
return makePtrDecoder(typ, tags)
|
||||||
case reflect.PtrTo(typ).Implements(decoderInterface):
|
case reflect.PointerTo(typ).Implements(decoderInterface):
|
||||||
return decodeDecoder, nil
|
return decodeDecoder, nil
|
||||||
case isUint(kind):
|
case isUint(kind):
|
||||||
return decodeUint, nil
|
return decodeUint, nil
|
||||||
|
|
@ -262,7 +262,7 @@ func decodeU256(s *Stream, val reflect.Value) error {
|
||||||
|
|
||||||
func makeListDecoder(typ reflect.Type, tag rlpstruct.Tags) (decoder, error) {
|
func makeListDecoder(typ reflect.Type, tag rlpstruct.Tags) (decoder, error) {
|
||||||
etype := typ.Elem()
|
etype := typ.Elem()
|
||||||
if etype.Kind() == reflect.Uint8 && !reflect.PtrTo(etype).Implements(decoderInterface) {
|
if etype.Kind() == reflect.Uint8 && !reflect.PointerTo(etype).Implements(decoderInterface) {
|
||||||
if typ.Kind() == reflect.Array {
|
if typ.Kind() == reflect.Array {
|
||||||
return decodeByteArray, nil
|
return decodeByteArray, nil
|
||||||
}
|
}
|
||||||
|
|
@ -474,7 +474,7 @@ func makeSimplePtrDecoder(etype reflect.Type, etypeinfo *typeinfo) decoder {
|
||||||
//
|
//
|
||||||
// This decoder is used for pointer-typed struct fields with struct tag "nil".
|
// This decoder is used for pointer-typed struct fields with struct tag "nil".
|
||||||
func makeNilPtrDecoder(etype reflect.Type, etypeinfo *typeinfo, ts rlpstruct.Tags) decoder {
|
func makeNilPtrDecoder(etype reflect.Type, etypeinfo *typeinfo, ts rlpstruct.Tags) decoder {
|
||||||
typ := reflect.PtrTo(etype)
|
typ := reflect.PointerTo(etype)
|
||||||
nilPtr := reflect.Zero(typ)
|
nilPtr := reflect.Zero(typ)
|
||||||
|
|
||||||
// Determine the value kind that results in nil pointer.
|
// Determine the value kind that results in nil pointer.
|
||||||
|
|
|
||||||
|
|
@ -141,17 +141,17 @@ func makeWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) {
|
||||||
switch {
|
switch {
|
||||||
case typ == rawValueType:
|
case typ == rawValueType:
|
||||||
return writeRawValue, nil
|
return writeRawValue, nil
|
||||||
case typ.AssignableTo(reflect.PtrTo(bigInt)):
|
case typ.AssignableTo(reflect.PointerTo(bigInt)):
|
||||||
return writeBigIntPtr, nil
|
return writeBigIntPtr, nil
|
||||||
case typ.AssignableTo(bigInt):
|
case typ.AssignableTo(bigInt):
|
||||||
return writeBigIntNoPtr, nil
|
return writeBigIntNoPtr, nil
|
||||||
case typ == reflect.PtrTo(u256Int):
|
case typ == reflect.PointerTo(u256Int):
|
||||||
return writeU256IntPtr, nil
|
return writeU256IntPtr, nil
|
||||||
case typ == u256Int:
|
case typ == u256Int:
|
||||||
return writeU256IntNoPtr, nil
|
return writeU256IntNoPtr, nil
|
||||||
case kind == reflect.Ptr:
|
case kind == reflect.Ptr:
|
||||||
return makePtrWriter(typ, ts)
|
return makePtrWriter(typ, ts)
|
||||||
case reflect.PtrTo(typ).Implements(encoderInterface):
|
case reflect.PointerTo(typ).Implements(encoderInterface):
|
||||||
return makeEncoderWriter(typ), nil
|
return makeEncoderWriter(typ), nil
|
||||||
case isUint(kind):
|
case isUint(kind):
|
||||||
return writeUint, nil
|
return writeUint, nil
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ func isSubscriptionType(t reflect.Type) bool {
|
||||||
return t == subscriptionType
|
return t == subscriptionType
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPubSub tests whether the given method has as as first argument a context.Context and
|
// isPubSub tests whether the given method's first argument is a context.Context and
|
||||||
// returns the pair (Subscription, error).
|
// returns the pair (Subscription, error).
|
||||||
func isPubSub(methodType reflect.Type) bool {
|
func isPubSub(methodType reflect.Type) bool {
|
||||||
// numIn(0) is the receiver type
|
// numIn(0) is the receiver type
|
||||||
|
|
|
||||||
|
|
@ -590,7 +590,10 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args apitypes.SendTxA
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Convert fields into a real transaction
|
// Convert fields into a real transaction
|
||||||
var unsignedTx = result.Transaction.ToTransaction()
|
unsignedTx, err := result.Transaction.ToTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Get the password for the transaction
|
// Get the password for the transaction
|
||||||
pw, err := api.lookupOrQueryPassword(acc.Address, "Account password",
|
pw, err := api.lookupOrQueryPassword(acc.Address, "Account password",
|
||||||
fmt.Sprintf("Please enter the password for account %s", acc.Address.String()))
|
fmt.Sprintf("Please enter the password for account %s", acc.Address.String()))
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@ package apitypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -34,6 +36,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Za-z](\w*)(\[\])?$`)
|
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Za-z](\w*)(\[\])?$`)
|
||||||
|
|
@ -92,12 +96,21 @@ type SendTxArgs struct {
|
||||||
// We accept "data" and "input" for backwards-compatibility reasons.
|
// We accept "data" and "input" for backwards-compatibility reasons.
|
||||||
// "input" is the newer name and should be preferred by clients.
|
// "input" is the newer name and should be preferred by clients.
|
||||||
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
|
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
|
||||||
Data *hexutil.Bytes `json:"data"`
|
Data *hexutil.Bytes `json:"data,omitempty"`
|
||||||
Input *hexutil.Bytes `json:"input,omitempty"`
|
Input *hexutil.Bytes `json:"input,omitempty"`
|
||||||
|
|
||||||
// For non-legacy transactions
|
// For non-legacy transactions
|
||||||
AccessList *types.AccessList `json:"accessList,omitempty"`
|
AccessList *types.AccessList `json:"accessList,omitempty"`
|
||||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||||
|
|
||||||
|
// For BlobTxType
|
||||||
|
BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
|
||||||
|
BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||||
|
|
||||||
|
// For BlobTxType transactions with blob sidecar
|
||||||
|
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
|
||||||
|
Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
|
||||||
|
Proofs []kzg4844.Proof `json:"proofs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (args SendTxArgs) String() string {
|
func (args SendTxArgs) String() string {
|
||||||
|
|
@ -108,24 +121,56 @@ func (args SendTxArgs) String() string {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data retrieves the transaction calldata. Input field is preferred.
|
||||||
|
func (args *SendTxArgs) data() []byte {
|
||||||
|
if args.Input != nil {
|
||||||
|
return *args.Input
|
||||||
|
}
|
||||||
|
if args.Data != nil {
|
||||||
|
return *args.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToTransaction converts the arguments to a transaction.
|
// ToTransaction converts the arguments to a transaction.
|
||||||
func (args *SendTxArgs) ToTransaction() *types.Transaction {
|
func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) {
|
||||||
// Add the To-field, if specified
|
// Add the To-field, if specified
|
||||||
var to *common.Address
|
var to *common.Address
|
||||||
if args.To != nil {
|
if args.To != nil {
|
||||||
dstAddr := args.To.Address()
|
dstAddr := args.To.Address()
|
||||||
to = &dstAddr
|
to = &dstAddr
|
||||||
}
|
}
|
||||||
|
if err := args.validateTxSidecar(); err != nil {
|
||||||
var input []byte
|
return nil, err
|
||||||
if args.Input != nil {
|
|
||||||
input = *args.Input
|
|
||||||
} else if args.Data != nil {
|
|
||||||
input = *args.Data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var data types.TxData
|
var data types.TxData
|
||||||
switch {
|
switch {
|
||||||
|
case args.BlobHashes != nil:
|
||||||
|
al := types.AccessList{}
|
||||||
|
if args.AccessList != nil {
|
||||||
|
al = *args.AccessList
|
||||||
|
}
|
||||||
|
data = &types.BlobTx{
|
||||||
|
To: *to,
|
||||||
|
ChainID: uint256.MustFromBig((*big.Int)(args.ChainID)),
|
||||||
|
Nonce: uint64(args.Nonce),
|
||||||
|
Gas: uint64(args.Gas),
|
||||||
|
GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)),
|
||||||
|
GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)),
|
||||||
|
Value: uint256.MustFromBig((*big.Int)(&args.Value)),
|
||||||
|
Data: args.data(),
|
||||||
|
AccessList: al,
|
||||||
|
BlobHashes: args.BlobHashes,
|
||||||
|
BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
|
||||||
|
}
|
||||||
|
if args.Blobs != nil {
|
||||||
|
data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{
|
||||||
|
Blobs: args.Blobs,
|
||||||
|
Commitments: args.Commitments,
|
||||||
|
Proofs: args.Proofs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case args.MaxFeePerGas != nil:
|
case args.MaxFeePerGas != nil:
|
||||||
al := types.AccessList{}
|
al := types.AccessList{}
|
||||||
if args.AccessList != nil {
|
if args.AccessList != nil {
|
||||||
|
|
@ -139,7 +184,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
|
||||||
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
|
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
|
||||||
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
|
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
|
||||||
Value: (*big.Int)(&args.Value),
|
Value: (*big.Int)(&args.Value),
|
||||||
Data: input,
|
Data: args.data(),
|
||||||
AccessList: al,
|
AccessList: al,
|
||||||
}
|
}
|
||||||
case args.AccessList != nil:
|
case args.AccessList != nil:
|
||||||
|
|
@ -150,7 +195,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
|
||||||
Gas: uint64(args.Gas),
|
Gas: uint64(args.Gas),
|
||||||
GasPrice: (*big.Int)(args.GasPrice),
|
GasPrice: (*big.Int)(args.GasPrice),
|
||||||
Value: (*big.Int)(&args.Value),
|
Value: (*big.Int)(&args.Value),
|
||||||
Data: input,
|
Data: args.data(),
|
||||||
AccessList: *args.AccessList,
|
AccessList: *args.AccessList,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -160,10 +205,81 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
|
||||||
Gas: uint64(args.Gas),
|
Gas: uint64(args.Gas),
|
||||||
GasPrice: (*big.Int)(args.GasPrice),
|
GasPrice: (*big.Int)(args.GasPrice),
|
||||||
Value: (*big.Int)(&args.Value),
|
Value: (*big.Int)(&args.Value),
|
||||||
Data: input,
|
Data: args.data(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.NewTx(data)
|
|
||||||
|
return types.NewTx(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateTxSidecar validates blob data, if present
|
||||||
|
func (args *SendTxArgs) validateTxSidecar() error {
|
||||||
|
// No blobs, we're done.
|
||||||
|
if args.Blobs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(args.Blobs)
|
||||||
|
// Assume user provides either only blobs (w/o hashes), or
|
||||||
|
// blobs together with commitments and proofs.
|
||||||
|
if args.Commitments == nil && args.Proofs != nil {
|
||||||
|
return errors.New(`blob proofs provided while commitments were not`)
|
||||||
|
} else if args.Commitments != nil && args.Proofs == nil {
|
||||||
|
return errors.New(`blob commitments provided while proofs were not`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// len(blobs) == len(commitments) == len(proofs) == len(hashes)
|
||||||
|
if args.Commitments != nil && len(args.Commitments) != n {
|
||||||
|
return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)
|
||||||
|
}
|
||||||
|
if args.Proofs != nil && len(args.Proofs) != n {
|
||||||
|
return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n)
|
||||||
|
}
|
||||||
|
if args.BlobHashes != nil && len(args.BlobHashes) != n {
|
||||||
|
return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Commitments == nil {
|
||||||
|
// Generate commitment and proof.
|
||||||
|
commitments := make([]kzg4844.Commitment, n)
|
||||||
|
proofs := make([]kzg4844.Proof, n)
|
||||||
|
for i, b := range args.Blobs {
|
||||||
|
c, err := kzg4844.BlobToCommitment(&b)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err)
|
||||||
|
}
|
||||||
|
commitments[i] = c
|
||||||
|
p, err := kzg4844.ComputeBlobProof(&b, c)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
|
||||||
|
}
|
||||||
|
proofs[i] = p
|
||||||
|
}
|
||||||
|
args.Commitments = commitments
|
||||||
|
args.Proofs = proofs
|
||||||
|
} else {
|
||||||
|
for i, b := range args.Blobs {
|
||||||
|
if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil {
|
||||||
|
return fmt.Errorf("failed to verify blob proof: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashes := make([]common.Hash, n)
|
||||||
|
hasher := sha256.New()
|
||||||
|
for i, c := range args.Commitments {
|
||||||
|
hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c)
|
||||||
|
}
|
||||||
|
if args.BlobHashes != nil {
|
||||||
|
for i, h := range hashes {
|
||||||
|
if h != args.BlobHashes[i] {
|
||||||
|
return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.BlobHashes = hashes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SigFormat struct {
|
type SigFormat struct {
|
||||||
|
|
@ -271,16 +387,8 @@ func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage
|
||||||
// Dependencies returns an array of custom types ordered by their hierarchical reference tree
|
// Dependencies returns an array of custom types ordered by their hierarchical reference tree
|
||||||
func (typedData *TypedData) Dependencies(primaryType string, found []string) []string {
|
func (typedData *TypedData) Dependencies(primaryType string, found []string) []string {
|
||||||
primaryType = strings.TrimSuffix(primaryType, "[]")
|
primaryType = strings.TrimSuffix(primaryType, "[]")
|
||||||
includes := func(arr []string, str string) bool {
|
|
||||||
for _, obj := range arr {
|
|
||||||
if obj == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if includes(found, primaryType) {
|
if slices.Contains(found, primaryType) {
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
if typedData.Types[primaryType] == nil {
|
if typedData.Types[primaryType] == nil {
|
||||||
|
|
@ -289,7 +397,7 @@ func (typedData *TypedData) Dependencies(primaryType string, found []string) []s
|
||||||
found = append(found, primaryType)
|
found = append(found, primaryType)
|
||||||
for _, field := range typedData.Types[primaryType] {
|
for _, field := range typedData.Types[primaryType] {
|
||||||
for _, dep := range typedData.Dependencies(field.Type, found) {
|
for _, dep := range typedData.Dependencies(field.Type, found) {
|
||||||
if !includes(found, dep) {
|
if !slices.Contains(found, dep) {
|
||||||
found = append(found, dep)
|
found = append(found, dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,16 @@
|
||||||
|
|
||||||
package apitypes
|
package apitypes
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
func TestIsPrimitive(t *testing.T) {
|
func TestIsPrimitive(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
@ -39,3 +48,96 @@ func TestIsPrimitive(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTxArgs(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
data []byte
|
||||||
|
want common.Hash
|
||||||
|
wantType uint8
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte(`{"from":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","accessList":[],"blobVersionedHashes":["0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014"],"chainId":"0x7","gas":"0x124f8","gasPrice":"0x693d4ca8","input":"0x","maxFeePerBlobGas":"0x3b9aca00","maxFeePerGas":"0x6fc23ac00","maxPriorityFeePerGas":"0x3b9aca00","nonce":"0x0","r":"0x2a922afc784d07e98012da29f2f37cae1f73eda78aa8805d3df6ee5dbb41ec1","s":"0x4f1f75ae6bcdf4970b4f305da1a15d8c5ddb21f555444beab77c9af2baab14","to":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","type":"0x1","v":"0x0","value":"0x0","yParity":"0x0"}`),
|
||||||
|
want: common.HexToHash("0x7d53234acc11ac5b5948632c901a944694e228795782f511887d36fd76ff15c4"),
|
||||||
|
wantType: types.BlobTxType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// on input, we don't read the type, but infer the type from the arguments present
|
||||||
|
data: []byte(`{"from":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","accessList":[],"chainId":"0x7","gas":"0x124f8","gasPrice":"0x693d4ca8","input":"0x","maxFeePerBlobGas":"0x3b9aca00","maxFeePerGas":"0x6fc23ac00","maxPriorityFeePerGas":"0x3b9aca00","nonce":"0x0","r":"0x2a922afc784d07e98012da29f2f37cae1f73eda78aa8805d3df6ee5dbb41ec1","s":"0x4f1f75ae6bcdf4970b4f305da1a15d8c5ddb21f555444beab77c9af2baab14","to":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","type":"0x12","v":"0x0","value":"0x0","yParity":"0x0"}`),
|
||||||
|
want: common.HexToHash("0x7919e2b0b9b543cb87a137b6ff66491ec7ae937cb88d3c29db4d9b28073dce53"),
|
||||||
|
wantType: types.DynamicFeeTxType,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var txArgs SendTxArgs
|
||||||
|
if err := json.Unmarshal(tc.data, &txArgs); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx, err := txArgs.ToTransaction()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if have := tx.Type(); have != tc.wantType {
|
||||||
|
t.Errorf("test %d, have type %d, want type %d", i, have, tc.wantType)
|
||||||
|
}
|
||||||
|
if have := tx.Hash(); have != tc.want {
|
||||||
|
t.Errorf("test %d: have %v, want %v", i, have, tc.want)
|
||||||
|
}
|
||||||
|
d2, err := json.Marshal(txArgs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var txArgs2 SendTxArgs
|
||||||
|
if err := json.Unmarshal(d2, &txArgs2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tx1, _ := txArgs.ToTransaction()
|
||||||
|
tx2, _ := txArgs2.ToTransaction()
|
||||||
|
if have, want := tx1.Hash(), tx2.Hash(); have != want {
|
||||||
|
t.Errorf("test %d: have %v, want %v", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
End to end testing:
|
||||||
|
|
||||||
|
$ go run ./cmd/clef --advanced --suppress-bootwarn
|
||||||
|
|
||||||
|
$ go run ./cmd/geth --nodiscover --maxpeers 0 --signer /home/user/.clef/clef.ipc console
|
||||||
|
|
||||||
|
> tx={"from":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","to":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","gas":"0x124f8","maxFeePerGas":"0x6fc23ac00","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","nonce":"0x0","input":"0x","accessList":[],"maxFeePerBlobGas":"0x3b9aca00","blobVersionedHashes":["0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014"]}
|
||||||
|
> eth.signTransaction(tx)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlobTxs(t *testing.T) {
|
||||||
|
blob := kzg4844.Blob{0x1}
|
||||||
|
commitment, err := kzg4844.BlobToCommitment(&blob)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
proof, err := kzg4844.ComputeBlobProof(&blob, commitment)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := kzg4844.CalcBlobHashV1(sha256.New(), &commitment)
|
||||||
|
b := &types.BlobTx{
|
||||||
|
ChainID: uint256.NewInt(6),
|
||||||
|
Nonce: 8,
|
||||||
|
GasTipCap: uint256.NewInt(500),
|
||||||
|
GasFeeCap: uint256.NewInt(600),
|
||||||
|
Gas: 21000,
|
||||||
|
BlobFeeCap: uint256.NewInt(700),
|
||||||
|
BlobHashes: []common.Hash{hash},
|
||||||
|
Value: uint256.NewInt(100),
|
||||||
|
Sidecar: &types.BlobTxSidecar{
|
||||||
|
Blobs: []kzg4844.Blob{blob},
|
||||||
|
Commitments: []kzg4844.Commitment{commitment},
|
||||||
|
Proofs: []kzg4844.Proof{proof},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tx := types.NewTx(b)
|
||||||
|
data, err := json.Marshal(tx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("tx %v", string(data))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
fmt.Printf("chainid: %v\n", chainId)
|
fmt.Printf("chainid: %v\n", chainId)
|
||||||
}
|
}
|
||||||
if list := request.Transaction.AccessList; list != nil {
|
if list := request.Transaction.AccessList; list != nil {
|
||||||
fmt.Printf("Accesslist\n")
|
fmt.Printf("Accesslist:\n")
|
||||||
for i, el := range *list {
|
for i, el := range *list {
|
||||||
fmt.Printf(" %d. %v\n", i, el.Address)
|
fmt.Printf(" %d. %v\n", i, el.Address)
|
||||||
for j, slot := range el.StorageKeys {
|
for j, slot := range el.StorageKeys {
|
||||||
|
|
@ -136,6 +136,12 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(request.Transaction.BlobHashes) > 0 {
|
||||||
|
fmt.Printf("Blob hashes:\n")
|
||||||
|
for _, bh := range request.Transaction.BlobHashes {
|
||||||
|
fmt.Printf(" %v\n", bh)
|
||||||
|
}
|
||||||
|
}
|
||||||
if request.Transaction.Data != nil {
|
if request.Transaction.Data != nil {
|
||||||
d := *request.Transaction.Data
|
d := *request.Transaction.Data
|
||||||
if len(d) > 0 {
|
if len(d) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -385,7 +385,7 @@ func TestJsonFiles(t *testing.T) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail")
|
expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail")
|
||||||
data, err := os.ReadFile(path.Join("testdata", fInfo.Name()))
|
data, err := os.ReadFile(filepath.Join("testdata", fInfo.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
|
t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -411,14 +411,14 @@ func TestJsonFiles(t *testing.T) {
|
||||||
// crashes or hangs.
|
// crashes or hangs.
|
||||||
func TestFuzzerFiles(t *testing.T) {
|
func TestFuzzerFiles(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
corpusdir := path.Join("testdata", "fuzzing")
|
corpusdir := filepath.Join("testdata", "fuzzing")
|
||||||
testfiles, err := os.ReadDir(corpusdir)
|
testfiles, err := os.ReadDir(corpusdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed reading files: %v", err)
|
t.Fatalf("failed reading files: %v", err)
|
||||||
}
|
}
|
||||||
verbose := false
|
verbose := false
|
||||||
for i, fInfo := range testfiles {
|
for i, fInfo := range testfiles {
|
||||||
data, err := os.ReadFile(path.Join(corpusdir, fInfo.Name()))
|
data, err := os.ReadFile(filepath.Join(corpusdir, fInfo.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
|
t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||||
|
|
@ -36,6 +35,11 @@ func (db *Database) ValidateTransaction(selector *string, tx *apitypes.SendTxArg
|
||||||
if tx.Data != nil && tx.Input != nil && !bytes.Equal(*tx.Data, *tx.Input) {
|
if tx.Data != nil && tx.Input != nil && !bytes.Equal(*tx.Data, *tx.Input) {
|
||||||
return nil, errors.New(`ambiguous request: both "data" and "input" are set and are not identical`)
|
return nil, errors.New(`ambiguous request: both "data" and "input" are set and are not identical`)
|
||||||
}
|
}
|
||||||
|
// ToTransaction validates, among other things, that blob hashes match with blobs, and also
|
||||||
|
// populates the hashes if they were previously unset.
|
||||||
|
if _, err := tx.ToTransaction(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Place data on 'data', and nil 'input'
|
// Place data on 'data', and nil 'input'
|
||||||
var data []byte
|
var data []byte
|
||||||
if tx.Input != nil {
|
if tx.Input != nil {
|
||||||
|
|
@ -52,7 +56,7 @@ func (db *Database) ValidateTransaction(selector *string, tx *apitypes.SendTxArg
|
||||||
// e.g. https://github.com/ethereum/go-ethereum/issues/16106.
|
// e.g. https://github.com/ethereum/go-ethereum/issues/16106.
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
// Prevent sending ether into black hole (show stopper)
|
// Prevent sending ether into black hole (show stopper)
|
||||||
if tx.Value.ToInt().Cmp(big.NewInt(0)) > 0 {
|
if tx.Value.ToInt().Sign() > 0 {
|
||||||
return nil, errors.New("transaction will create a contract with value but empty code")
|
return nil, errors.New("transaction will create a contract with value but empty code")
|
||||||
}
|
}
|
||||||
// No value submitted at least, critically Warn, but don't blow up
|
// No value submitted at least, critically Warn, but don't blow up
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,14 @@ func TestExecutionSpecBlocktests(t *testing.T) {
|
||||||
}
|
}
|
||||||
bt := new(testMatcher)
|
bt := new(testMatcher)
|
||||||
|
|
||||||
|
// These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we
|
||||||
|
// no longer delete "leftover storage" when deploying a contract.
|
||||||
|
bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/self_destructing_initcode_create_tx.json`)
|
||||||
|
bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/self_destructing_initcode.json`)
|
||||||
|
bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/recreate_self_destructed_contract_different_txs.json`)
|
||||||
|
bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/delegatecall_from_new_contract_to_pre_existing_contract.json`)
|
||||||
|
bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/create_selfdestruct_same_tx.json`)
|
||||||
|
|
||||||
bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) {
|
bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) {
|
||||||
execBlockTest(t, bt, test)
|
execBlockTest(t, bt, test)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
blsG1Add = byte(10)
|
blsG1Add = byte(11)
|
||||||
blsG1Mul = byte(11)
|
blsG1Mul = byte(12)
|
||||||
blsG1MultiExp = byte(12)
|
blsG1MultiExp = byte(13)
|
||||||
blsG2Add = byte(13)
|
blsG2Add = byte(14)
|
||||||
blsG2Mul = byte(14)
|
blsG2Mul = byte(15)
|
||||||
blsG2MultiExp = byte(15)
|
blsG2MultiExp = byte(16)
|
||||||
blsPairing = byte(16)
|
blsPairing = byte(17)
|
||||||
blsMapG1 = byte(17)
|
blsMapG1 = byte(18)
|
||||||
blsMapG2 = byte(18)
|
blsMapG2 = byte(19)
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkInput(id byte, inputLen int) bool {
|
func checkInput(id byte, inputLen int) bool {
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,22 @@ func initMatcher(st *testMatcher) {
|
||||||
// Uses 1GB RAM per tested fork
|
// Uses 1GB RAM per tested fork
|
||||||
st.skipLoad(`^stStaticCall/static_Call1MB`)
|
st.skipLoad(`^stStaticCall/static_Call1MB`)
|
||||||
|
|
||||||
|
// These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we
|
||||||
|
// no longer delete "leftover storage" when deploying a contract.
|
||||||
|
st.skipLoad(`^stSStoreTest/InitCollision\.json`)
|
||||||
|
st.skipLoad(`^stRevertTest/RevertInCreateInInit\.json`)
|
||||||
|
st.skipLoad(`^stExtCodeHash/dynamicAccountOverwriteEmpty\.json`)
|
||||||
|
st.skipLoad(`^stCreate2/create2collisionStorage\.json`)
|
||||||
|
st.skipLoad(`^stCreate2/RevertInCreateInInitCreate2\.json`)
|
||||||
|
|
||||||
// Broken tests:
|
// Broken tests:
|
||||||
// EOF is not part of cancun
|
// EOF is not part of cancun
|
||||||
st.skipLoad(`^stEOF/`)
|
st.skipLoad(`^stEOF/`)
|
||||||
|
|
||||||
|
// The tests under Pyspecs are the ones that are published as execution-spec tests.
|
||||||
|
// We run these tests separately, no need to _also_ run them as part of the
|
||||||
|
// reference tests.
|
||||||
|
st.skipLoad(`^Pyspecs/`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestState(t *testing.T) {
|
func TestState(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -207,8 +207,7 @@ func CodeSizeKey(address []byte) []byte {
|
||||||
func codeChunkIndex(chunk *uint256.Int) (*uint256.Int, byte) {
|
func codeChunkIndex(chunk *uint256.Int) (*uint256.Int, byte) {
|
||||||
var (
|
var (
|
||||||
chunkOffset = new(uint256.Int).Add(codeOffset, chunk)
|
chunkOffset = new(uint256.Int).Add(codeOffset, chunk)
|
||||||
treeIndex = new(uint256.Int).Div(chunkOffset, verkleNodeWidth)
|
treeIndex, subIndexMod = new(uint256.Int).DivMod(chunkOffset, verkleNodeWidth, new(uint256.Int))
|
||||||
subIndexMod = new(uint256.Int).Mod(chunkOffset, verkleNodeWidth)
|
|
||||||
)
|
)
|
||||||
var subIndex byte
|
var subIndex byte
|
||||||
if len(subIndexMod) != 0 {
|
if len(subIndexMod) != 0 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue